changeset 4326:07862b0663fd

merge moin/1.8 repo
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Fri, 22 Aug 2008 22:58:18 +0200
parents b6b6979b4813 (current diff) 24ef81d2b5f0 (diff)
children 62177a952833
files .hgtags MoinMoin/PageEditor.py MoinMoin/PageGraphicalEditor.py MoinMoin/_tests/ldap_testbase.py MoinMoin/_tests/test_sourcecode.py MoinMoin/action/AttachFile.py MoinMoin/action/Despam.py MoinMoin/action/fckdialog.py MoinMoin/action/info.py MoinMoin/conftest.py MoinMoin/converter/_tests/test_text_html_text_moin_wiki.py MoinMoin/converter/text_html_text_moin_wiki.py MoinMoin/macro/_tests/test_Action.py MoinMoin/macro/_tests/test_EmbedObject.py MoinMoin/macro/_tests/test_StatsChart.py MoinMoin/stats/hitcounts.py MoinMoin/stats/useragents.py MoinMoin/widget/browser.py wiki/htdocs/applets/FCKeditor/CVS/Entries wiki/htdocs/applets/FCKeditor/CVS/Repository wiki/htdocs/applets/FCKeditor/CVS/Root wiki/htdocs/applets/FCKeditor/README wiki/htdocs/applets/FCKeditor/_documentation.html wiki/htdocs/applets/FCKeditor/_samples/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/CVS/Entries.Log wiki/htdocs/applets/FCKeditor/_samples/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/_plugins/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/_plugins/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/_plugins/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/fckplugin.js wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/find.gif wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/find.html wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/lang/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/lang/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/lang/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/lang/en.js wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/lang/fr.js wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/lang/it.js wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/replace.gif wiki/htdocs/applets/FCKeditor/_samples/_plugins/findreplace/replace.html wiki/htdocs/applets/FCKeditor/_samples/_plugins/samples/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/_plugins/samples/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/_plugins/samples/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/_plugins/samples/fckplugin.js wiki/htdocs/applets/FCKeditor/_samples/afp/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/afp/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/afp/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/afp/fck.afpa wiki/htdocs/applets/FCKeditor/_samples/afp/fck.afpa.code wiki/htdocs/applets/FCKeditor/_samples/afp/sample01.afp wiki/htdocs/applets/FCKeditor/_samples/afp/sampleposteddata.afp wiki/htdocs/applets/FCKeditor/_samples/asp/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/asp/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/asp/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/asp/sample01.asp wiki/htdocs/applets/FCKeditor/_samples/asp/sample02.asp wiki/htdocs/applets/FCKeditor/_samples/asp/sample03.asp wiki/htdocs/applets/FCKeditor/_samples/asp/sample04.asp wiki/htdocs/applets/FCKeditor/_samples/asp/sampleposteddata.asp wiki/htdocs/applets/FCKeditor/_samples/cfm/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/cfm/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/cfm/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/cfm/sample01.cfm wiki/htdocs/applets/FCKeditor/_samples/cfm/sample02_mx.cfm wiki/htdocs/applets/FCKeditor/_samples/default.html wiki/htdocs/applets/FCKeditor/_samples/html/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/html/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/html/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/html/sample01.html wiki/htdocs/applets/FCKeditor/_samples/html/sample02.html wiki/htdocs/applets/FCKeditor/_samples/html/sample03.html wiki/htdocs/applets/FCKeditor/_samples/html/sample04.html wiki/htdocs/applets/FCKeditor/_samples/html/sample05.html wiki/htdocs/applets/FCKeditor/_samples/html/sample06.config.js wiki/htdocs/applets/FCKeditor/_samples/html/sample06.html wiki/htdocs/applets/FCKeditor/_samples/html/sample07.html wiki/htdocs/applets/FCKeditor/_samples/html/sample08.html wiki/htdocs/applets/FCKeditor/_samples/html/sample09.html wiki/htdocs/applets/FCKeditor/_samples/html/sample10.html wiki/htdocs/applets/FCKeditor/_samples/html/sampleposteddata.asp wiki/htdocs/applets/FCKeditor/_samples/html/sampleposteddata.html wiki/htdocs/applets/FCKeditor/_samples/lasso/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/lasso/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/lasso/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/lasso/sample01.lasso wiki/htdocs/applets/FCKeditor/_samples/lasso/sample02.lasso wiki/htdocs/applets/FCKeditor/_samples/lasso/sample03.lasso wiki/htdocs/applets/FCKeditor/_samples/lasso/sample04.lasso wiki/htdocs/applets/FCKeditor/_samples/lasso/sampleposteddata.lasso wiki/htdocs/applets/FCKeditor/_samples/perl/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/perl/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/perl/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/perl/sample01.cgi wiki/htdocs/applets/FCKeditor/_samples/perl/sample02.cgi wiki/htdocs/applets/FCKeditor/_samples/perl/sample03.cgi wiki/htdocs/applets/FCKeditor/_samples/perl/sample04.cgi wiki/htdocs/applets/FCKeditor/_samples/perl/sampleposteddata.cgi wiki/htdocs/applets/FCKeditor/_samples/php/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/php/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/php/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/php/sample01.php wiki/htdocs/applets/FCKeditor/_samples/php/sample02.php wiki/htdocs/applets/FCKeditor/_samples/php/sample03.php wiki/htdocs/applets/FCKeditor/_samples/php/sample04.php wiki/htdocs/applets/FCKeditor/_samples/php/sampleposteddata.php wiki/htdocs/applets/FCKeditor/_samples/py/CVS/Entries wiki/htdocs/applets/FCKeditor/_samples/py/CVS/Repository wiki/htdocs/applets/FCKeditor/_samples/py/CVS/Root wiki/htdocs/applets/FCKeditor/_samples/py/sample01.py wiki/htdocs/applets/FCKeditor/_samples/py/sampleposteddata.py wiki/htdocs/applets/FCKeditor/_samples/sample.css wiki/htdocs/applets/FCKeditor/_samples/sampleslist.html wiki/htdocs/applets/FCKeditor/_testcases/001.html wiki/htdocs/applets/FCKeditor/_testcases/002.html wiki/htdocs/applets/FCKeditor/_testcases/003.html wiki/htdocs/applets/FCKeditor/_testcases/004.html wiki/htdocs/applets/FCKeditor/_testcases/005.html wiki/htdocs/applets/FCKeditor/_testcases/006.html wiki/htdocs/applets/FCKeditor/_testcases/007.html wiki/htdocs/applets/FCKeditor/_testcases/008.html wiki/htdocs/applets/FCKeditor/_testcases/009.html wiki/htdocs/applets/FCKeditor/_testcases/010.html wiki/htdocs/applets/FCKeditor/_testcases/CVS/Entries wiki/htdocs/applets/FCKeditor/_testcases/CVS/Repository wiki/htdocs/applets/FCKeditor/_testcases/CVS/Root wiki/htdocs/applets/FCKeditor/_testcases/sampleposteddata.asp wiki/htdocs/applets/FCKeditor/_testcases/testcases.css wiki/htdocs/applets/FCKeditor/_whatsnew.html wiki/htdocs/applets/FCKeditor/editor/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/CVS/Root wiki/htdocs/applets/FCKeditor/editor/_source/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/_source/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/_source/CVS/Root wiki/htdocs/applets/FCKeditor/editor/_source/classes/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/_source/classes/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/_source/classes/CVS/Root wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckcontextmenugroup.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckcontextmenuitem.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckcontextmenuseparator.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckevents.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckpanel_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckpanel_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckplugin.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckspecialcombo.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckstyledef.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckstyledef_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckstyledef_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckstylesloader.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fcktoolbar.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fcktoolbarbreak_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fcktoolbarbreak_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fcktoolbarbutton.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fcktoolbarfontformatcombo.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fcktoolbarfontscombo.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fcktoolbarfontsizecombo.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fcktoolbarpanelbutton.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fcktoolbarspecialcombo.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fcktoolbarstylecombo.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckxml_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/classes/fckxml_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/CVS/Root wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/fck_othercommands.js wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/fcknamedcommand.js wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/fckpasteplaintextcommand.js wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/fckpastewordcommand.js wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/fckspellcheckcommand_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/fckspellcheckcommand_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/fckstylecommand.js wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/fcktablecommand.js wiki/htdocs/applets/FCKeditor/editor/_source/commandclasses/fcktextcolorcommand.js wiki/htdocs/applets/FCKeditor/editor/_source/globals/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/_source/globals/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/_source/globals/CVS/Root wiki/htdocs/applets/FCKeditor/editor/_source/globals/fck_constants.js wiki/htdocs/applets/FCKeditor/editor/_source/globals/fckeditorapi.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/_source/internals/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/_source/internals/CVS/Root wiki/htdocs/applets/FCKeditor/editor/_source/internals/fck.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fck_1.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fck_1_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fck_1_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fck_2.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fck_2_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fck_2_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fck_last.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fck_onload.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckbrowserinfo.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckcodeformatter.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckcommands.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckconfig.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckcontextmenu.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckcoreextensions.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckdebug.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckdialog.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckdialog_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckdialog_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fcklanguagemanager.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fcknamespace.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckplugins.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckregexlib.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckscriptloader.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckselection.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckselection_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckselection_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fcktablehandler.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fcktablehandler_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fcktablehandler_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fcktoolbaritems.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fcktoolbarset.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fcktools.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fcktools_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fcktools_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckundo_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckundo_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckurlparams.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckxhtml.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckxhtml_gecko.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckxhtml_ie.js wiki/htdocs/applets/FCKeditor/editor/_source/internals/fckxhtmlentities.js wiki/htdocs/applets/FCKeditor/editor/css/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/css/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/css/CVS/Root wiki/htdocs/applets/FCKeditor/editor/css/behaviors/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/css/behaviors/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/css/behaviors/CVS/Root wiki/htdocs/applets/FCKeditor/editor/css/behaviors/disablehandles.htc wiki/htdocs/applets/FCKeditor/editor/css/behaviors/hiddenfield.gif wiki/htdocs/applets/FCKeditor/editor/css/behaviors/hiddenfield.htc wiki/htdocs/applets/FCKeditor/editor/css/behaviors/showtableborders.htc wiki/htdocs/applets/FCKeditor/editor/css/fck_editorarea.css wiki/htdocs/applets/FCKeditor/editor/css/fck_internal.css wiki/htdocs/applets/FCKeditor/editor/css/fck_showtableborders_gecko.css wiki/htdocs/applets/FCKeditor/editor/css/images/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/css/images/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/css/images/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/common/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/common/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/common/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/common/fck_dialog_common.css wiki/htdocs/applets/FCKeditor/editor/dialog/common/fck_dialog_common.js wiki/htdocs/applets/FCKeditor/editor/dialog/common/fcknumericfield.htc wiki/htdocs/applets/FCKeditor/editor/dialog/common/images/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/common/images/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/common/images/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/common/moz-bindings.xml wiki/htdocs/applets/FCKeditor/editor/dialog/fck_about.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_about/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_about/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_about/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_about/lgpl.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_anchor.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_button.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_checkbox.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_colorselector.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_docprops.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_docprops/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_docprops/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_docprops/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_docprops/fck_document_preview.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_find.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_flash.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_flash/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_flash/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_flash/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_flash/fck_flash.js wiki/htdocs/applets/FCKeditor/editor/dialog/fck_flash/fck_flash_preview.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_form.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_hiddenfield.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_image.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_image/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_image/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_image/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_image/fck_image.js wiki/htdocs/applets/FCKeditor/editor/dialog/fck_image/fck_image_preview.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_link.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_link/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_link/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_link/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_link/fck_link.js wiki/htdocs/applets/FCKeditor/editor/dialog/fck_listprop.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_paste.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_radiobutton.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_replace.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_select.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_select/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_select/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_select/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_select/fck_select.js wiki/htdocs/applets/FCKeditor/editor/dialog/fck_smiley.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_source.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_specialchar.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/controlWindow.js wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/controls.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellchecker.cfm wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellchecker.php wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellchecker.pl wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/spellChecker.js wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/spellchecker.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/spellerStyle.css wiki/htdocs/applets/FCKeditor/editor/dialog/fck_spellerpages/spellerpages/wordWindow.js wiki/htdocs/applets/FCKeditor/editor/dialog/fck_table.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_tablecell.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_template.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_template/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_template/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_template/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_template/images/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_template/images/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_template/images/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_textarea.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_textfield.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_universalkey.html wiki/htdocs/applets/FCKeditor/editor/dialog/fck_universalkey/00.gif wiki/htdocs/applets/FCKeditor/editor/dialog/fck_universalkey/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/dialog/fck_universalkey/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/dialog/fck_universalkey/CVS/Root wiki/htdocs/applets/FCKeditor/editor/dialog/fck_universalkey/data.js wiki/htdocs/applets/FCKeditor/editor/dialog/fck_universalkey/diacritic.js wiki/htdocs/applets/FCKeditor/editor/dialog/fck_universalkey/dialogue.js wiki/htdocs/applets/FCKeditor/editor/dialog/fck_universalkey/fck_universalkey.css wiki/htdocs/applets/FCKeditor/editor/dialog/fck_universalkey/keyboard_layout.gif wiki/htdocs/applets/FCKeditor/editor/dialog/fck_universalkey/multihexa.js wiki/htdocs/applets/FCKeditor/editor/fckblank.html wiki/htdocs/applets/FCKeditor/editor/fckdebug.html wiki/htdocs/applets/FCKeditor/editor/fckdialog.html wiki/htdocs/applets/FCKeditor/editor/fckdocument.html wiki/htdocs/applets/FCKeditor/editor/fckeditor.html wiki/htdocs/applets/FCKeditor/editor/fckeditor.original.html wiki/htdocs/applets/FCKeditor/editor/filemanager/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/browser.css wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/browser.html wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/CVS/Entries.Log wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/asp/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/asp/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/asp/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/asp/basexml.asp wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/asp/class_upload.asp wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/asp/commands.asp wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/asp/config.asp wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/asp/connector.asp wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/asp/io.asp wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/asp/util.asp wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/aspx/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/aspx/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/aspx/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/aspx/connector.aspx wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/cfm/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/cfm/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/cfm/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/cfm/config.cfm wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/cfm/connector.cfm wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/lasso/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/lasso/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/lasso/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/lasso/config.lasso wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/lasso/connector.lasso wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/perl/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/perl/CVS/Entries.Log wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/perl/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/perl/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/perl/basexml.pl wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/perl/commands.pl wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/perl/connector.cgi wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/perl/io.pl wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/perl/upload_fck.pl wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/perl/util.pl wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/php/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/php/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/php/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/php/basexml.php wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/php/commands.php wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/php/config.php wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/php/connector.php wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/php/io.php wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/php/util.php wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/py/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/py/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/py/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/py/connector.py wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/connectors/test.html wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/frmactualfolder.html wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/frmcreatefolder.html wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/frmfolders.html wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/frmresourceslist.html wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/frmresourcetype.html wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/frmupload.html wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/images/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/images/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/images/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/images/icons/32/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/images/icons/32/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/images/icons/32/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/images/icons/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/images/icons/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/images/icons/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/js/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/js/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/js/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/js/common.js wiki/htdocs/applets/FCKeditor/editor/filemanager/browser/default/js/fckxml.js wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/CVS/Entries.Log wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/asp/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/asp/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/asp/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/asp/class_upload.asp wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/asp/config.asp wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/asp/io.asp wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/asp/upload.asp wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/aspx/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/aspx/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/aspx/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/aspx/upload.aspx wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/cfm/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/cfm/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/cfm/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/cfm/config.cfm wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/cfm/upload.cfm wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/lasso/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/lasso/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/lasso/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/lasso/config.lasso wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/lasso/upload.lasso wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/php/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/php/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/php/CVS/Root wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/php/config.php wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/php/upload.php wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/php/util.php wiki/htdocs/applets/FCKeditor/editor/filemanager/upload/test.html wiki/htdocs/applets/FCKeditor/editor/images/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/images/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/images/CVS/Root wiki/htdocs/applets/FCKeditor/editor/images/smiley/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/images/smiley/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/images/smiley/CVS/Root wiki/htdocs/applets/FCKeditor/editor/images/smiley/msn/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/images/smiley/msn/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/images/smiley/msn/CVS/Root wiki/htdocs/applets/FCKeditor/editor/js/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/js/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/js/CVS/Root wiki/htdocs/applets/FCKeditor/editor/js/fck_startup.js wiki/htdocs/applets/FCKeditor/editor/js/fckeditorcode_gecko_1.js wiki/htdocs/applets/FCKeditor/editor/js/fckeditorcode_gecko_2.js wiki/htdocs/applets/FCKeditor/editor/js/fckeditorcode_ie_1.js wiki/htdocs/applets/FCKeditor/editor/js/fckeditorcode_ie_2.js wiki/htdocs/applets/FCKeditor/editor/lang/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/lang/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/lang/CVS/Root wiki/htdocs/applets/FCKeditor/editor/lang/_getfontformat.html wiki/htdocs/applets/FCKeditor/editor/lang/_translationstatus.txt wiki/htdocs/applets/FCKeditor/editor/lang/ar.js wiki/htdocs/applets/FCKeditor/editor/lang/bg.js wiki/htdocs/applets/FCKeditor/editor/lang/bs.js wiki/htdocs/applets/FCKeditor/editor/lang/ca.js wiki/htdocs/applets/FCKeditor/editor/lang/cs.js wiki/htdocs/applets/FCKeditor/editor/lang/da.js wiki/htdocs/applets/FCKeditor/editor/lang/de.js wiki/htdocs/applets/FCKeditor/editor/lang/el.js wiki/htdocs/applets/FCKeditor/editor/lang/en-au.js wiki/htdocs/applets/FCKeditor/editor/lang/en-uk.js wiki/htdocs/applets/FCKeditor/editor/lang/en.js wiki/htdocs/applets/FCKeditor/editor/lang/eo.js wiki/htdocs/applets/FCKeditor/editor/lang/es.js wiki/htdocs/applets/FCKeditor/editor/lang/et.js wiki/htdocs/applets/FCKeditor/editor/lang/eu.js wiki/htdocs/applets/FCKeditor/editor/lang/fa.js wiki/htdocs/applets/FCKeditor/editor/lang/fcklanguagemanager.js wiki/htdocs/applets/FCKeditor/editor/lang/fi.js wiki/htdocs/applets/FCKeditor/editor/lang/fo.js wiki/htdocs/applets/FCKeditor/editor/lang/fr.js wiki/htdocs/applets/FCKeditor/editor/lang/gl.js wiki/htdocs/applets/FCKeditor/editor/lang/he.js wiki/htdocs/applets/FCKeditor/editor/lang/hi.js wiki/htdocs/applets/FCKeditor/editor/lang/hr.js wiki/htdocs/applets/FCKeditor/editor/lang/hu.js wiki/htdocs/applets/FCKeditor/editor/lang/it.js wiki/htdocs/applets/FCKeditor/editor/lang/ja.js wiki/htdocs/applets/FCKeditor/editor/lang/ko.js wiki/htdocs/applets/FCKeditor/editor/lang/lt.js wiki/htdocs/applets/FCKeditor/editor/lang/lv.js wiki/htdocs/applets/FCKeditor/editor/lang/mn.js wiki/htdocs/applets/FCKeditor/editor/lang/ms.js wiki/htdocs/applets/FCKeditor/editor/lang/nl.js wiki/htdocs/applets/FCKeditor/editor/lang/no.js wiki/htdocs/applets/FCKeditor/editor/lang/pl.js wiki/htdocs/applets/FCKeditor/editor/lang/pt-br.js wiki/htdocs/applets/FCKeditor/editor/lang/pt.js wiki/htdocs/applets/FCKeditor/editor/lang/ro.js wiki/htdocs/applets/FCKeditor/editor/lang/ru.js wiki/htdocs/applets/FCKeditor/editor/lang/sk.js wiki/htdocs/applets/FCKeditor/editor/lang/sl.js wiki/htdocs/applets/FCKeditor/editor/lang/sr-latn.js wiki/htdocs/applets/FCKeditor/editor/lang/sr.js wiki/htdocs/applets/FCKeditor/editor/lang/sv.js wiki/htdocs/applets/FCKeditor/editor/lang/th.js wiki/htdocs/applets/FCKeditor/editor/lang/tr.js wiki/htdocs/applets/FCKeditor/editor/lang/uk.js wiki/htdocs/applets/FCKeditor/editor/lang/vi.js wiki/htdocs/applets/FCKeditor/editor/lang/zh-cn.js wiki/htdocs/applets/FCKeditor/editor/lang/zh.js wiki/htdocs/applets/FCKeditor/editor/plugins/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/plugins/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/plugins/CVS/Root wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/CVS/Root wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/fck_placeholder.html wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/fckplugin.js wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/lang/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/lang/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/lang/CVS/Root wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/lang/de.js wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/lang/en.js wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/lang/fr.js wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/lang/it.js wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/lang/pl.js wiki/htdocs/applets/FCKeditor/editor/plugins/placeholder/placeholder.gif wiki/htdocs/applets/FCKeditor/editor/plugins/simplecommands/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/plugins/simplecommands/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/plugins/simplecommands/CVS/Root wiki/htdocs/applets/FCKeditor/editor/plugins/simplecommands/fckplugin.js wiki/htdocs/applets/FCKeditor/editor/plugins/tablecommands/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/plugins/tablecommands/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/plugins/tablecommands/CVS/Root wiki/htdocs/applets/FCKeditor/editor/plugins/tablecommands/fckplugin.js wiki/htdocs/applets/FCKeditor/editor/skins/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/skins/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/skins/CVS/Root wiki/htdocs/applets/FCKeditor/editor/skins/default/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/skins/default/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/skins/default/CVS/Root wiki/htdocs/applets/FCKeditor/editor/skins/default/fck_contextmenu.css wiki/htdocs/applets/FCKeditor/editor/skins/default/fck_dialog.css wiki/htdocs/applets/FCKeditor/editor/skins/default/fck_editor.css wiki/htdocs/applets/FCKeditor/editor/skins/default/images/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/skins/default/images/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/skins/default/images/CVS/Root wiki/htdocs/applets/FCKeditor/editor/skins/default/images/toolbar.start.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/CVS/Root wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/about.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/anchor.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/bgcolor.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/bold.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/bulletedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/button.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/checkbox.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/copy.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/cut.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/docprops.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/find.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/flash.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/form.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/hiddenfield.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/image.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/imagebutton.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/indent.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/inserthorizontalrule.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/insertorderedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/insertunorderedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/italic.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/justifycenter.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/justifyfull.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/justifyleft.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/justifyright.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/link.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/newpage.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/numberedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/outdent.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/pagebreak.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/paste.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/pastetext.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/pasteword.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/preview.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/print.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/radio.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/redo.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/removeformat.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/replace.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/save.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/select.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/selectall.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/showdetails.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/showtableborders.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/smiley.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/source.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/specialchar.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/spellcheck.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/strikethrough.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/subscript.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/superscript.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/table.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/tablecell.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/tabledeletecells.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/tabledeletecolumns.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/tabledeleterows.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/tableinsertcell.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/tableinsertcolumn.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/tableinsertrow.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/tablemergecells.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/tablesplitcell.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/templates.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/textarea.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/textcolor.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/textfield.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/underline.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/undo.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/universalkey.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/unlink.gif wiki/htdocs/applets/FCKeditor/editor/skins/default/toolbar/unorderedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/skins/office2003/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/skins/office2003/CVS/Root wiki/htdocs/applets/FCKeditor/editor/skins/office2003/fck_contextmenu.css wiki/htdocs/applets/FCKeditor/editor/skins/office2003/fck_dialog.css wiki/htdocs/applets/FCKeditor/editor/skins/office2003/fck_editor.css wiki/htdocs/applets/FCKeditor/editor/skins/office2003/images/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/skins/office2003/images/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/skins/office2003/images/CVS/Root wiki/htdocs/applets/FCKeditor/editor/skins/office2003/images/office.start.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/images/toolbar.end.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/images/toolbar.start.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/CVS/Root wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/about.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/anchor.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/bgcolor.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/bold.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/bulletedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/button.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/checkbox.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/copy.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/cut.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/docprops.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/find.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/flash.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/fontstyleadv.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/form.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/hidden.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/hiddenfield.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/image.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/imagebutton.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/indent.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/input.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/inserthorizontalrule.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/insertorderedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/insertunorderedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/italic.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/justifycenter.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/justifyfull.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/justifyleft.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/justifyright.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/link.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/mail.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/new.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/newpage.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/numberedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/open.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/outdent.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/pagebreak.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/paste.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/pastetext.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/pasteword.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/preview.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/print.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/radio.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/redo.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/removeformat.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/replace.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/save.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/select.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/selectall.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/showdetails.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/showtableborders.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/smiley.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/source.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/specialchar.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/spellcheck.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/strikethrough.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/subscript.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/superscript.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/table.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/tablecell.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/tabledeletecells.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/tabledeletecolumns.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/tabledeleterows.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/tableinsertcell.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/tableinsertcolumn.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/tableinsertrow.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/tablemergecells.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/tablesplitcell.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/templates.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/textarea.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/textcolor.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/textfield.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/underline.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/undo.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/universalkey.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/unlink.gif wiki/htdocs/applets/FCKeditor/editor/skins/office2003/toolbar/unorderedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/skins/silver/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/skins/silver/CVS/Root wiki/htdocs/applets/FCKeditor/editor/skins/silver/fck_contextmenu.css wiki/htdocs/applets/FCKeditor/editor/skins/silver/fck_dialog.css wiki/htdocs/applets/FCKeditor/editor/skins/silver/fck_editor.css wiki/htdocs/applets/FCKeditor/editor/skins/silver/images/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/skins/silver/images/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/skins/silver/images/CVS/Root wiki/htdocs/applets/FCKeditor/editor/skins/silver/images/toolbar.buttonbg.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/images/toolbar.start.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/CVS/Entries wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/CVS/Repository wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/CVS/Root wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/about.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/anchor.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/bgcolor.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/bold.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/bulletedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/button.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/checkbox.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/copy.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/cut.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/docprops.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/find.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/flash.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/form.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/hiddenfield.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/image.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/imagebutton.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/indent.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/inserthorizontalrule.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/insertorderedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/insertunorderedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/italic.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/justifycenter.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/justifyfull.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/justifyleft.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/justifyright.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/link.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/newpage.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/numberedlist.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/outdent.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/pagebreak.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/paste.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/pastetext.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/pasteword.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/preview.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/print.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/radio.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/redo.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/removeformat.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/replace.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/save.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/select.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/selectall.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/showdetails.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/showtableborders.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/smiley-corporate.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/smiley.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/source.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/specialchar.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/spellcheck.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/strikethrough.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/subscript.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/superscript.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/table.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/tablecell.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/tabledeletecells.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/tabledeletecolumns.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/tabledeleterows.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/tableinsertcell.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/tableinsertcolumn.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/tableinsertrow.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/tablemergecells.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/tablesplitcell.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/templates.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/textarea.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/textcolor.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/textfield.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/underline.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/undo.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/universalkey.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/unlink.gif wiki/htdocs/applets/FCKeditor/editor/skins/silver/toolbar/unorderedlist.gif wiki/htdocs/applets/FCKeditor/fckconfig.js wiki/htdocs/applets/FCKeditor/fckeditor.afp wiki/htdocs/applets/FCKeditor/fckeditor.asp wiki/htdocs/applets/FCKeditor/fckeditor.cfc wiki/htdocs/applets/FCKeditor/fckeditor.cfm wiki/htdocs/applets/FCKeditor/fckeditor.js wiki/htdocs/applets/FCKeditor/fckeditor.lasso wiki/htdocs/applets/FCKeditor/fckeditor.php wiki/htdocs/applets/FCKeditor/fckeditor.pl wiki/htdocs/applets/FCKeditor/fckeditor.py wiki/htdocs/applets/FCKeditor/fckstyles.xml wiki/htdocs/applets/FCKeditor/fcktemplates.xml wiki/htdocs/applets/FCKeditor/htaccess.txt wiki/htdocs/applets/FCKeditor/license.txt
diffstat 1062 files changed, 93393 insertions(+), 63755 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Fri Aug 22 22:16:13 2008 +0200
+++ b/.hgtags	Fri Aug 22 22:58:18 2008 +0200
@@ -22,4 +22,4 @@
 9901ffff5280b81d0476e8d1e434ea70e3a6fbdc 1.7.0rc2
 01ef230fb671b0f0636328b04ade9b44c5548327 1.7.0rc3
 761c3a503be2b97d8e7beb902751dbb5e60f3127 1.7.0
-0f4ac92a2c62c789dd55cb24063182909f755a15 SOC2008-END
+b1e192a3651a57aa451fefca06f5a849e1e4b422 SOC2008-END
--- a/MoinMoin/PageEditor.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/PageEditor.py	Fri Aug 22 22:58:18 2008 +0200
@@ -24,6 +24,7 @@
 from MoinMoin.widget import html
 from MoinMoin.widget.dialog import Status
 from MoinMoin.logfile import editlog, eventlog
+from MoinMoin.mail.sendmail import encodeSpamSafeEmail
 from MoinMoin.support.python_compatibility import set
 from MoinMoin.util import filesys, timefuncs, web
 from MoinMoin.events import PageDeletedEvent, PageRenamedEvent, PageCopiedEvent, PageRevertedEvent
@@ -762,6 +763,7 @@
         request = self.request
         now = self._get_local_timestamp()
         u = request.user
+        obfuscated_email_address = encodeSpamSafeEmail(u.email)
         signature = u.signature()
         variables = {
             'PAGE': self.page_name,
@@ -771,6 +773,7 @@
             'USERNAME': signature,
             'USER': "-- %s" % signature,
             'SIG': "-- %s <<DateTime(%s)>>" % (signature, now),
+            'EMAIL': "<<MailTo(%s)>>" % (obfuscated_email_address)
         }
 
         if u.valid and u.name:
--- a/MoinMoin/_tests/__init__.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/_tests/__init__.py	Fri Aug 22 22:58:18 2008 +0200
@@ -3,16 +3,19 @@
     MoinMoin - some common code for testing
 
     @copyright: 2007 MoinMoin:KarolNowak,
-                2008 MoinMoin:ThomasWaldmann
+                2008 MoinMoin:ThomasWaldmann, MoinMoin:ReimarBauer
     @license: GNU GPL, see COPYING for details.
 """
 
 import os, shutil
 
+from MoinMoin.parser.text import Parser
+from MoinMoin.formatter.text_html import Formatter
 from MoinMoin.Page import Page
 from MoinMoin.PageEditor import PageEditor
 from MoinMoin.util import random_string
 from MoinMoin import caching, user
+
 # Promoting the test user -------------------------------------------
 # Usually the tests run as anonymous user, but for some stuff, you
 # need more privs...
@@ -82,6 +85,12 @@
     page.saveText(content, 0)
     return page
 
+def nuke_eventlog(request):
+    """ removes event-log file """
+    fpath = request.rootpage.getPagePath('event-log', isfile=1)
+    if os.path.exists(fpath):
+        os.remove(fpath)
+
 def nuke_page(request, pagename):
     """ completely delete a page, everything in the pagedir """
     page = PageEditor(request, pagename, do_editor_backup=False)
@@ -94,3 +103,15 @@
     """ creates a list of random strings """
     chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
     return [u"%s" % random_string(length, chars) for counter in range(count)]
+
+def make_macro(request, page):
+    """ creates the macro """
+    from MoinMoin import macro
+    p = Parser("##\n", request)
+    p.formatter = Formatter(request)
+    p.formatter.page = page
+    request.page = page
+    request.formatter = p.formatter
+    p.form = request.form
+    m = macro.Macro(p)
+    return m
--- a/MoinMoin/_tests/ldap_testbase.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/_tests/ldap_testbase.py	Fri Aug 22 22:58:18 2008 +0200
@@ -32,6 +32,8 @@
 
     # /etc/init.d/apparmor stop
 
+    Requires Python 2.4 (for subprocess module).
+
     @copyright: 2008 by Thomas Waldmann
     @license: GNU GPL, see COPYING for details.
 """
--- a/MoinMoin/_tests/maketestwiki.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/_tests/maketestwiki.py	Fri Aug 22 22:58:18 2008 +0200
@@ -14,7 +14,6 @@
 
 filename = globals().get("__file__") or sys.argv[0]
 moinpath = os.path.abspath(os.path.join(os.path.dirname(filename), os.pardir, os.pardir))
-sys.path.insert(0, moinpath)
 
 from MoinMoin.support import tarfile
 
@@ -62,5 +61,6 @@
     untarUnderlay()
 
 if __name__ == '__main__':
+    sys.path.insert(0, moinpath)
     run()
 
--- a/MoinMoin/_tests/test_PageEditor.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/_tests/test_PageEditor.py	Fri Aug 22 22:58:18 2008 +0200
@@ -210,6 +210,7 @@
         """
         simple test if it is possible to delete a Dict page after creation
         """
+        become_trusted(self.request)
         pagename = u'SomeDict'
         page = PageEditor(self.request, pagename, do_editor_backup=0)
         body = u"This is an example text"
--- a/MoinMoin/_tests/test_packages.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/_tests/test_packages.py	Fri Aug 22 22:58:18 2008 +0200
@@ -86,7 +86,7 @@
 
     def testSearch(self):
         package = PackagePages(self.request.rootpage.page_name, self.request)
-        assert package.searchpackage(self.request, "Bad") == [u'BadContent']
+        assert package.searchpackage(self.request, "BadCon") == [u'BadContent']
 
     def testListCreate(self):
         package = PackagePages(self.request.rootpage.page_name, self.request)
--- a/MoinMoin/_tests/test_sourcecode.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/_tests/test_sourcecode.py	Fri Aug 22 22:58:18 2008 +0200
@@ -33,6 +33,8 @@
 
 try:
     import xattr
+    if not hasattr(xattr, "xattr"): # there seem to be multiple modules with that name
+        raise ImportError
     def mark_file_ok(path, mtime):
         x = xattr.xattr(path)
         try:
--- a/MoinMoin/action/AttachFile.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/action/AttachFile.py	Fri Aug 22 22:58:18 2008 +0200
@@ -1085,7 +1085,7 @@
 
         browser = DataBrowserWidget(request)
         browser.setData(data)
-        return browser.toHTML()
+        return browser.render()
 
     return ''
 
--- a/MoinMoin/action/Despam.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/action/Despam.py	Fri Aug 22 22:58:18 2008 +0200
@@ -71,7 +71,7 @@
 
     table = DataBrowserWidget(request)
     table.setData(dataset)
-    table.render()
+    return table.render()
 
 class tmp:
     pass
@@ -195,7 +195,7 @@
     elif editor:
         show_pages(request, pagename, editor, timestamp)
     else:
-        show_editors(request, pagename, timestamp)
+        request.write(show_editors(request, pagename, timestamp))
 
     # End content and send footer
     request.write(request.formatter.endContent())
--- a/MoinMoin/action/fckdialog.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/action/fckdialog.py	Fri Aug 22 22:58:18 2008 +0200
@@ -7,7 +7,7 @@
 """
 
 from MoinMoin import config, wikiutil
-import re
+import re
 
 ##############################################################################
 ### Macro dialog
@@ -235,15 +235,15 @@
     else:
         resultlist = iwpreferred[:-1]
     interwiki = "\n".join(
-        ['<option value="%s">%s</option>' % (key, key) for key in resultlist])
-
+        ['<option value="%s">%s</option>' % (key, key) for key in resultlist])
+    
     # wiki url
     url_prefix_static = request.cfg.url_prefix_static
     scriptname = request.script_root
     if not scriptname or scriptname[-1] != "/":
         scriptname += "/"
     action = scriptname
-    basepage = request.page.page_name.encode(config.charset)
+    basepage = request.page.page_name.encode(config.charset)
     request.write('''
 <!--
  * FCKeditor - The text editor for internet
@@ -320,7 +320,7 @@
          <td>
           <span fckLang="WikiDlgName">Wiki:PageName</span><br>
           <select id="sctInterwiki" size="1">
-          %(interwiki)s
+          %(interwiki)s 
           </select>:
           <input id="txtInterwikipagename"></input>
          </td>
--- a/MoinMoin/action/info.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/action/info.py	Fri Aug 22 22:58:18 2008 +0200
@@ -176,7 +176,7 @@
 
         div = html.DIV(id="page-history")
         div.append(html.INPUT(type="hidden", name="action", value="diff"))
-        div.append(history_table.toHTML())
+        div.append(history_table.render())
 
         form = html.FORM(method="GET", action="")
         form.append(div)
--- a/MoinMoin/auth/ldap_login.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/auth/ldap_login.py	Fri Aug 22 22:58:18 2008 +0200
@@ -140,7 +140,7 @@
                         (ldap.OPT_X_TLS, self.start_tls),
                         #(ldap.OPT_X_TLS_ALLOW, 1),
                     ):
-                        if value:
+                        if value is not None:
                             ldap.set_option(option, value)
 
                 server = self.server_uri
--- a/MoinMoin/auth/smb_mount.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/auth/smb_mount.py	Fri Aug 22 22:58:18 2008 +0200
@@ -6,6 +6,8 @@
     authentication at the SMB server). This can be used if you need access
     to files on some share via the wiki, but needs more code to be useful.
 
+    Note: requires Python 2.4 (for subprocess module)
+
     @copyright: 2006-2008 MoinMoin:ThomasWaldmann
                 2007 MoinMoin:JohannesBerg
     @license: GNU GPL, see COPYING for details.
--- a/MoinMoin/caching.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/caching.py	Fri Aug 22 22:58:18 2008 +0200
@@ -25,12 +25,7 @@
 
 
 def get_arena_dir(request, arena, scope):
-    if scope == 'page_or_wiki': # XXX DEPRECATED, remove later
-        if isinstance(arena, str):
-            return os.path.join(request.cfg.cache_dir, request.cfg.siteid, arena)
-        else: # arena is in fact a page object
-            return arena.getPagePath('cache', check_create=1)
-    elif scope == 'item': # arena is a Page instance
+    if scope == 'item': # arena is a Page instance
         # we could move cache out of the page directory and store it to cache_dir
         return arena.getPagePath('cache', check_create=1)
     elif scope == 'wiki':
@@ -49,7 +44,7 @@
 
 
 class CacheEntry:
-    def __init__(self, request, arena, key, scope='page_or_wiki', do_locking=True,
+    def __init__(self, request, arena, key, scope='wiki', do_locking=True,
                  use_pickle=False, use_encode=False):
         """ init a cache entry
             @param request: the request object
--- a/MoinMoin/conftest.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/conftest.py	Fri Aug 22 22:58:18 2008 +0200
@@ -32,7 +32,7 @@
 from MoinMoin._tests import maketestwiki, compat
 modules["unittest"] = compat # evil hack
 
-sys.path.insert(0, str(moindir.join("tests")))
+wikiconfig_dir = str(moindir.join("tests"))
 
 from MoinMoin.support.python_compatibility import set
 from MoinMoin.web.request import TestRequest, Client
@@ -74,6 +74,9 @@
     if not static_state[0]:
         maketestwiki.run(True)
         static_state[0] = True
+    if sys.path[0] != wikiconfig_dir:
+        sys.path.insert(0, wikiconfig_dir) # this is a race with py.test's collectors
+                                           # because they modify sys.path as well
     request = TestRequest()
     request = init(request)
     return request
--- a/MoinMoin/converter/_tests/test_text_html_text_moin_wiki.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/converter/_tests/test_text_html_text_moin_wiki.py	Fri Aug 22 22:58:18 2008 +0200
@@ -788,6 +788,67 @@
 """
         self.do(test, output)
 
+    def testPreSuccess10(self):
+        test = ur"""
+ * {{{{
+{{{
+test
+}}}
+}}}}
+
+"""
+        output = ur"""
+<ul>
+<li>
+<pre>
+{{{
+test
+}}}
+</pre>
+</li>
+</ul>
+"""
+
+    def testPreSuccess11(self):
+        test = ur"""
+ * {{{{
+test
+}}}
+}}}}
+
+"""
+        output = ur"""
+<ul>
+<li>
+<pre>
+test
+}}}
+</pre>
+</li>
+</ul>
+"""
+
+    def testPreSuccess12(self):
+        test = ur"""
+ * {{{{
+{{{
+test
+}}}}
+
+"""
+        output = ur"""
+<ul>
+<li>
+<pre>
+{{{
+test
+</pre>
+</li>
+</ul>
+"""
+
+        self.do(test, output)
+
     def testRule1(self):
         py.test.skip('broken test')
         test = ur"""
--- a/MoinMoin/converter/text_html_text_moin_wiki.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/converter/text_html_text_moin_wiki.py	Fri Aug 22 22:58:18 2008 +0200
@@ -606,6 +606,11 @@
             style = listitem.getAttribute("style")
             if re.match(ur"list-style-type:\s*none", style, re.I):
                 markup = ". "
+                # set markup with white space when list element containes table
+                for i in listitem.childNodes:
+                    if i.nodeType == Node.ELEMENT_NODE:
+                        if i.localName == 'table':
+                            markup = " "
             else:
                 markup = "* "
         elif name == 'dl':
@@ -661,7 +666,10 @@
             self.text.append(indent)
         for i in nodelist:
             if i.nodeType == Node.ELEMENT_NODE:
-                self.process_inline(i)
+                if i.localName == 'br':
+                    self.text.append('<<BR>>')
+                else:
+                    self.process_inline(i)
             elif i.nodeType == Node.TEXT_NODE:
                 self.text.append(i.data.strip('\n').replace('\n', ' '))
         self.text.append(self.new_line)
@@ -700,6 +708,8 @@
                     self.text.append(indent)
                 self.process_table(i)
                 found = True
+            elif name == 'br':
+                pending.append(i)
             else:
                 pending.append(i)
 
@@ -815,16 +825,48 @@
         self.text.append(command)
 
     def process_span(self, node):
-        # ignore span tags - just descend
+        # process span tag for firefox3
+        node_style = node.getAttribute("style")
+
         is_strike = node.getAttribute("class") == "strike"
+        is_strike = is_strike or "line-through" in node_style
+        is_strong = "bold" in node_style
+        is_italic = "italic" in node_style
+        is_underline = "underline" in node_style
+        is_comment = node.getAttribute("class") == "comment"
+
+        # start tag
+        if is_comment:
+            self.text.append("/* ")
         if is_strike:
             self.text.append("--(")
+        if is_strong:
+            self.text.append("'''")
+        if is_italic:
+            self.text.append("''")
+        if is_underline:
+            self.text.append("__")
+
+        # body
         for i in node.childNodes:
             self.process_inline(i)
+
+        # end tag
+        if is_underline:
+            self.text.append("__")
+        if is_italic:
+            self.text.append("''")
+        if is_strong:
+            self.text.append("'''")
         if is_strike:
             self.text.append(")--")
+        if is_comment:
+            self.text.append(" */")
 
     def process_div(self, node):
+        # process indent
+        self._process_indent(node)
+
         # ignore div tags - just descend
         for i in node.childNodes:
             self.visit(i)
@@ -848,9 +890,21 @@
         self.text.extend([self.new_line, "-" * length, self.new_line])
 
     def process_p(self, node):
+        # process indent
+        self._process_indent(node)
         self.process_paragraph_item(node)
         self.text.append("\n\n") # do not use self.new_line here!
 
+    def _process_indent(self, node):
+        # process indent
+        node_style = node.getAttribute("style")
+        match = re.match(r"margin-left:\s*(\d+)px", node_style)
+        if match:
+            left_margin = int(match.group(1))
+            indent_depth = int(left_margin / 40)
+            if indent_depth > 0:
+                self.text.append(' . ')
+
     def process_paragraph_item(self, node):
         for i in node.childNodes:
             if i.nodeType == Node.ELEMENT_NODE:
@@ -870,25 +924,60 @@
         if class_ == "comment": # we currently use this for stuff like ## or #acl
             for i in node.childNodes:
                 if i.nodeType == Node.TEXT_NODE:
-                    self.text.append(i.data)
-                    #print "'%s'" % i.data
+                    self.text.append(i.data.replace('\n', ''))
                 elif i.localName == 'br':
                     self.text.append(self.new_line)
                 else:
                     pass
-                    #print i.localName
         else:
-            self.text.extend(["{{{", self.new_line])
+            content_buffer = []
+            longest_inner_formater = ''
+            bang_args = ''
+            delimiters = []
+
+            """
+            below code fixed for MoinMoinBugs/GuiEditorCantNest bug
+            this has problem when outer delimiter has two more { than inside one
+            e.g. {{{{{{ {{{ foo }}} }}}}}}  --> {{{{ {{{ foo }}} }}}}
+                   {{{foo {{{ }}} foo}}} --> {{{{ {{{ }}} }}}}
+            """
+
             for i in node.childNodes:
                 if i.nodeType == Node.TEXT_NODE:
-                    self.text.append(i.data)
-                    #print "'%s'" % i.data
+                    # get longest pre tag({{{ or }}}) from content
+                    delimiters.extend(re.compile("((?u){+)").findall(i.data))
+                    delimiters.extend(re.compile("((?u)}+)").findall(i.data))
+                    # when first line is empty, start iteration second line of i.data
+                    data_lines = i.data.rstrip().split('\n')
+                    if data_lines[0].strip() == '':
+                        data_lines = data_lines[1:]
+                    for line in data_lines:
+                        if line.strip().startswith('#!'):
+                            if bang_args == '':
+                                bang_args = line.strip()
+                            else:
+                                content_buffer.extend([line, self.new_line])
+                        else:
+                            content_buffer.extend([line, self.new_line])
                 elif i.localName == 'br':
-                    self.text.append(self.new_line_dont_remove)
+                    content_buffer.append(self.new_line_dont_remove)
                 else:
                     pass
                     #print i.localName
-            self.text.extend(["}}}", self.new_line])
+
+            if delimiters:
+                longest_inner_formater = max(delimiters)
+
+            if (len(longest_inner_formater) >= 3):
+                self.text.extend([("{" * (len(longest_inner_formater) + 1)) + bang_args, \
+                                      self.new_line])
+                self.text.extend(content_buffer)
+                self.text.extend(["}" * (len(longest_inner_formater) + 1), \
+                                      self.new_line])
+            else:
+                self.text.extend(["{{{"+bang_args, self.new_line])
+                self.text.extend(content_buffer)
+                self.text.extend(["}}}", self.new_line])
 
     _alignment = {"left": "(",
                   "center": ":",
@@ -1007,7 +1096,8 @@
         return " ".join(result).strip()
 
     def process_table(self, node, style=""):
-        self.text.append(self.new_line)
+        if self.depth == 0:
+            self.text.append(self.new_line)
         self.new_table = True
         style += self._table_style(node)
         for i in node.childNodes:
@@ -1187,6 +1277,9 @@
                 # relative link ../
                 elif desc.startswith('../') and href.endswith(desc[3:]):
                     self.text.append(wikiutil.pagelinkmarkup(desc))
+                # internal link #internal
+                elif '#' in href and pagename.startswith(self.pagename):
+                    self.text.append(wikiutil.pagelinkmarkup(href[href.index('#'):], desc))
                 # labeled link
                 else:
                     self.text.append(wikiutil.pagelinkmarkup(pagename, desc))
--- a/MoinMoin/filter/__init__.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/filter/__init__.py	Fri Aug 22 22:58:18 2008 +0200
@@ -17,12 +17,27 @@
 
 standard_codings = ['utf-8', 'iso-8859-15', 'iso-8859-1', ]
 
+
+def quote_filename(filename):
+    """ quote a filename (could contain blanks or other special chars) in a
+        way suitable for the platform we run on.
+    """
+    # XXX Use os.name AND/OR sys.platform?
+    if os.name == 'posix':
+        filename = "'%s'" % filename
+    elif sys.platform == 'win32':
+        filename = '"%s"' % filename
+    else:
+        raise ValueError("MoinMoin.filter.quote_filename: os/platform not supported")
+    return filename
+
+
 def execfilter(cmd, filename, codings=standard_codings):
     """ use cmd to get plaintext content of filename
         to decode to unicode, we use the first coding of codings list that
         does not throw an exception or force ascii
     """
-    filter_cmd = cmd % filename
+    filter_cmd = cmd % quote_filename(filename)
     child_stdin, child_stdout, child_stderr = os.popen3(filter_cmd)
     data = child_stdout.read()
     errors = child_stderr.read()
--- a/MoinMoin/filter/application_msword.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/filter/application_msword.py	Fri Aug 22 22:58:18 2008 +0200
@@ -4,12 +4,15 @@
 
     Depends on: antiword command from antiword package
 
-    @copyright: 2006 MoinMoin:ThomasWaldmann
+    @copyright: 2006-2008 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
 
 from MoinMoin.filter import execfilter
 
 def execute(indexobj, filename):
-    return execfilter("HOME=/tmp antiword '%s'", filename) # no HOME makes antiword complain
+    cmd = "antiword %s"
+    if os.name == 'posix':
+        cmd = "HOME=/tmp " + cmd  # no HOME makes antiword complain (on Linux)
+    return execfilter(cmd, filename)
 
--- a/MoinMoin/filter/application_pdf.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/filter/application_pdf.py	Fri Aug 22 22:58:18 2008 +0200
@@ -11,5 +11,5 @@
 from MoinMoin.filter import execfilter
 
 def execute(indexobj, filename):
-    return execfilter("pdftotext -enc UTF-8 '%s' -", filename)
+    return execfilter("pdftotext -enc UTF-8 %s -", filename)
 
--- a/MoinMoin/filter/application_vnd_ms_excel.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/filter/application_vnd_ms_excel.py	Fri Aug 22 22:58:18 2008 +0200
@@ -11,7 +11,7 @@
 from MoinMoin.filter import execfilter
 
 def execute(indexobj, filename):
-    data = execfilter("xls2csv '%s'", filename)
+    data = execfilter("xls2csv %s", filename)
     # xls2csv uses comma as field separator and "field content",
     # we strip both to not confuse the indexer
     data = data.replace(u',', u' ').replace(u'"', u' ')
--- a/MoinMoin/formatter/text_gedit.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/formatter/text_gedit.py	Fri Aug 22 22:58:18 2008 +0200
@@ -234,3 +234,9 @@
     def line_anchorlink(self, on, lineno=0):
         return '' # not needed for gui editor feeding
 
+    def span(self, on, **kw):
+        previous_state = self.request.user.show_comments
+        self.request.user.show_comments = True
+        ret = text_html.Formatter.span(self, on, **kw)
+        self.request.user.show_comments = previous_state
+        return ret
--- a/MoinMoin/formatter/text_html_percent.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/formatter/text_html_percent.py	Fri Aug 22 22:58:18 2008 +0200
@@ -17,7 +17,7 @@
 class Formatter(TextHtmlFormatter):
 
     def _open(self, tag, newline=False, attr=None, allowed_attrs=None, **kw):
-        """ Escape % signs in tags, see also text_html.Formatter._open. """
+        """ Escape % characters in tags, see also text_html.Formatter._open. """
         tagstr = TextHtmlFormatter._open(self, tag, newline, attr, allowed_attrs, **kw)
         return tagstr.replace('%', '%%')
 
--- a/MoinMoin/macro/EditTemplates.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/macro/EditTemplates.py	Fri Aug 22 22:58:18 2008 +0200
@@ -1,12 +1,14 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - Create an action link
+    MoinMoin - Create a list of currentpage?action=edit&template=X links
+    for all available templates X. Used by MissingPage.
 
     @copyright: 2004 Johannes Berg <johannes@sipsolutions.de>
     @license: GNU GPL, see COPYING for details.
 """
 
 Dependencies = ["language"]
+
 def macro_EditTemplates(macro):
     result = ''
     # we don't want to spend much CPU for spiders requesting nonexisting pages
--- a/MoinMoin/macro/ShowSmileys.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/macro/ShowSmileys.py	Fri Aug 22 22:58:18 2008 +0200
@@ -49,7 +49,7 @@
     if data:
         browser = DataBrowserWidget(macro.request)
         browser.setData(data)
-        return browser.toHTML()
+        return browser.render()
 
     return ''
 
--- a/MoinMoin/macro/_tests/test_Action.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/macro/_tests/test_Action.py	Fri Aug 22 22:58:18 2008 +0200
@@ -3,46 +3,31 @@
     MoinMoin - MoinMoin.macro.Action Tests
 
     @copyright: 2007 MoinMoin:ReimarBauer
-
     @license: GNU GPL, see COPYING for details.
 """
 import os
+
 from MoinMoin import macro
 from MoinMoin.macro import Action
 from MoinMoin.Page import Page
 from MoinMoin.PageEditor import PageEditor
 
-from MoinMoin._tests import become_trusted, create_page, nuke_page
+from MoinMoin._tests import become_trusted, create_page, make_macro, nuke_page
 
 class TestAction:
     """ testing macro Action calling action raw """
     pagename = u'AutoCreatedMoinMoinTemporaryTestPageForAction'
 
-    def _make_macro(self):
-        """Test helper"""
-        from MoinMoin.parser.text import Parser
-        from MoinMoin.formatter.text_html import Formatter
-        p = Parser("##\n", self.request)
-        p.formatter = Formatter(self.request)
-        p.formatter.page = self.page
-        self.request.formatter = p.formatter
-        p.form = self.request.form
-        m = macro.Macro(p)
-        return m
-
     def testActionCallingRaw(self):
         """ module_tested: executes raw by macro Action on existing page"""
         request = self.request
         become_trusted(request)
-
         self.page = create_page(request, self.pagename, u'= title1 =\n||A||B||\n')
-        m = self._make_macro()
+        m = make_macro(self.request, self.page)
         result = Action.macro_Action(m, 'raw')
         nuke_page(request, self.pagename)
-
         expected = '<a href="/AutoCreatedMoinMoinTemporaryTestPageForAction?action=raw">raw</a>'
         assert result == expected
 
-
 coverage_modules = ['MoinMoin.macro.Action']
 
--- a/MoinMoin/macro/_tests/test_EmbedObject.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/macro/_tests/test_EmbedObject.py	Fri Aug 22 22:58:18 2008 +0200
@@ -4,16 +4,14 @@
 
     @copyright: 2008 MoinMoin:ReimarBauer,
                 2008 MoinMoin:JohannesBerg
-
     @license: GNU GPL, see COPYING for details.
 """
-
 import py
 
 from MoinMoin import macro
 from MoinMoin.action import AttachFile
 
-from MoinMoin._tests import become_trusted, create_page, nuke_page
+from MoinMoin._tests import become_trusted, create_page, make_macro, nuke_page
 
 class TestEmbedObject:
     """ testing macro Action calling action raw """
@@ -22,10 +20,8 @@
     def setup_class(self):
         request = self.request
         pagename = self.pagename
-
         become_trusted(request)
         self.page = create_page(request, pagename, u"Foo")
-
         AttachFile.getAttachDir(request, pagename)
         test_files = [
             ('test.ogg', 'vorbis'),
@@ -40,18 +36,6 @@
     def teardown_class(self):
         nuke_page(self.request, self.pagename)
 
-    def _make_macro(self):
-        """ Test helper """
-        from MoinMoin.parser.text import Parser
-        from MoinMoin.formatter.text_html import Formatter
-        p = Parser("##\n", self.request)
-        p.formatter = Formatter(self.request)
-        p.formatter.page = self.page
-        self.request.formatter = p.formatter
-        p.form = self.request.form
-        m = macro.Macro(p)
-        return m
-
     def testEmbedObjectMimetype(self):
         """ tests defined mimetyes """
         tests = [
@@ -61,13 +45,13 @@
             (u'test.mp3', 'audio/mpeg'),
         ]
         for filename, mimetype in tests:
-            m = self._make_macro()
+            m = make_macro(self.request, self.page)
             result = m.execute('EmbedObject', filename)
             assert mimetype in result
 
     def testEmbedObjectDefaultValues(self):
         """ tests default values of macro EmbedObject """
-        m = self._make_macro()
+        m = make_macro(self.request, self.page)
         filename = 'test.mpg'
         result = m.execute('EmbedObject', u'%s' % filename)
         assert '<object data="/AutoCreatedMoinMoinTemporaryTestPageForEmbedObject?action=AttachFile&amp;do=get&amp;target=test.mpg"' in result
@@ -76,7 +60,7 @@
 
     def testEmbedObjectPercentHeight(self):
         """ tests a unit value for macro EmbedObject """
-        m = self._make_macro()
+        m = make_macro(self.request, self.page)
         filename = 'test.mpg'
         height = '50 %' # also tests that space is allowed in there
         result = m.execute('EmbedObject', u'target=%s, height=%s' % (filename, height))
@@ -86,7 +70,7 @@
 
     def testEmbedObjectFromUrl(self):
         """ tests using a URL for macro EmbedObject """
-        m = self._make_macro()
+        m = make_macro(self.request, self.page)
         target = 'http://localhost/%s?action=AttachFile&do=view&target=test.mpg' % self.pagename
         result = m.execute('EmbedObject', u'target=%s, url_mimetype=video/mpeg' % target)
         assert '<object data="http://localhost/AutoCreatedMoinMoinTemporaryTestPageForEmbedObject?action=AttachFile&amp;do=view&amp;target=test.mpg" type="video/mpeg"' in result
--- a/MoinMoin/macro/_tests/test_FootNote.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/macro/_tests/test_FootNote.py	Fri Aug 22 22:58:18 2008 +0200
@@ -3,16 +3,15 @@
     MoinMoin - MoinMoin.macro.FootNote Tests
 
     @copyright: 2008 MoinMoin:ReimarBauer
-
     @license: GNU GPL, see COPYING for details.
 """
 import os
+
 from MoinMoin import macro
 from MoinMoin.macro import FootNote
 from MoinMoin.Page import Page
 from MoinMoin.PageEditor import PageEditor
-
-from MoinMoin._tests import become_trusted, create_page, nuke_page
+from MoinMoin._tests import become_trusted, create_page, make_macro, nuke_page
 
 class TestFootNote:
     """ testing macro Action calling action raw """
@@ -25,28 +24,14 @@
     def teardown_class(self):
         nuke_page(self.request, self.pagename)
 
-    def _make_macro(self):
-        """Test helper"""
-        from MoinMoin.parser.text import Parser
-        from MoinMoin.formatter.text_html import Formatter
-        p = Parser("##\n", self.request)
-        p.formatter = Formatter(self.request)
-        p.formatter.page = self.page
-        self.request.formatter = p.formatter
-        p.form = self.request.form
-        m = macro.Macro(p)
-        return m
-
     def test_enumbering(self):
         """ module_tested: enumbering of Footnotes"""
-        m = self._make_macro()
+        m = make_macro(self.request, self.page)
         text = 'a'
         FootNote.execute(m, text)
         text = 'b'
         FootNote.execute(m, text)
         result = FootNote.emit_footnotes(m.request, m.request.formatter)
-
         assert result.endswith('2</a>)</li></ol></div>')
 
-
 coverage_modules = ['MoinMoin.macro.FootNote']
--- a/MoinMoin/macro/_tests/test_GetVal.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/macro/_tests/test_GetVal.py	Fri Aug 22 22:58:18 2008 +0200
@@ -3,7 +3,6 @@
     MoinMoin - MoinMoin.macro GetVal tested
 
     @copyright: 2007 MoinMoin:ReimarBauer
-
     @license: GNU GPL, see COPYING for details.
 """
 import os, py
@@ -11,8 +10,7 @@
 from MoinMoin import macro
 from MoinMoin.Page import Page
 from MoinMoin.PageEditor import PageEditor
-
-from MoinMoin._tests import become_trusted, create_page, nuke_page
+from MoinMoin._tests import become_trusted, create_page, make_macro, nuke_page
 
 class TestGetVal:
     """GetVal: testing getVal macro """
@@ -25,43 +23,24 @@
     def teardown_class(self):
         nuke_page(self.request, self.pagename)
 
-    def _make_macro(self):
-        """Test helper"""
-        from MoinMoin.parser.text import Parser
-        from MoinMoin.formatter.text_html import Formatter
-        p = Parser("##\n", self.request)
-        p.formatter = Formatter(self.request)
-        p.formatter.page = self.page
-        self.request.formatter = p.formatter
-        p.form = self.request.form
-        m = macro.Macro(p)
-        return m
-
     def _test_macro(self, name, args):
-        m = self._make_macro()
+        m = make_macro(self.request, self.page)
         return m.execute(name, args)
 
     def testGetValNoACLs(self):
         """ macro GetVal test: 'reads VAR' """
-
         self.page = create_page(self.request, self.pagename, u' VAR:: This is an example')
-
         result = self._test_macro(u'GetVal', "%s,%s" % (self.pagename, u'VAR'))
-
         assert result == "This is an example"
 
     def testGetValAfterADictPageIsDeleted(self):
         """ macro GetVal test: 'reads Dict var after another Dict is removed' """
         request = self.request
-
         page = create_page(request, u'SomeDict', u" EXAMPLE:: This is an example text")
         page.deletePage()
-
         page = create_page(request, self.pagename, u' VAR:: This is a brand new example')
         result = self._test_macro(u'GetVal', "%s,%s" % (self.pagename, u'VAR'))
-
         nuke_page(request, u'SomeDict')
-
         assert result == "This is a brand new example"
 
     def testGetValACLs(self):
--- a/MoinMoin/macro/_tests/test_Hits.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/macro/_tests/test_Hits.py	Fri Aug 22 22:58:18 2008 +0200
@@ -11,8 +11,7 @@
 from MoinMoin.logfile import eventlog
 from MoinMoin.PageEditor import PageEditor
 from MoinMoin.Page import Page
-
-from MoinMoin._tests import become_trusted, create_page, nuke_page
+from MoinMoin._tests import become_trusted, create_page, make_macro, nuke_eventlog, nuke_page
 
 class TestHits:
     """Hits: testing Hits macro """
@@ -22,39 +21,21 @@
         request = self.request
         become_trusted(request)
         self.page = create_page(request, self.pagename, u"Foo!")
-
         # for that test eventlog needs to be empty
-        fpath = request.rootpage.getPagePath('event-log', isfile=1)
-        if os.path.exists(fpath):
-            os.remove(fpath)
-
+        nuke_eventlog(request)
         # hits is based on hitcounts which reads the cache
         caching.CacheEntry(request, 'charts', 'hitcounts', scope='wiki').remove()
 
     def teardown_class(self):
         nuke_page(self.request, self.pagename)
 
-    def _make_macro(self):
-        """Test helper"""
-        from MoinMoin.parser.text import Parser
-        from MoinMoin.formatter.text_html import Formatter
-        p = Parser("##\n", self.request)
-        p.formatter = Formatter(self.request)
-        p.formatter.page = self.page
-        self.request.formatter = p.formatter
-        p.form = self.request.form
-        m = macro.Macro(p)
-        return m
-
     def _test_macro(self, name, args):
-        m = self._make_macro()
+        m = make_macro(self.request, self.page)
         return m.execute(name, args)
 
     def _cleanStats(self):
         # cleans all involved cache and log files
-        fpath = self.request.rootpage.getPagePath('event-log', isfile=1)
-        if os.path.exists(fpath):
-            os.remove(fpath)
+        nuke_eventlog(self.request)
         # hits is based on hitcounts which reads the cache
         caching.CacheEntry(self.request, 'charts', 'hitcounts', scope='wiki').remove()
         arena = Page(self.request, self.pagename)
@@ -67,7 +48,6 @@
         eventlog.EventLog(self.request).add(self.request, 'VIEWPAGE', {'pagename': 'WikiSandBox'})
         for i in range(count):
             eventlog.EventLog(self.request).add(self.request, 'VIEWPAGE', {'pagename': self.pagename})
-
         result = self._test_macro(u'Hits', u'')
         self._cleanStats()
         assert result == str(count)
@@ -81,7 +61,6 @@
         for i in range(count):
             for pagename in pagenames:
                 eventlog.EventLog(self.request).add(self.request, 'VIEWPAGE', {'pagename': pagename})
-
         result = self._test_macro(u'Hits', u'all=True')
         self._cleanStats()
         assert result == str(count * num_pages)
@@ -91,7 +70,6 @@
         eventlog.EventLog(self.request).add(self.request, 'SAVEPAGE', {'pagename': self.pagename})
         # simulate a log entry SAVEPAGE for WikiSandBox to destinguish current page
         eventlog.EventLog(self.request).add(self.request, 'SAVEPAGE', {'pagename': 'WikiSandBox'})
-
         result = self._test_macro(u'Hits', u'event_type=SAVEPAGE')
         self._cleanStats()
         assert result == "1"
@@ -100,7 +78,6 @@
         """ macro test: 'all=True, event_type=SAVEPAGE' for Hits (all pages are counted for SAVEPAGE)"""
         eventlog.EventLog(self.request).add(self.request, 'SAVEPAGE', {'pagename': 'WikiSandBox'})
         eventlog.EventLog(self.request).add(self.request, 'SAVEPAGE', {'pagename': self.pagename})
-
         result = self._test_macro(u'Hits', u'all=True, event_type=SAVEPAGE')
         self._cleanStats()
         assert result == "2"
--- a/MoinMoin/macro/_tests/test_PageHits.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/macro/_tests/test_PageHits.py	Fri Aug 22 22:58:18 2008 +0200
@@ -3,7 +3,6 @@
     MoinMoin - MoinMoin.macro PageHits tested
 
     @copyright: 2008 MoinMoin:ReimarBauer
-
     @license: GNU GPL, see COPYING for details.
 """
 import os
@@ -13,7 +12,7 @@
 from MoinMoin.PageEditor import PageEditor
 from MoinMoin.Page import Page
 
-from MoinMoin._tests import become_trusted, create_page, nuke_page
+from MoinMoin._tests import become_trusted, create_page, make_macro, nuke_eventlog, nuke_page
 
 class TestHits:
     """Hits: testing Hits macro """
@@ -23,12 +22,8 @@
         request = self.request
         become_trusted(request)
         self.page = create_page(request, self.pagename, u"Foo!")
-
         # for that test eventlog needs to be empty
-        fpath = request.rootpage.getPagePath('event-log', isfile=1)
-        if os.path.exists(fpath):
-            os.remove(fpath)
-
+        nuke_eventlog(self.request)
         # hits is based on hitcounts which reads the cache
         caching.CacheEntry(request, 'charts', 'pagehits', scope='wiki').remove()
         caching.CacheEntry(request, 'charts', 'hitcounts', scope='wiki').remove()
@@ -36,20 +31,8 @@
     def teardown_class(self):
         nuke_page(self.request, self.pagename)
 
-    def _make_macro(self):
-        """Test helper"""
-        from MoinMoin.parser.text import Parser
-        from MoinMoin.formatter.text_html import Formatter
-        p = Parser("##\n", self.request)
-        p.formatter = Formatter(self.request)
-        p.formatter.page = self.page
-        self.request.formatter = p.formatter
-        p.form = self.request.form
-        m = macro.Macro(p)
-        return m
-
     def _test_macro(self, name, args):
-        m = self._make_macro()
+        m = make_macro(self.request, self.page)
         return m.execute(name, args)
 
     def testPageHits(self):
@@ -58,7 +41,6 @@
         for counter in range(count):
             eventlog.EventLog(self.request).add(self.request, 'VIEWPAGE', {'pagename': 'PageHits'})
             result = self._test_macro(u'PageHits', u'') # XXX SENSE???
-
         cache = caching.CacheEntry(self.request, 'charts', 'pagehits', scope='wiki', use_pickle=True)
         date, hits = 0, {}
         if cache.exists():
--- a/MoinMoin/macro/_tests/test_StatsChart.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/macro/_tests/test_StatsChart.py	Fri Aug 22 22:58:18 2008 +0200
@@ -3,7 +3,6 @@
     MoinMoin - MoinMoin.macro StatsChart tested
 
     @copyright: 2008 MoinMoin:ReimarBauer
-
     @license: GNU GPL, see COPYING for details.
 """
 import os
@@ -12,7 +11,7 @@
 from MoinMoin.logfile import eventlog
 from MoinMoin.PageEditor import PageEditor
 from MoinMoin.Page import Page
-from MoinMoin._tests import become_trusted, create_page, nuke_page
+from MoinMoin._tests import become_trusted, create_page, make_macro, nuke_page
 
 class TestStatsCharts:
     """StartsChart: testing StatsChart macro """
@@ -28,21 +27,8 @@
     def teardown_class(self):
         nuke_page(self.request, self.pagename)
 
-    def _make_macro(self):
-        """Test helper"""
-        from MoinMoin.parser.text import Parser
-        from MoinMoin.formatter.text_html import Formatter
-        p = Parser("##\n", self.request)
-        p.formatter = Formatter(self.request)
-        p.formatter.page = self.page
-        self.request.page = self.page
-        self.request.formatter = p.formatter
-        p.form = self.request.form
-        m = macro.Macro(p)
-        return m
-
     def _test_macro(self, name, args):
-        m = self._make_macro()
+        m = make_macro(self.request, self.page)
         return m.execute(name, args)
 
     def testStatsChart_useragents(self):
--- a/MoinMoin/macro/_tests/test_macro.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/macro/_tests/test_macro.py	Fri Aug 22 22:58:18 2008 +0200
@@ -10,25 +10,25 @@
 from MoinMoin import macro
 from MoinMoin.parser.text import Parser
 from MoinMoin.formatter.text_html import Formatter
-
+from MoinMoin._tests import become_trusted, create_page, make_macro, nuke_page
 
 class TestMacro:
+    pagename = u'AutoCreatedMoinMoinTemporaryTestPageForTestMacro'
+
+    def setup_class(self):
+        request = self.request
+        become_trusted(request)
+        self.page = create_page(request, self.pagename, u"Foo!")
+
+    def teardown_class(self):
+        nuke_page(self.request, self.pagename)
+
     def testTrivialMacro(self):
         """macro: trivial macro works"""
-        m = self._make_macro()
+        m = make_macro(self.request, self.page)
         expected = m.formatter.linebreak(0)
         result = m.execute("BR", "")
         assert result == expected
 
-    def _make_macro(self):
-        """Test helper"""
-        p = Parser("##\n", self.request)
-        p.formatter = Formatter(self.request)
-        self.request.formatter = p.formatter
-        p.form = self.request.form
-        m = macro.Macro(p)
-        return m
-
-
 coverage_modules = ['MoinMoin.macro']
 
--- a/MoinMoin/mail/_tests/test_sendmail.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/mail/_tests/test_sendmail.py	Fri Aug 22 22:58:18 2008 +0200
@@ -40,6 +40,31 @@
         for coded, expected in self._tests:
             assert sendmail.decodeSpamSafeEmail(coded) == expected
 
+class TestencodeSpamSafeEmail:
+    """mail.sendmail: testing spam safe mail"""
+
+    _tests = (
+        ('', ''),
+        ('@', ' AT '),
+        ('.', ' DOT '),
+        ('-', ' DASH '),
+        ('lower', 'lower'),
+        ('Firstname.Lastname@example.net',
+         'firstname DOT lastname AT example DOT net'),
+        ('F.Lastname@example.net',
+         'f DOT lastname AT example DOT net'),
+        )
+
+    def testEncodeSpamSafeMail(self):
+        """mail.sendmail: encoding mail address to spam safe mail"""
+        for coded, expected in self._tests:
+            assert sendmail.encodeSpamSafeEmail(coded) == expected
+
+    def testEncodeSpamSafeMailAndObfuscate(self):
+        """mail.sendmail: encoding mail address by an obfuscate string to spam safe mail """
+        for coded, expected in self._tests:
+            expected = expected.replace(' AT ', ' AT SYCTE ')
+            assert sendmail.encodeSpamSafeEmail(coded, 'SYCTE') == expected
 
 class TestEncodeAddress:
     """ Address encoding tests
@@ -103,6 +128,5 @@
         expected = address.encode(config.charset)
         assert sendmail.encodeAddress(address, self.charset) == expected
 
-
 coverage_modules = ['MoinMoin.mail.sendmail']
 
--- a/MoinMoin/mail/sendmail.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/mail/sendmail.py	Fri Aug 22 22:58:18 2008 +0200
@@ -156,6 +156,26 @@
     logging.debug("Mail sent OK")
     return (1, _("Mail sent OK"))
 
+def encodeSpamSafeEmail(email_address, obfuscation_text=''):
+    """ Encodes a standard email address to an obfuscated address
+    @param email_address: mail address to encode.
+                          Known characters and their all-uppercase words translation:
+                          "." -> " DOT "
+                          "@" -> " AT "
+                          "-" -> " DASH "
+    @param obfuscation_text: optional text to obfuscate the email.
+                             All characters in the string must be alphabetic
+                             and they will be added in uppercase.
+    """
+    address = email_address.lower()
+    # uppercase letters will be stripped by decodeSpamSafeEmail
+    for word, sign in _transdict.items():
+        address = address.replace(sign, ' %s ' % word)
+    if obfuscation_text.isalpha():
+        # is the obfuscation_text alphabetic
+        address = address.replace(' AT ', ' AT %s ' % obfuscation_text.upper())
+
+    return address
 
 def decodeSpamSafeEmail(address):
     """ Decode obfuscated email address to standard email address
@@ -168,7 +188,7 @@
         "AT"    -> "@"
         "DASH"  -> "-"
 
-    Any unknown all-uppercase words simply get stripped.
+    Any unknown all-uppercase words or an uppercase letter simply get stripped.
     Use that to make it even harder for spam bots!
 
     Blanks (spaces) simply get stripped.
--- a/MoinMoin/parser/_ParserBase.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/parser/_ParserBase.py	Fri Aug 22 22:58:18 2008 +0200
@@ -2,10 +2,12 @@
 """
     MoinMoin - Base Source Parser
 
-    @copyright: 2002 by Taesu Pyo <bigflood@hitel.net>
+    @copyright: 2002 by Taesu Pyo <bigflood@hitel.net>,
+                2005 by Oliver Graf <ograf@bitart.de>,
+                2005-2008 MoinMoin:ThomasWaldmann
+
     @license: GNU GPL, see COPYING for details.
 
-    Docstrings and some refactoring by Oliver Graf <ograf@bitart.de>
 
 basic css:
 
@@ -23,8 +25,95 @@
 """
 
 import re, sha
+
 from MoinMoin import config, wikiutil
 
+class FormatTextBase:
+    pass
+
+class FormatBeginLine(FormatTextBase):
+    def formatString(self, formatter, word):
+        return formatter.code_line(1)
+
+class FormatEndLine(FormatTextBase):
+    def formatString(self, formatter, word):
+        return formatter.code_line(0)
+
+class FormatText(FormatTextBase):
+
+    def __init__(self, fmt):
+        self.fmt = fmt
+
+    def formatString(self, formatter, word):
+        return (formatter.code_token(1, self.fmt) +
+                formatter.text(word) +
+                formatter.code_token(0, self.fmt))
+
+class FormatTextID(FormatTextBase):
+
+    def __init__(self, fmt, icase=False):
+        if not isinstance(fmt, FormatText):
+            fmt = FormatText(fmt)
+        self.setDefaultFormat(fmt)
+        self._ignore_case = icase
+        self.fmt = {}
+
+    def setDefaultFormat(self, fmt):
+        self._def_fmt = fmt
+
+    def addFormat(self, word, fmt):
+        if self._ignore_case:
+            word = word.lower()
+        self.fmt[word] = fmt
+
+    def formatString(self, formatter, word):
+        if self._ignore_case:
+            sword = word.lower()
+        else:
+            sword = word
+        return self.fmt.get(sword, self._def_fmt).formatString(formatter, word)
+
+
+class FormattingRuleSingle:
+
+    def __init__(self, name, str_re, icase=False):
+        self.name = name
+        self.str_re = str_re
+
+    def getStartRe(self):
+        return self.str_re
+
+    def getText(self, parser, hit):
+        return hit
+
+
+class FormattingRulePair:
+
+    def __init__(self, name, str_begin, str_end, icase=False):
+        self.name = name
+        self.str_begin = str_begin
+        self.str_end = str_end
+        re_flags = re.M
+        if icase:
+            re_flags |= re.I
+        self.end_re = re.compile(str_end, re_flags)
+
+    def getStartRe(self):
+        return self.str_begin
+
+    def getText(self, parser, hit):
+        match = self.end_re.search(parser.text, parser.lastpos)
+        if not match:
+            next_lastpos = parser.text_len
+        else:
+            next_lastpos = match.end() + (match.end() == parser.lastpos)
+        r = parser.text[parser.lastpos:next_lastpos]
+        parser.lastpos = next_lastpos
+        return hit + r
+
+
+# ------------------------------------------------------------------------
+
 def parse_start_step(request, args):
     """
     Parses common Colorizer parameters start, step, numbers.
@@ -60,101 +149,39 @@
             nums = -1
     return nums, start, step, attrs
 
-class FormatTextBase:
-    pass
-
-class FormatText(FormatTextBase):
-
-    def __init__(self, fmt):
-        self.fmt = fmt
-
-    def formatString(self, formatter, word):
-        return (formatter.code_token(1, self.fmt) +
-                formatter.text(word) +
-                formatter.code_token(0, self.fmt))
-
-class FormatTextID(FormatTextBase):
-
-    def __init__(self, fmt, icase=0):
-        if not isinstance(fmt, FormatText):
-            self.def_fmt = FormatText(fmt)
-        else:
-            self.def_fmt = fmt
-        self._ignore_case = icase
-        self.fmt = {}
-
-    def addFormat(self, word, fmt):
-        if self._ignore_case:
-            word = word.lower()
-        self.fmt[word] = fmt
-
-    def setDefaultFormat(self, fmt):
-        self.def_fmt = fmt
-
-    def formatString(self, formatter, word):
-        if self._ignore_case:
-            sword = word.lower()
-        else:
-            sword = word
-        return self.fmt.get(sword, self.def_fmt).formatString(formatter, word)
-
-class FormattingRuleSingle:
-
-    def __init__(self, name, str_re, icase=0):
-        self.name = name
-        self.str_re = str_re
-
-    def getStartRe(self):
-        return self.str_re
-
-    def getText(self, parser, hit):
-        return hit
-
-class FormattingRulePair:
-
-    def __init__(self, name, str_begin, str_end, icase=0):
-        self.name = name
-        self.str_begin = str_begin
-        self.str_end = str_end
-        if icase:
-            self.end_re = re.compile(str_end, re.M|re.I)
-        else:
-            self.end_re = re.compile(str_end, re.M)
-
-    def getStartRe(self):
-        return self.str_begin
-
-    def getText(self, parser, hit):
-        match = self.end_re.search(parser.line, parser.lastpos)
-        if not match:
-            next_lastpos = len(parser.line)
-        else:
-            next_lastpos = match.end() + (match.end() == parser.lastpos)
-        r = parser.line[parser.lastpos:next_lastpos]
-        parser.lastpos = next_lastpos
-        return hit + r
-
-
-# ------------------------------------------------------------------------
 
 class ParserBase:
 
     parsername = 'ParserBase'
+    tabwidth = 4
+
+    # for dirty tricks, see comment in format():
+    STARTL, STARTL_RE = u"^\n", ur"\^\n"
+    ENDL, ENDL_RE = u"\n$", ur"\n\$"
+    LINESEP = ENDL + STARTL
 
     def __init__(self, raw, request, **kw):
         self.raw = raw
         self.request = request
         self.show_nums, self.num_start, self.num_step, attrs = parse_start_step(request, kw.get('format_args', ''))
 
-        self._ignore_case = 0
+        self._ignore_case = False
         self._formatting_rules = []
         self._formatting_rules_n2r = {}
         self._formatting_rule_index = 0
         self.rule_fmt = {}
-        self.line_count = len(raw.split('\n'))+1
+        #self.line_count = len(raw.split('\n')) + 1
 
     def setupRules(self):
+        self.addRuleFormat("BEGINLINE", FormatBeginLine())
+        self.addRuleFormat("ENDLINE", FormatEndLine())
+        # we need a little dirty trick here, see comment in format():
+        self.addRule("BEGINLINE", self.STARTL_RE)
+        self.addRule("ENDLINE", self.ENDL_RE)
+
         self.def_format = FormatText('Default')
+        self.reserved_word_format = FormatText('ResWord')
+        self.constant_word_format = FormatText('ConsWord')
         self.ID_format = FormatTextID('ID', self._ignore_case)
         self.addRuleFormat("ID", self.ID_format)
         self.addRuleFormat("Operator")
@@ -169,22 +196,18 @@
         self.addRuleFormat("Special")
         self.addRuleFormat("Preprc")
         self.addRuleFormat("Error")
-        self.reserved_word_format = FormatText('ResWord')
-        self.constant_word_format = FormatText('ConsWord')
+
+    def _addRule(self, name, fmt):
+        self._formatting_rule_index += 1
+        name = "%s_%s" % (name, self._formatting_rule_index) # create unique name
+        self._formatting_rules.append((name, fmt))
+        self._formatting_rules_n2r[name] = fmt
 
     def addRule(self, name, str_re):
-        self._formatting_rule_index += 1
-        n = "%s_%s" % (name, self._formatting_rule_index)
-        f = FormattingRuleSingle(name, str_re, self._ignore_case)
-        self._formatting_rules.append((n, f))
-        self._formatting_rules_n2r[n] = f
+        self._addRule(name, FormattingRuleSingle(name, str_re, self._ignore_case))
 
     def addRulePair(self, name, start_re, end_re):
-        self._formatting_rule_index += 1
-        n = "%s_%s" % (name, self._formatting_rule_index)
-        f = FormattingRulePair(name, start_re, end_re, self._ignore_case)
-        self._formatting_rules.append((n, f))
-        self._formatting_rules_n2r[n] = f
+        self._addRule(name, FormattingRulePair(name, start_re, end_re, self._ignore_case))
 
     def addWords(self, words, fmt):
         if not isinstance(fmt, FormatTextBase):
@@ -209,64 +232,69 @@
 
         self.setupRules()
 
-        l = []
-        for n, f in self._formatting_rules:
-            l.append("(?P<%s>%s)" % (n, f.getStartRe()))
-
+        formatting_regexes = ["(?P<%s>%s)" % (n, f.getStartRe())
+                              for n, f in self._formatting_rules]
+        re_flags = re.M
         if self._ignore_case:
-            scan_re = re.compile("|".join(l), re.M|re.I)
-        else:
-            scan_re = re.compile("|".join(l), re.M)
+            re_flags |= re.I
+        scan_re = re.compile("|".join(formatting_regexes), re_flags)
 
-        self.lastpos = 0
-        self.line = self.raw
+        self.text = self.raw
+
+        # dirty little trick to work around re lib's limitations (it can't have
+        # zero length matches at line beginning for ^ and at the same time match
+        # something else at the beginning of the line):
+        self.text = self.LINESEP.join([line.replace('\r', '') for line in self.text.splitlines()])
+        self.text = self.STARTL + self.text + self.ENDL
+        self.text_len = len(self.text)
+
+        result = [] # collects output
 
         self._code_id = sha.new(self.raw.encode(config.charset)).hexdigest()
-        self.request.write(formatter.code_area(1, self._code_id, self.parsername, self.show_nums, self.num_start, self.num_step))
-
-        self.request.write(formatter.code_line(1))
-            #formatter, len('%d' % (self.line_count,)))
+        result.append(formatter.code_area(1, self._code_id, self.parsername, self.show_nums, self.num_start, self.num_step))
 
-        match = scan_re.search(self.line)
-
-        while match and self.lastpos < len(self.line):
-            # add the match we found
-            self.write_normal_text(formatter,
-                                   self.line[self.lastpos:match.start()])
+        self.lastpos = 0
+        match = scan_re.search(self.text)
+        while match and self.lastpos < self.text_len:
+            # add the rendering of the text left of the match we found
+            text = self.text[self.lastpos:match.start()]
+            if text:
+                result.extend(self.format_normal_text(formatter, text))
             self.lastpos = match.end() + (match.end() == self.lastpos)
 
-            self.write_match(formatter, match)
+            # add the rendering of the match we found
+            result.extend(self.format_match(formatter, match))
 
             # search for the next one
-            match = scan_re.search(self.line, self.lastpos)
-
-        self.write_normal_text(formatter, self.line[self.lastpos:])
-
-        self.request.write(formatter.code_area(0, self._code_id))
-
+            match = scan_re.search(self.text, self.lastpos)
 
-    def write_normal_text(self, formatter, text):
-        first = 1
-        for line in text.expandtabs(4).split('\n'):
-            if not first:
-                self.request.write(formatter.code_line(1))
-            else:
-                first = 0
-            self.request.write(formatter.text(line))
+        # add the rendering of the text right of the last match we found
+        text = self.text[self.lastpos:]
+        if text:
+            result.extend(self.format_normal_text(formatter, text))
 
-    def write_match(self, formatter, match):
+        result.append(formatter.code_area(0, self._code_id))
+        self.request.write(''.join(result))
+
+    def format_normal_text(self, formatter, text):
+        return [formatter.text(text.expandtabs(self.tabwidth))]
+
+    def format_match(self, formatter, match):
+        result = []
         for n, hit in match.groupdict().items():
-            if not hit:
+            if hit is None:
                 continue
             r = self._formatting_rules_n2r[n]
             s = r.getText(self, hit)
             c = self.rule_fmt.get(r.name, None)
             if not c:
                 c = self.def_format
-            first = 1
-            for line in s.expandtabs(4).split('\n'):
-                if not first:
-                    self.request.write(formatter.code_line(1))
-                else:
-                    first = 0
-                self.request.write(c.formatString(formatter, line))
+            if s:
+                lines = s.expandtabs(self.tabwidth).split(self.LINESEP)
+                for line in lines[:-1]:
+                    result.append(c.formatString(formatter, line))
+                    result.append(FormatEndLine().formatString(formatter, ''))
+                    result.append(FormatBeginLine().formatString(formatter, ''))
+                result.append(c.formatString(formatter, lines[-1]))
+        return result
+
--- a/MoinMoin/parser/text_cplusplus.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/parser/text_cplusplus.py	Fri Aug 22 22:58:18 2008 +0200
@@ -36,13 +36,13 @@
     def setupRules(self):
         ParserBase.setupRules(self)
 
-        self.addRulePair("Comment", "/[*]", "[*]/")
-        self.addRule("Comment", "//.*$")
-        self.addRulePair("String", 'L?"', r'$|[^\\](\\\\)*"')
+        self.addRulePair("Comment", r"/[*]", r"[*]/")
+        self.addRule("Comment", r"//.*$")
+        self.addRulePair("String", r'L?"', r'$|[^\\](\\\\)*"')
         self.addRule("Char", r"'\\.'|'[^\\]'")
         self.addRule("Number", r"[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?")
         self.addRule("Preprc", r"^\s*#(.*\\\n)*(.*(?!\\))$")
-        self.addRule("ID", "[a-zA-Z_][0-9a-zA-Z_]*")
+        self.addRule("ID", r"[a-zA-Z_][0-9a-zA-Z_]*")
         self.addRule("SPChar", r"[~!%^&*()+=|\[\]:;,.<>/?{}-]")
 
         reserved_words = ['struct', 'class', 'union', 'enum',
--- a/MoinMoin/parser/text_java.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/parser/text_java.py	Fri Aug 22 22:58:18 2008 +0200
@@ -20,12 +20,12 @@
     def setupRules(self):
         ParserBase.setupRules(self)
 
-        self.addRulePair("Comment", "/[*]", "[*]/")
-        self.addRule("Comment", "//.*$")
-        self.addRulePair("String", '"', r'$|[^\\](\\\\)*"')
+        self.addRulePair("Comment", r"/[*]", r"[*]/")
+        self.addRule("Comment", r"//.*$")
+        self.addRulePair("String", r'"', r'$|[^\\](\\\\)*"')
         self.addRule("Char", r"'\\.'|'[^\\]'")
         self.addRule("Number", r"[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?")
-        self.addRule("ID", "[a-zA-Z_][0-9a-zA-Z_]*")
+        self.addRule("ID", r"[a-zA-Z_][0-9a-zA-Z_]*")
         self.addRule("SPChar", r"[~!%^&*()+=|\[\]:;,.<>/?{}-]")
 
         reserved_words = ['class', 'interface', 'enum', 'import', 'package',
@@ -40,3 +40,4 @@
         constant_words = ['true', 'false', 'null']
 
         self.addConstant(constant_words)
+
--- a/MoinMoin/parser/text_moin_wiki.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/parser/text_moin_wiki.py	Fri Aug 22 22:58:18 2008 +0200
@@ -1364,7 +1364,9 @@
                     lastpos += 1 # proceed, we don't want to match this again
             else:
                 if self.in_pre:
-                    self._parser_content(line[lastpos:])
+                    # ilastpos is more then 0 and result of line slice is empty make useless line
+                    if not (lastpos > 0 and line[lastpos:] == ''):
+                        self._parser_content(line[lastpos:])
                 elif line[lastpos:]:
                     ###result.append('<span class="info">[no match, add rest: <tt>"%s"<tt>]</span>' % line[lastpos:])
                     if not (inhibit_p or self.inhibit_p or self.in_pre or self.formatter.in_p or
--- a/MoinMoin/parser/text_pascal.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/parser/text_pascal.py	Fri Aug 22 22:58:18 2008 +0200
@@ -18,18 +18,18 @@
 
     def __init__(self, raw, request, **kw):
         ParserBase.__init__(self, raw, request, **kw)
-        self._ignore_case = 1
+        self._ignore_case = True
 
     def setupRules(self):
         ParserBase.setupRules(self)
 
-        self.addRulePair("Comment", "\(\*", "\*\)")
-        self.addRulePair("Comment", "\{", "\}")
-        self.addRule("Comment", "//.*$")
-        self.addRulePair("String", '\'', '\'')
+        self.addRulePair("Comment", r"\(\*", r"\*\)")
+        self.addRulePair("Comment", r"\{", r"\}")
+        self.addRule("Comment", r"//.*$")
+        self.addRulePair("String", r"'", r"'")
         self.addRule("Char", r"'\\.'|#[a-f0-9][a-f0-9]")
         self.addRule("Number", r"[0-9](\.[0-9]*)?(eE[+-][0-9])?|\$[0-9a-fA-F]+")
-        self.addRule("ID", "[a-zA-Z_][0-9a-zA-Z_]*")
+        self.addRule("ID", r"[a-zA-Z_][0-9a-zA-Z_]*")
         self.addRule("SPChar", r"[~!%^&*()+=|\[\]:;,.<>/?{}-]")
 
         reserved_words = ['class', 'interface', 'set', 'uses', 'unit',
@@ -48,3 +48,4 @@
         constant_words = ['true', 'false', 'nil']
 
         self.addConstant(constant_words)
+
--- a/MoinMoin/parser/text_python.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/parser/text_python.py	Fri Aug 22 22:58:18 2008 +0200
@@ -2,27 +2,29 @@
 """
     MoinMoin - highlighting Python Source Parser
 
-    @copyright: 2001 Juergen Hermann <jh@web.de>
+    @copyright: 2001 Juergen Hermann <jh@web.de>,
+                2006-2008 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
 
 import StringIO
 import keyword, token, tokenize, sha
+
 from MoinMoin import config, wikiutil
 from MoinMoin.parser._ParserBase import parse_start_step
 
 _KEYWORD = token.NT_OFFSET + 1
-_TEXT    = token.NT_OFFSET + 2
+_TEXT = token.NT_OFFSET + 2
 
 _tokens = {
-    token.NUMBER:       'Number',
-    token.OP:           'Operator',
-    token.STRING:       'String',
-    tokenize.COMMENT:   'Comment',
-    token.NAME:         'ID',
-    token.ERRORTOKEN:   'Error',
-    _KEYWORD:           'ResWord',
-    _TEXT:              'Text',
+    token.NUMBER: 'Number',
+    token.OP: 'Operator',
+    token.STRING: 'String',
+    tokenize.COMMENT: 'Comment',
+    token.NAME: 'ID',
+    token.ERRORTOKEN: 'Error',
+    _KEYWORD: 'ResWord',
+    _TEXT: 'Text',
 }
 
 Dependencies = ['user'] # the "Toggle line numbers link" depends on user's language
@@ -58,10 +60,12 @@
             self.lines.append(pos)
         self.lines.append(len(self.raw))
 
+        self.result = [] # collects output
+
         self._code_id = sha.new(self.raw.encode(config.charset)).hexdigest()
-        self.request.write(formatter.code_area(1, self._code_id, 'ColorizedPython', self.show_num, self.num_start, self.num_step))
+        self.result.append(formatter.code_area(1, self._code_id, 'ColorizedPython', self.show_num, self.num_start, self.num_step))
         self.formatter = formatter
-        self.request.write(formatter.code_line(1))
+        self.result.append(formatter.code_line(1))
         #len('%d' % (len(self.lines)-1, )))
 
         # parse the source and write it
@@ -74,7 +78,7 @@
             errmsg = (self.formatter.linebreak() +
                       self.formatter.strong(1) + "ERROR: %s" % msg + self.formatter.strong(0) +
                       self.formatter.linebreak())
-            self.request.write(errmsg)
+            self.result.append(errmsg)
         except tokenize.TokenError, ex:
             msg = ex[0]
             line = ex[1][0]
@@ -82,9 +86,10 @@
                       self.formatter.strong(1) + "ERROR: %s" % msg + self.formatter.strong(0) +
                       self.formatter.linebreak() +
                       wikiutil.escape(self.raw[self.lines[line]:]))
-            self.request.write(errmsg)
-        self.request.write(self.formatter.code_line(0))
-        self.request.write(formatter.code_area(0, self._code_id))
+            self.result.append(errmsg)
+        self.result.append(self.formatter.code_line(0))
+        self.result.append(formatter.code_area(0, self._code_id))
+        self.request.write(''.join(self.result))
 
     def __call__(self, toktype, toktext, (srow, scol), (erow, ecol), line):
         """ Token handler.
@@ -96,13 +101,13 @@
 
         # handle newlines
         if toktype in [token.NEWLINE, tokenize.NL]:
-            self.request.write(self.formatter.code_line(0))
-            self.request.write(self.formatter.code_line(1))
+            self.result.append(self.formatter.code_line(0))
+            self.result.append(self.formatter.code_line(1))
             return
 
         # send the original whitespace, if needed
         if newpos > oldpos:
-            self.request.write(self.formatter.text(self.raw[oldpos:newpos]))
+            self.result.append(self.formatter.text(self.raw[oldpos:newpos]))
 
         # skip indenting tokens
         if toktype in [token.INDENT, token.DEDENT]:
@@ -117,14 +122,14 @@
         tokid = _tokens.get(toktype, _tokens[_TEXT])
 
         # send text
-        first = 1
+        first = True
         for part in toktext.split('\n'):
             if not first:
-                self.request.write(self.formatter.code_line(0))
-                self.request.write(self.formatter.code_line(1))
+                self.result.append(self.formatter.code_line(0))
+                self.result.append(self.formatter.code_line(1))
             else:
-                first = 0
-            self.request.write(self.formatter.code_token(1, tokid) +
+                first = False
+            self.result.append(self.formatter.code_token(1, tokid) +
                                self.formatter.text(part) +
                                self.formatter.code_token(0, tokid))
 
--- a/MoinMoin/script/maint/mkpagepacks.py	Fri Aug 22 22:16:13 2008 +0200
+++ b/MoinMoin/script/maint/mkpagepacks.py	Fri Aug 22 22:58:18 2008 +0200
@@ -9,9 +9,9 @@
 
 import os
 import zipfile
-from sets import Set
 from datetime import datetime
 
+from MoinMoin.support.python_compatibility import set
 from MoinMoin import wikidicts, wikiutil
 from MoinMoin.Page import Page
 from MoinMoin.packages import packLine, MOIN_PACKAGE_FILE
@@ -49,7 +49,7 @@
         request = self.request
         pageSets = {}
 
-        allPages = Set(request.rootpage.getPageList())
+        allPages = set(request.rootpage.getPageList())
 
         systemPages = wikidicts.Group(request, "SystemPagesGroup").members()
 
@@ -58,20 +58,20 @@
                 #print x + " -> " + repr(wikidicts.Group(request, x).members())
                 self.gd.addgroup(request, pagename)
 
-        langPages = Set()
+        langPages = set()
         for name, group in self.gd.dictdict.items():
-            groupPages = Set(group.members() + [name])
+            groupPages = set(group.members() + [name])
             name = name.replace("SystemPagesIn", "").replace("Group", "")
             pageSets[name] = groupPages
             langPages |= groupPages
 
-        specialPages = Set(["SystemPagesGroup"])
+        specialPages = set(["SystemPagesGroup"])
 
         masterNonSystemPages = allPages - langPages - specialPages
 
-        moinI18nPages = Set([x for x in masterNonSystemPages if x.startswith("MoinI18n")])
+        moinI18nPages = set([x for x in masterNonSystemPages if x.startswith("MoinI18n")])
 
-        nodistPages = moinI18nPages | Set(["InterWikiMap", ])
+        nodistPages = moinI18nPages | set(["InterWikiMap", ])
 
         extraPages = masterNonSystemPages - nodistPages
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/migration/1059997.py	Fri Aug 22 22:58:18 2008 +0200
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - 1st pass of 1.6a to 1.6 migration
+
+    Note: this is a special hack for some users of a early 1.6 alpha version,
+          this code is skipped and NOT executed in a normal release-to-release
+          migration (like when going from 1.5.x release to 1.6.0 release).
+
+          If you run this early 1.6alpha code (with different link markup than
+          1.5.x AND 1.6.x release has), you need to manually put 1059997 into
+          your data/meta file to have this special code executed.
+
+    @copyright: 2008 by Thomas Waldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from _conv160a import DataConverter
+
+def execute(script, data_dir, rev):
+    # the first pass just creates <data_dir>/rename1.txt
+    dc = DataConverter(script.request, data_dir, None)
+    dc.pass1()
+    return 1059998
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/migration/1059998.py	Fri Aug 22 22:58:18 2008 +0200
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - 2nd pass of 1.6 migration
+
+    @copyright: 2007 by Thomas Waldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import os, shutil
+
+from _conv160a import DataConverter
+
+def execute(script, data_dir, rev):
+    rename1_map = os.path.join(data_dir, 'rename1.txt')
+    rename2_map = os.path.join(data_dir, 'rename2.txt')
+    fieldsep = DataConverter.LIST_FIELDSEP
+    if fieldsep == u'\t':
+        fieldsep = u'TAB'
+    if not os.path.exists(rename2_map):
+        print "You must first edit %s." % rename1_map
+        print "For editing it, please use an editor that is able to edit UTF-8 encoded files."
+        print "Carefully edit - the fields are separated by a %s char, do not change this!" % fieldsep
+        print "Entries in this file look like:"
+        print "PAGE OLDPAGENAME NEWPAGENAME"
+        print "FILE OLDPAGENAME OLDFILENAME NEWFILENAME"
+        print "You may ONLY edit the rightmost field (the new name - in case you want to rename the page or file)."
+        print
+        print "After you have finished editing, rename the file to %s and re-issue the moin migrate command." % rename2_map
+        return None # terminate here
+    # the second pass does the conversion, reading <data_dir>/rename2.txt
+    src_data_dir = os.path.abspath(os.path.join(data_dir, '..', 'data.pre160')) # keep the orig data_dir here
+    dst_data_dir = data_dir
+    shutil.move(data_dir, src_data_dir)
+    # the 1.5 parser checks page existance, so we must use the orig, fully populated dir:
+    saved_data_dir = script.request.cfg.data_dir
+    script.request.cfg.data_dir = src_data_dir
+    os.mkdir(dst_data_dir)
+    shutil.move(os.path.join(src_data_dir, 'cache'), os.path.join(dst_data_dir, 'cache')) # mig script has locks there
+    dc = DataConverter(script.request, src_data_dir, dst_data_dir)
+    dc.pass2()
+    # restore correct data dir:
+    script.request.cfg.data_dir = saved_data_dir
+    return 1060000
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/migration/_conv160a.py	Fri Aug 22 22:58:18 2008 +0200
@@ -0,0 +1,567 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - migration from 1.6.0alpha (rev 1844: 58ebb64243cc - used a similar markup as 1.5.8, but with quotes for linking stuff with blanks) to 1.6.0 (creole link style)
+
+    What it does:
+
+    a) reverse underscore == blank stuff in pagenames (introducing this was a fault)
+
+                   pagename            quoted pagename
+       -----------------------------------------------------
+       old         MainPage/Sub_Page   MainPage(2f)Sub_Page
+       new         MainPage/Sub Page   MainPage(2f)Sub(20)Page    or
+       new         MainPage/Sub_Page   MainPage(2f)Sub_Page       (user has to decide by editing rename1.txt)
+
+
+                   markup
+       ----------------------------------------------------
+       old         MoinMoin:MainPage/Sub_Page      ../Sub_Page2
+       new         [[MoinMoin:MainPage/Sub Page]]  [[../Sub Page2]]
+
+
+    b) decode url encoded chars in attachment names (and quote the whole fname):
+
+                   markup
+       ----------------------------------------------------
+       old         attachment:file%20with%20blanks.txt
+       new         [[attachment:file with blanks.txt]]
+
+    c) users: move bookmarks from separate files into user profile
+    d) users: generate new name[] for lists and name{} for dicts
+
+    e) kill all */MoinEditorBackup pages (replaced by drafts functionality)
+
+    @copyright: 2007 by Thomas Waldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import os.path
+import re
+import time
+import codecs, urllib, glob
+
+from MoinMoin import config, wikiutil
+from MoinMoin.script.migration.migutil import opj, listdir, copy_file, move_file, copy_dir
+
+import mimetypes # this MUST be after wikiutil import!
+
+from _conv160b_wiki import convert_wiki
+
+create_rev = True # create a <new> rev with the converted content of <new-1> rev?
+
+def markup_converter(request, pagename, text, renames):
+    """ Convert the <text> content of page <pagename>, using <renames> dict
+        to rename links correctly. Additionally, convert some changed markup.
+    """
+    if text.startswith('<?xml'):
+        # would be done with xslt processor
+        return text
+
+    pis, body = wikiutil.get_processing_instructions(text)
+    for pi, val in pis:
+        if pi == 'format' and val != 'wiki':
+            # not wiki page
+            return text
+
+    text = convert_wiki(request, pagename, text, renames)
+    return text
+
+
+class EventLog:
+    def __init__(self, request, fname):
+        self.request = request
+        self.fname = fname
+        self.data = None
+        self.renames = {}
+
+    def read(self):
+        """ read complete event-log from disk """
+        data = []
+        try:
+            lineno = 0
+            f = file(self.fname, 'r')
+            for line in f:
+                lineno += 1
+                line = line.replace('\r', '').replace('\n', '')
+                if not line.strip(): # skip empty lines
+                    continue
+                fields = line.split('\t')
+                try:
+                    timestamp, action, kvpairs = fields[:3]
+                    timestamp = int(timestamp)
+                    kvdict = wikiutil.parseQueryString(kvpairs)
+                    data.append((timestamp, action, kvdict))
+                except ValueError, err:
+                    # corrupt event log line, log error and skip it
+                    print "Error: invalid event log (%s) line %d, err: %s, SKIPPING THIS LINE!" % (self.fname, lineno, str(err))
+            f.close()
+        except IOError, err:
+            # no event-log
+            pass
+        self.data = data
+
+    def write(self, fname):
+        """ write complete event-log to disk """
+        if self.data:
+            f = file(fname, 'w')
+            for timestamp, action, kvdict in self.data:
+                pagename = kvdict.get('pagename')
+                if pagename and ('PAGE', pagename) in self.renames:
+                    kvdict['pagename'] = self.renames[('PAGE', pagename)]
+                kvpairs = wikiutil.makeQueryString(kvdict, want_unicode=False)
+                fields = str(timestamp), action, kvpairs
+                line = '\t'.join(fields) + '\n'
+                f.write(line)
+            f.close()
+
+    def copy(self, destfname, renames):
+        self.renames = renames
+        self.read()
+        self.write(destfname)
+
+
+class EditLog:
+    def __init__(self, request, fname):
+        self.request = request
+        self.fname = fname
+        self.data = None
+        self.renames = {}
+
+    def read(self):
+        """ read complete edit-log from disk """
+        data = {}
+        try:
+            f = file(self.fname, 'r')
+            for line in f:
+                line = line.replace('\r', '').replace('\n', '')
+                if not line.strip(): # skip empty lines
+                    continue
+                fields = line.split('\t') + [''] * 9
+                timestamp, rev, action, pagename, ip, hostname, userid, extra, comment = fields[:9]
+                timestamp = int(timestamp)
+                rev = int(rev)
+                pagename = wikiutil.unquoteWikiname(pagename)
+                data[(timestamp, rev, pagename)] = (timestamp, rev, action, pagename, ip, hostname, userid, extra, comment)
+            f.close()
+        except IOError, err:
+            # no edit-log
+            pass
+        self.data = data
+
+    def write(self, fname, deleted=False):
+        """ write complete edit-log to disk """
+        if self.data:
+            editlog = self.data.items()
+            editlog.sort()
+            f = file(fname, "w")
+            max_rev = 0
+            for key, fields in editlog:
+                timestamp, rev, action, pagename, ip, hostname, userid, extra, comment = fields
+                if action.startswith('ATT'):
+                    try:
+                        fname = urllib.unquote(extra).decode('utf-8')
+                    except UnicodeDecodeError:
+                        fname = urllib.unquote(extra).decode('iso-8859-1')
+                    if ('FILE', pagename, fname) in self.renames:
+                        fname = self.renames[('FILE', pagename, fname)]
+                    extra = urllib.quote(fname.encode('utf-8'))
+                if ('PAGE', pagename) in self.renames:
+                    pagename = self.renames[('PAGE', pagename)]
+                timestamp = str(timestamp)
+                if rev != 99999999:
+                    max_rev = max(rev, max_rev)
+                revstr = '%08d' % rev
+                pagename = wikiutil.quoteWikinameFS(pagename)
+                fields = timestamp, revstr, action, pagename, ip, hostname, userid, extra, comment
+                log_str = '\t'.join(fields) + '\n'
+                f.write(log_str)
+            if create_rev and not deleted:
+                timestamp = str(wikiutil.timestamp2version(time.time()))
+                revstr = '%08d' % (max_rev + 1)
+                action = 'SAVE'
+                ip = '127.0.0.1'
+                hostname = 'localhost'
+                userid = ''
+                extra = ''
+                comment = "converted to 1.6 markup"
+                fields = timestamp, revstr, action, pagename, ip, hostname, userid, extra, comment
+                log_str = '\t'.join(fields) + '\n'
+                f.write(log_str)
+            f.close()
+
+    def copy(self, destfname, renames, deleted=False):
+        self.renames = renames
+        self.read()
+        self.write(destfname, deleted)
+
+
+class PageRev:
+    """ a single revision of a page """
+    def __init__(self, request, pagename, rev_dir, rev):
+        self.request = request
+        self.pagename = pagename
+        self.rev_dir = rev_dir
+        self.rev = rev
+
+    def read(self):
+        fname = opj(self.rev_dir, '%08d' % self.rev)
+        f = file(fname, "rb")
+        data = f.read()
+        f.close()
+        data = data.decode(config.charset)
+        return data
+
+    def write(self, data, rev_dir, convert, rev=None):
+        if rev is None:
+            rev = self.rev
+        if convert:
+            data = markup_converter(self.request, self.pagename, data, self.renames)
+        fname = opj(rev_dir, '%08d' % rev)
+        data = data.encode(config.charset)
+        f = file(fname, "wb")
+        f.write(data)
+        f.close()
+
+    def copy(self, rev_dir, renames, convert=False, new_rev=None):
+        self.renames = renames
+        data = self.read()
+        self.write(data, rev_dir, convert, new_rev)
+
+
+class Attachment:
+    """ a single attachment """
+    def __init__(self, request, attach_dir, attfile):
+        self.request = request
+        self.path = opj(attach_dir, attfile)
+        self.name = attfile.decode('utf-8', 'replace')
+
+    def copy(self, attach_dir):
+        """ copy attachment file from orig path to new destination """
+        attfile = self.name.encode('utf-8')
+        dest = opj(attach_dir, attfile)
+        copy_file(self.path, dest)
+
+
+class Page:
+    """ represents a page with all related data """
+    def __init__(self, request, pages_dir, qpagename):
+        self.request = request
+        self.name = wikiutil.unquoteWikiname(qpagename)
+        self.name_old = self.name # renaming: still original name when self.name has the new name
+        self.page_dir = opj(pages_dir, qpagename)
+        self.current = None # int current
+        self.editlog = None # dict (see read_editlog)
+        self.revlist = None # list of ints (page text revisions)
+        self.revisions = None # dict int: pagerev obj
+        self.attachments = None # dict of unicode fname: full path
+        self.renames = {} # info for renaming pages/attachments
+
+    def read(self):
+        """ read a page, including revisions, log, attachments from disk """
+        page_dir = self.page_dir
+        # read current file
+        current_fname = opj(page_dir, 'current')
+        if os.path.exists(current_fname):
+            current_file = file(current_fname, "r")
+            current_rev = current_file.read()
+            current_file.close()
+            try:
+                self.current = int(current_rev)
+            except ValueError:
+                print "Error: invalid current file %s, SKIPPING THIS PAGE!" % current_fname
+                return
+        # read edit-log
+        editlog_fname = opj(page_dir, 'edit-log')
+        if os.path.exists(editlog_fname):
+            self.editlog = EditLog(self.request, editlog_fname)
+        # read page revisions
+        rev_dir = opj(page_dir, 'revisions')
+        if os.path.exists(rev_dir):
+            revlist = listdir(rev_dir)
+            revlist = [int(rev) for rev in revlist]
+            revlist.sort()
+            self.revlist = revlist
+            self.revisions = {}
+            for rev in revlist:
+                self.revisions[rev] = PageRev(self.request, self.name_old, rev_dir, rev)
+        # set deleted status
+        self.is_deleted = not self.revisions or self.current not in self.revisions
+        # read attachment filenames
+        attach_dir = opj(page_dir, 'attachments')
+        if os.path.exists(attach_dir):
+            self.attachments = {}
+            attlist = listdir(attach_dir)
+            for attfile in attlist:
+                a = Attachment(self.request, attach_dir, attfile)
+                self.attachments[a.name] = a
+
+    def write(self, pages_dir):
+        """ write a page, including revisions, log, attachments to disk """
+        if ('PAGE', self.name) in self.renames:
+            name_new = self.renames[('PAGE', self.name)]
+            if name_new != self.name:
+                print "Renaming page %r -> %r" % (self.name, name_new)
+                self.name_old = self.name
+                self.name = name_new
+        qpagename = wikiutil.quoteWikinameFS(self.name)
+        page_dir = opj(pages_dir, qpagename)
+        os.makedirs(page_dir)
+        # write current file
+        current = self.current
+        if current is not None:
+            if create_rev and not self.is_deleted:
+                current += 1
+            current_fname = opj(page_dir, 'current')
+            current_file = file(current_fname, "w")
+            current_str = '%08d\n' % current
+            current_file.write(current_str)
+            current_file.close()
+        # copy edit-log
+        if self.editlog is not None:
+            editlog_fname = opj(page_dir, 'edit-log')
+            self.editlog.copy(editlog_fname, self.renames, deleted=self.is_deleted)
+        # copy page revisions
+        if self.revisions is not None:
+            rev_dir = opj(page_dir, 'revisions')
+            os.makedirs(rev_dir)
+            for rev in self.revlist:
+                if create_rev:
+                    self.revisions[rev].copy(rev_dir, self.renames)
+                else:
+                    if int(rev) == self.current:
+                        self.revisions[rev].copy(rev_dir, self.renames, convert=True)
+                    else:
+                        self.revisions[rev].copy(rev_dir, self.renames)
+            if create_rev and not self.is_deleted:
+                self.revisions[rev].copy(rev_dir, self.renames, convert=True, new_rev=rev+1)
+
+        # copy attachments
+        if self.attachments is not None:
+            attach_dir = opj(page_dir, 'attachments')
+            os.makedirs(attach_dir)
+            for fn, att in self.attachments.items():
+                # we have to check for renames here because we need the (old) pagename, too:
+                if ('FILE', self.name_old, fn) in self.renames:
+                    fn_new = self.renames[('FILE', self.name_old, fn)]
+                    if fn_new != fn:
+                        print "Renaming file %r %r -> %r" % (self.name_old, fn, fn_new)
+                        att.name = fn_new
+                att.copy(attach_dir)
+
+    def copy(self, pages_dir, renames):
+        self.renames = renames
+        self.read()
+        self.write(pages_dir)
+
+
+class User:
+    """ represents a user with all related data """
+    def __init__(self, request, users_dir, uid):
+        self.request = request
+        self.uid = uid
+        self.users_dir = users_dir
+        self.profile = None
+        self.bookmarks = None
+
+    def read(self):
+        """ read profile and bookmarks data from disk """
+        self.profile = {}
+        fname = opj(self.users_dir, self.uid)
+        # read user profile
+        f = codecs.open(fname, 'r', config.charset)
+        for line in f:
+            line = line.replace(u'\r', '').replace(u'\n', '')
+            if not line.strip() or line.startswith(u'#'): # skip empty or comment lines
+                continue
+            try:
+                key, value = line.split(u'=', 1)
+            except Exception, err:
+                print "Error: User reader can not parse line %r from profile %r (%s)" % (line, fname, str(err))
+                continue
+            self.profile[key] = value
+        f.close()
+        # read bookmarks
+        self.bookmarks = {}
+        fname_pattern = opj(self.users_dir, "%s.*.bookmark" % self.uid)
+        for fname in glob.glob(fname_pattern):
+            f = file(fname, "r")
+            bookmark = f.read()
+            f.close()
+            wiki = fname.replace('.bookmark', '').replace(opj(self.users_dir, self.uid+'.'), '')
+            self.bookmarks[wiki] = int(bookmark)
+        # don't care about trail
+
+    def write(self, users_dir):
+        """ write profile and bookmarks data to disk """
+        fname = opj(users_dir, self.uid)
+        f = codecs.open(fname, 'w', config.charset)
+        for key, value in self.profile.items():
+            if key in (u'subscribed_pages', u'quicklinks'):
+                pages = value.split(u'\t')
+                for i in range(len(pages)):
+                    pagename = pages[i]
+                    try:
+                        interwiki, pagename = pagename.split(u':', 1)
+                    except:
+                        interwiki, pagename = u'Self', pagename
+                    if interwiki == u'Self' or interwiki == self.request.cfg.interwikiname:
+                        if ('PAGE', pagename) in self.renames:
+                            pagename = self.renames[('PAGE', pagename)]
+                            pages[i] = u'%s:%s' % (interwiki, pagename)
+                key += '[]' # we have lists here
+                value = u'\t'.join(pages)
+                f.write(u"%s=%s\n" % (key, value))
+            else:
+                f.write(u"%s=%s\n" % (key, value))
+        bookmark_entries = [u'%s:%s' % item for item in self.bookmarks.items()]
+        key = u"bookmarks{}"
+        value = u'\t'.join(bookmark_entries)
+        f.write(u"%s=%s\n" % (key, value))
+        f.close()
+        # don't care about trail
+
+    def copy(self, users_dir, renames):
+        self.renames = renames
+        self.read()
+        self.write(users_dir)
+
+
+class DataConverter(object):
+    def __init__(self, request, src_data_dir, dest_data_dir):
+        self.request = request
+        self.sdata = src_data_dir
+        self.ddata = dest_data_dir
+        self.pages = {}
+        self.users = {}
+        self.complete = {}
+        self.renames = {}
+        self.complete_fname = opj(self.sdata, 'complete.txt')
+        self.rename_fname1 = opj(self.sdata, 'rename1.txt')
+        self.rename_fname2 = opj(self.sdata, 'rename2.txt')
+
+    def pass1(self):
+        """ First create the rename list - the user has to review/edit it as
+            we can't decide about page/attachment names automatically.
+        """
+        self.read_src()
+        # pages
+        for pn, p in self.pages.items():
+            p.read()
+            if not p.revisions:
+                continue # we don't care for pages with no revisions (trash)
+            if pn.endswith('/MoinEditorBackup'):
+                continue # we don't care for old editor backups
+            self.complete[('PAGE', pn)] = None
+            if "_" in pn:
+                # log all pagenames with underscores
+                self.renames[('PAGE', pn)] = None
+            if p.attachments is not None:
+                for fn in p.attachments:
+                    try:
+                        fn_str = fn.encode('ascii')
+                        log = False # pure ascii filenames are no problem
+                    except UnicodeEncodeError:
+                        log = True # this file maybe has a strange representation in wiki markup
+                    else:
+                        if ' ' in fn_str or '%' in fn_str: # files with blanks need quoting
+                            log = True
+                    self.complete[('FILE', pn, fn)] = None
+                    if log:
+                        # log all strange attachment filenames
+                        fn_str = fn.encode('utf-8')
+                        self.renames[('FILE', pn, fn)] = None
+        self.save_list(self.complete_fname, self.complete)
+        self.save_list(self.rename_fname1, self.renames)
+
+    LIST_FIELDSEP = u'|' # in case | makes trouble, one can use \t tab char
+
+    def save_list(self, fname, what):
+        what_sorted = what.keys()
+        # make sure we have 3-tuples:
+        what_sorted = [(k + (None, ))[:3] for k in what_sorted]
+        # we only have python 2.3, thus no cmp keyword for the sort() call,
+        # thus we need to do it the more complicated way:
+        what_sorted = [(pn, fn, rtype) for rtype, pn, fn in what_sorted] # shuffle
+        what_sorted.sort() # sort
+        what_sorted = [(rtype, pn, fn) for pn, fn, rtype in what_sorted] # shuffle
+        f = codecs.open(fname, 'w', 'utf-8')
+        for rtype, pn, fn in what_sorted:
+            if rtype == 'PAGE':
+                line = (rtype, pn, pn)
+            elif rtype == 'FILE':
+                line = (rtype, pn, fn, fn)
+            line = self.LIST_FIELDSEP.join(line)
+            f.write(line + u'\n')
+        f.close()
+
+    def load_list(self, fname, what):
+        f = codecs.open(fname, 'r', 'utf-8')
+        for line in f:
+            line = line.rstrip()
+            if not line:
+                continue
+            t = line.split(self.LIST_FIELDSEP)
+            rtype, p1, p2, p3 = (t + [None]*3)[:4]
+            if rtype == u'PAGE':
+                what[(str(rtype), p1)] = p2
+            elif rtype == u'FILE':
+                what[(str(rtype), p1, p2)] = p3
+        f.close()
+
+    def pass2(self):
+        """ Second, read the (user edited) rename list and do the renamings everywhere. """
+        self.read_src()
+        #self.load_list(self.complete_fname, self.complete)
+        self.load_list(self.rename_fname2, self.renames)
+        self.write_dest()
+
+    def read_src(self):
+        # create Page objects in memory
+        pages_dir = opj(self.sdata, 'pages')
+        pagelist = listdir(pages_dir)
+        for qpagename in pagelist:
+            p = Page(self.request, pages_dir, qpagename)
+            self.pages[p.name] = p
+
+        # create User objects in memory
+        users_dir = opj(self.sdata, 'user')
+        user_re = re.compile(r'^\d+\.\d+(\.\d+)?$')
+        userlist = listdir(users_dir)
+        userlist = [f for f in userlist if user_re.match(f)]
+        for userid in userlist:
+            u = User(self.request, users_dir, userid)
+            self.users[u.uid] = u
+
+        # create log objects in memory
+        self.editlog = EditLog(self.request, opj(self.sdata, 'edit-log'))
+        self.eventlog = EventLog(self.request, opj(self.sdata, 'event-log'))
+
+    def write_dest(self):
+        self.init_dest()
+        # copy pages
+        pages_dir = opj(self.ddata, 'pages')
+        for pn, page in self.pages.items():
+            if pn.endswith('/MoinEditorBackup'):
+                continue # we don't care for old editor backups
+            page.copy(pages_dir, self.renames)
+
+        # copy users
+        users_dir = opj(self.ddata, 'user')
+        for user in self.users.values():
+            user.copy(users_dir, self.renames)
+
+        # copy logs
+        self.editlog.copy(opj(self.ddata, 'edit-log'), self.renames)
+        self.eventlog.copy(opj(self.ddata, 'event-log'), self.renames)
+
+    def init_dest(self):
+        try:
+            os.makedirs(self.ddata)
+        except:
+            pass
+        os.makedirs(opj(self.ddata, 'pages'))
+        os.makedirs(opj(self.ddata, 'user'))
+        copy_dir(opj(self.sdata, 'plugin'), opj(self.ddata, 'plugin'))
+        copy_file(opj(self.sdata, 'intermap.txt'), opj(self.ddata, 'intermap.txt'))
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/migration/_conv160a_wiki.py	Fri Aug 22 22:58:18 2008 +0200
@@ -0,0 +1,629 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - convert content in 1.6.0alpha (rev 1844: 58ebb64243cc) wiki markup to 1.6.0 style
+               by using a modified 1.6.0alpha parser as translator.
+
+    Assuming we have this "renames" map:
+    -------------------------------------------------------
+    'PAGE', 'some_page'        -> 'some page'
+    'FILE', 'with%20blank.txt' -> 'with blank.txt'
+
+    Markup transformations needed:
+    -------------------------------------------------------
+    ["some_page"]           -> [[some page]] # renamed
+    [:some_page:some text]  -> [[some page|some text]]
+    [:page:text]            -> [[page|text]]
+                               (with a page not being renamed)
+
+    attachment:with%20blank.txt -> [[attachment:with blank.txt]]
+    attachment:some_page/with%20blank.txt -> [[attachment:some page/with blank.txt]]
+    The attachment processing should also urllib.unquote the filename (or at
+    least replace %20 by space) and put it into "quotes" if it contains spaces.
+
+    @copyright: 2007 MoinMoin:JohannesBerg,
+                2007 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import re
+
+from MoinMoin import i18n
+i18n.wikiLanguages = lambda: {}
+
+from MoinMoin import config, macro, wikiutil
+from MoinMoin.action import AttachFile
+from MoinMoin.Page import Page
+from MoinMoin.support.python_compatibility import rsplit
+
+import wikiutil160a
+from text_moin160a_wiki import Parser
+
+QUOTE_CHARS = u"'\""
+
+def convert_wiki(request, pagename, intext, renames):
+    """ Convert content written in wiki markup """
+    noeol = False
+    if not intext.endswith('\r\n'):
+        intext += '\r\n'
+        noeol = True
+    c = Converter(request, pagename, intext, renames)
+    result = request.redirectedOutput(c.convert, request)
+    if noeol and result.endswith('\r\n'):
+        result = result[:-2]
+    return result
+
+
+STONEAGE_IMAGELINK = False # True for ImageLink(target,image), False for ImageLink(image,target)
+
+# copied from moin 1.6.0 macro/ImageLink.py (to be safe in case we remove ImageLink some day)
+# ... and slightly modified/refactored for our needs here.
+# hint: using parse_quoted_separated from wikiutil does NOT work here, because we do not have
+#       quoted urls when they contain a '=' char in the 1.5 data input.
+def explore_args(args):
+    """ explore args for positional and keyword parameters """
+    if args:
+        args = args.split(',')
+        args = [arg.strip() for arg in args]
+    else:
+        args = []
+
+    kw_count = 0
+    kw = {} # keyword args
+    pp = [] # positional parameters
+
+    kwAllowed = ('width', 'height', 'alt')
+
+    for arg in args:
+        if '=' in arg:
+            key, value = arg.split('=', 1)
+            key_lowerstr = str(key.lower())
+            # avoid that urls with "=" are interpreted as keyword
+            if key_lowerstr in kwAllowed:
+                kw_count += 1
+                kw[key_lowerstr] = value
+            elif not kw_count and '://' in arg:
+                # assuming that this is the image
+                pp.append(arg)
+        else:
+            pp.append(arg)
+
+    if STONEAGE_IMAGELINK and len(pp) >= 2:
+        pp[0], pp[1] = pp[1], pp[0]
+
+    return pp, kw
+
+
+class Converter(Parser):
+    def __init__(self, request, pagename, raw, renames):
+        self.pagename = pagename
+        self.raw = raw
+        self.renames = renames
+        self.request = request
+        self._ = None
+        self.in_pre = 0
+
+        self.formatting_rules = self.formatting_rules % {'macronames': u'|'.join(['ImageLink', ] + macro.getNames(self.request.cfg))}
+
+    # no change
+    def return_word(self, word):
+        return word
+    _emph_repl = return_word
+    _emph_ibb_repl = return_word
+    _emph_ibi_repl = return_word
+    _emph_ib_or_bi_repl = return_word
+    _u_repl = return_word
+    _strike_repl = return_word
+    _sup_repl = return_word
+    _sub_repl = return_word
+    _small_repl = return_word
+    _big_repl = return_word
+    _tt_repl = return_word
+    _tt_bt_repl = return_word
+    _remark_repl = return_word
+    _table_repl = return_word
+    _tableZ_repl = return_word
+    _rule_repl = return_word
+    _smiley_repl = return_word
+    _smileyA_repl = return_word
+    _ent_repl = return_word
+    _ent_numeric_repl = return_word
+    _ent_symbolic_repl = return_word
+    _heading_repl = return_word
+    _email_repl = return_word
+    _notword_repl = return_word
+    _indent_repl = return_word
+    _li_none_repl = return_word
+    _li_repl = return_word
+    _ol_repl = return_word
+    _dl_repl = return_word
+    _comment_repl = return_word
+
+    # translate pagenames using pagename translation map
+
+    def _replace(self, key):
+        """ replace a item_name if it is in the renames dict
+            key is either a 2-tuple ('PAGE', pagename)
+            or a 3-tuple ('FILE', pagename, filename)
+        """
+        current_page = self.pagename
+        item_type, page_name, file_name = (key + (None, ))[:3]
+        abs_page_name = wikiutil.AbsPageName(current_page, page_name)
+        if item_type == 'PAGE':
+            key = (item_type, abs_page_name)
+            new_name = self.renames.get(key)
+            if new_name is None:
+                # we don't have an entry in rename map - apply the same magic
+                # to the page name as 1.5 did (" " -> "_") and try again:
+                abs_magic_name = abs_page_name.replace(u' ', u'_')
+                key = (item_type, abs_magic_name)
+                new_name = self.renames.get(key)
+                if new_name is None:
+                    # we didn't find it under the magic name either -
+                    # that means we do not rename it!
+                    new_name = page_name
+            if new_name != page_name and abs_page_name != page_name:
+                # we have to fix the (absolute) new_name to be a relative name (as it was before)
+                new_name = wikiutil.RelPageName(current_page, new_name)
+        elif item_type == 'FILE':
+            key = (item_type, abs_page_name, file_name)
+            new_name = self.renames.get(key)
+            if new_name is None:
+                # we don't have an entry in rename map - apply the same magic
+                # to the page name as 1.5 did (" " -> "_") and try again:
+                abs_magic_name = abs_page_name.replace(u' ', u'_')
+                key = (item_type, abs_magic_name, file_name)
+                new_name = self.renames.get(key)
+                if new_name is None:
+                    # we didn't find it under the magic name either -
+                    # that means we do not rename it!
+                    new_name = file_name
+        return new_name
+
+    def _replace_target(self, target):
+        target_and_anchor = rsplit(target, '#', 1)
+        if len(target_and_anchor) > 1:
+            target, anchor = target_and_anchor
+            target = self._replace(('PAGE', target))
+            return '%s#%s' % (target, anchor)
+        else:
+            target = self._replace(('PAGE', target))
+            return target
+
+    # markup conversion
+
+    def _macro_repl(self, word):
+        # we use [[...]] for links now, macros will be <<...>>
+        macro_rule = ur"""
+            \[\[
+            (?P<macro_name>\w+)
+            (\((?P<macro_args>.*?)\))?
+            \]\]
+        """
+        word = unicode(word) # XXX why is word not unicode before???
+        m = re.match(macro_rule, word, re.X|re.U)
+        macro_name = m.group('macro_name')
+        macro_args = m.group('macro_args')
+        if macro_name == 'ImageLink':
+            fixed, kw = explore_args(macro_args)
+            #print "macro_args=%r" % macro_args
+            #print "fixed=%r, kw=%r" % (fixed, kw)
+            image, target = (fixed + ['', ''])[:2]
+            if image is None:
+                image = ''
+            if target is None:
+                target = ''
+            if '://' not in image:
+                # if it is not a URL, it is meant as attachment
+                image = u'attachment:%s' % image
+            if not target:
+                target = image
+            elif target.startswith('inline:'):
+                target = 'attachment:' + target[7:] # we don't support inline:
+            elif target.startswith('wiki:'):
+                target = target[5:] # drop wiki:
+            image_attrs = []
+            alt = kw.get('alt') or ''
+            width = kw.get('width')
+            if width is not None:
+                image_attrs.append(u"width=%s" % width)
+            height = kw.get('height')
+            if height is not None:
+                image_attrs.append(u"height=%s" % height)
+            image_attrs = u", ".join(image_attrs)
+            if image_attrs:
+                image_attrs = u'|' + image_attrs
+            if alt or image_attrs:
+                alt = u'|' + alt
+            result = u'[[%s|{{%s%s%s}}]]' % (target, image, alt, image_attrs)
+        else:
+            if macro_args:
+                macro_args = u"(%s)" % macro_args
+            else:
+                macro_args = u''
+            result = u"<<%s%s>>" % (macro_name, macro_args)
+        # XXX later check whether some to be renamed pagename is used as macro param
+        return result
+
+    def _word_repl(self, word, text=None):
+        """Handle WikiNames."""
+        if not text:
+            if wikiutil.isStrictWikiname(word):
+                return word
+            else:
+                return '[[%s]]' % word
+        else: # internal use:
+            return '[[%s|%s]]' % (word, text)
+
+    def _wikiname_bracket_repl(self, text):
+        """Handle special-char wikinames with link text, like:
+           ["Jim O'Brian" Jim's home page] or ['Hello "world"!' a page with doublequotes]
+        """
+        word = text[1:-1] # strip brackets
+        first_char = word[0]
+        if first_char in QUOTE_CHARS:
+            # split on closing quote
+            target, linktext = word[1:].split(first_char, 1)
+        else: # not quoted
+            # split on whitespace
+            target, linktext = word.split(None, 1)
+        if target:
+            target = self._replace(('PAGE', target))
+            linktext = linktext.strip()
+            if linktext and linktext != target:
+                return '[[%s|%s]]' % (target, linktext)
+            else:
+                return '[[%s]]' % target
+        else:
+            return text
+
+    ''' old:
+    def _interwiki_repl(self, word):
+        """Handle InterWiki links."""
+        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, word)
+        if wikitag_bad:
+            return word
+        else:
+            wikiname, pagename = word.split(':', 1)
+            pagename = wikiutil.url_unquote(pagename) # maybe someone has used %20 for blanks in pagename
+            camelcase = wikiutil.isStrictWikiname(pagename)
+            if wikiname in ('Self', self.request.cfg.interwikiname):
+                pagename = self._replace(('PAGE', pagename))
+                if camelcase:
+                    return '%s' % pagename # optimize special case
+                else:
+                    return '[[%s]]' % pagename # optimize special case
+            else:
+                if ' ' in pagename: # we could get a ' '  by urlunquoting
+                    return '[[%s:%s]]' % (wikiname, pagename)
+                else:
+                    return '%s:%s' % (wikiname, pagename)
+    '''
+
+    def _interwiki_repl(self, word):
+        """Handle InterWiki links."""
+        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, word)
+        if wikitag_bad:
+            return word
+        else:
+            return self.interwiki("wiki:" + word)
+
+    def interwiki(self, target_and_text, **kw):
+        scheme, rest = target_and_text.split(':', 1)
+        wikiname, pagename, text = wikiutil160a.split_wiki(rest)
+        if text:
+            text = '|' + text
+
+        if (pagename.startswith(wikiutil.CHILD_PREFIX) or # fancy link to subpage [wiki:/SubPage text]
+            Page(self.request, pagename).exists()): # fancy link to local page [wiki:LocalPage text]
+            pagename = wikiutil.url_unquote(pagename)
+            pagename = self._replace_target(pagename)
+            return '[[%s%s]]' % (pagename, text)
+
+        if wikiname in ('Self', self.request.cfg.interwikiname, ''): # [wiki:Self:LocalPage text] or [:LocalPage:text]
+            pagename = wikiutil.url_unquote(pagename)
+            pagename = self._replace_target(pagename)
+            camelcase = wikiutil.isStrictWikiname(pagename)
+            if camelcase and text == pagename:
+                return '%s' % pagename # optimize special case
+            else:
+                return '[[%s%s]]' % (pagename, text)
+
+        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, wikiname+':')
+        if wikitag_bad: # likely we got some /InterWiki as wikitail, we don't want that!
+            pagename = wikiutil.url_unquote(pagename)
+            pagename = self._replace_target(pagename)
+            wikitail = pagename
+        else: # good
+            wikitail = wikiutil.url_unquote(pagename)
+
+        # link to self?
+        if wikiutil.isPicture(wikitail):
+            return '{{%s:%s%s}}' % (wikitag, wikitail, text)
+        else:
+            if ' ' not in wikitail and not text:
+                return '%s:%s' % (wikitag, wikitail)
+            else:
+                return '[[%s:%s%s]]' % (wikitag, wikitail, text)
+
+    ''' old:
+    def interwiki(self, url_and_text):
+        # keep track of whether this is a self-reference, so links
+        # are always shown even the page doesn't exist.
+        wikiname, pagename = wikiutil.split_wiki(url)
+    '''
+    '''
+    def attachment(self, url_and_text):
+        """ This gets called on attachment URLs. """
+        if len(url_and_text) == 1:
+            url = url_and_text[0]
+            text = ''
+        else:
+            url, text = url_and_text
+            text = '|' + text
+
+        scheme, fname = url.split(":", 1)
+        #scheme, fname, text = wikiutil.split_wiki(target_and_text)
+
+        pagename, fname = AttachFile.absoluteName(fname, self.pagename)
+        from_this_page = pagename == self.pagename
+        fname = self._replace(('FILE', pagename, fname))
+        fname = wikiutil.url_unquote(fname, want_unicode=True)
+        fname = self._replace(('FILE', pagename, fname))
+        pagename = self._replace(('PAGE', pagename))
+        if from_this_page:
+            name = fname
+        else:
+            name = "%s/%s" % (pagename, fname)
+
+        if scheme == 'drawing':
+            return "{{drawing:%s%s}}" % (name, text)
+
+        # check for image URL, and possibly return IMG tag
+        # (images are always inlined, just like for other URLs)
+        if wikiutil.isPicture(name):
+            return "{{attachment:%s%s}}" % (name, text)
+
+        # inline the attachment
+        if scheme == 'inline':
+            return '{{attachment:%s%s}}' % (name, text)
+        else: # 'attachment'
+            return '[[attachment:%s%s]]' % (name, text)
+    '''
+
+    def attachment(self, target_and_text, **kw):
+        """ This gets called on attachment URLs """
+        _ = self._
+        scheme, fname, text = wikiutil160a.split_wiki(target_and_text)
+        fn_txt = fname
+        if text:
+            fn_txt += '|' + text
+
+        if scheme == 'drawing':
+            return "{{drawing:%s}}" % fn_txt
+
+        # check for image, and possibly return IMG tag (images are always inlined)
+        if not kw.get('pretty_url', 0) and wikiutil.isPicture(fname):
+            return "{{attachment:%s}}" % fn_txt
+
+        # inline the attachment
+        if scheme == 'inline':
+            return '{{attachment:%s}}' % fn_txt
+
+        return '[[attachment:%s]]' % fn_txt
+
+    def _url_repl(self, word):
+        """Handle literal URLs including inline images."""
+        scheme = word.split(":", 1)[0]
+
+        if scheme == 'wiki':
+            return self.interwiki(word)
+        if scheme in self.attachment_schemas:
+            return '%s' % self.attachment(word)
+
+        if wikiutil.isPicture(word): # magic will go away in 1.6!
+            return '{{%s}}' % word # new markup for inline images
+        else:
+            return word
+
+
+    def _url_bracket_repl(self, word):
+        """Handle bracketed URLs."""
+        word = word[1:-1] # strip brackets
+
+        # Local extended link? [:page name:link text] XXX DEPRECATED
+        if word[0] == ':':
+            words = word[1:].split(':', 1)
+            pagename = self._replace(('PAGE', words[0]))
+            if len(words) == 1 or len(words) == 2 and not words[1]:
+                return '[[%s]]' % (pagename, )
+            else:
+                return '[[%s|%s]]' % (pagename, words[1])
+
+        scheme_and_rest = word.split(":", 1)
+        if len(scheme_and_rest) == 1: # no scheme
+            # Traditional split on space
+            words = word.split(None, 1)
+            if len(words) == 1:
+                words = words * 2
+
+            if words[0].startswith('#'): # anchor link
+                if words[0] == words[1]:
+                    return '[[%s]]' % words[0]
+                else:
+                    return '[[%s|%s]]' % tuple(words)
+        else:
+            scheme, rest = scheme_and_rest
+            if scheme == "wiki":
+                return self.interwiki(word, pretty_url=1)
+            if scheme in self.attachment_schemas:
+                return self.attachment(word)
+
+            words = word.split(None, 1)
+            if len(words) == 1:
+                words = words * 2
+
+        target, text = words
+        if wikiutil.isPicture(text) and re.match(self.url_rule, text):
+            return '[[%s|{{%s}}]]' % (target, text)
+        else:
+            if target == text:
+                return '[[%s]]' % target
+            else:
+                return '[[%s|%s]]' % (target, text)
+
+
+    '''
+    def _url_bracket_repl(self, word):
+        """Handle bracketed URLs."""
+        word = word[1:-1] # strip brackets
+
+        # Local extended link?
+        if word[0] == ':':
+            words = word[1:].split(':', 1)
+            link, text = (words + ['', ''])[:2]
+            if link.strip() == text.strip():
+                text = ''
+            link = self._replace_target(link)
+            if text:
+                text = '|' + text
+            return '[[%s%s]]' % (link, text)
+
+        # Traditional split on space
+        words = word.split(None, 1)
+        if words[0][0] == '#':
+            # anchor link
+            link, text = (words + ['', ''])[:2]
+            if link.strip() == text.strip():
+                text = ''
+            #link = self._replace_target(link)
+            if text:
+                text = '|' + text
+            return '[[%s%s]]' % (link, text)
+
+        scheme = words[0].split(":", 1)[0]
+        if scheme == "wiki":
+            return self.interwiki(words)
+            #scheme, wikiname, pagename, text = self.interwiki(word)
+            #print "%r %r %r %r" % (scheme, wikiname, pagename, text)
+            #if wikiname in ('Self', self.request.cfg.interwikiname, ''):
+            #    if text:
+            #        text = '|' + text
+            #    return '[[%s%s]]' % (pagename, text)
+            #else:
+            #    if text:
+            #        text = '|' + text
+            #    return "[[%s:%s%s]]" % (wikiname, pagename, text)
+        if scheme in self.attachment_schemas:
+            m = self.attachment(words)
+            if m.startswith('{{') and m.endswith('}}'):
+                # with url_bracket markup, 1.5.8 parser does not embed, but link!
+                m = '[[%s]]' % m[2:-2]
+            return m
+
+        target, desc = (words + ['', ''])[:2]
+        if wikiutil.isPicture(desc) and re.match(self.url_rule, desc):
+            #return '[[%s|{{%s|%s}}]]' % (words[0], words[1], words[0])
+            return '[[%s|{{%s}}]]' % (target, desc)
+        else:
+            if desc:
+                desc = '|' + desc
+            return '[[%s%s]]' % (target, desc)
+    '''
+
+    def _pre_repl(self, word):
+        w = word.strip()
+        if w == '{{{' and not self.in_pre:
+            self.in_pre = True
+        elif w == '}}}' and self.in_pre:
+            self.in_pre = False
+        return word
+
+    def _processor_repl(self, word):
+        self.in_pre = True
+        return word
+
+    def scan(self, scan_re, line):
+        """ Scans one line - append text before match, invoke replace() with match, and add text after match.  """
+        result = []
+        lastpos = 0
+
+        for match in scan_re.finditer(line):
+            # Add text before the match
+            if lastpos < match.start():
+                result.append(line[lastpos:match.start()])
+            # Replace match with markup
+            result.append(self.replace(match))
+            lastpos = match.end()
+
+        # Add remainder of the line
+        result.append(line[lastpos:])
+        return u''.join(result)
+
+
+    def replace(self, match):
+        """ Replace match using type name """
+        result = []
+        for _type, hit in match.groupdict().items():
+            if hit is not None and not _type in ["hmarker", ]:
+                # Get replace method and replace hit
+                replace = getattr(self, '_' + _type + '_repl')
+                # print _type, hit
+                result.append(replace(hit))
+                return ''.join(result)
+        else:
+            # We should never get here
+            import pprint
+            raise Exception("Can't handle match %r\n%s\n%s" % (
+                match,
+                pprint.pformat(match.groupdict()),
+                pprint.pformat(match.groups()),
+            ))
+
+        return ""
+
+    def convert(self, request):
+        """ For each line, scan through looking for magic
+            strings, outputting verbatim any intervening text.
+        """
+        self.request = request
+        # prepare regex patterns
+        rules = self.formatting_rules.replace('\n', '|')
+        if self.request.cfg.bang_meta:
+            rules = ur'(?P<notword>!%(word_rule)s)|%(rules)s' % {
+                'word_rule': self.word_rule,
+                'rules': rules,
+            }
+        pre_rules = r'''(?P<pre>\}\}\})'''
+        pre_scan_re = re.compile(pre_rules, re.UNICODE)
+        scan_re = re.compile(rules, re.UNICODE)
+        eol_re = re.compile(r'\r?\n', re.UNICODE)
+
+        rawtext = self.raw
+
+        # remove last item because it's guaranteed to be empty
+        self.lines = eol_re.split(rawtext)[:-1]
+        self.in_processing_instructions = True
+
+        # Main loop
+        for line in self.lines:
+            # ignore processing instructions
+            if self.in_processing_instructions:
+                found = False
+                for pi in ("##", "#format", "#refresh", "#redirect", "#deprecated",
+                           "#pragma", "#form", "#acl", "#language"):
+                    if line.lower().startswith(pi):
+                        self.request.write(line + '\r\n')
+                        found = True
+                        break
+                if not found:
+                    self.in_processing_instructions = False
+                else:
+                    continue # do not parse this line
+            if not line.strip():
+                self.request.write(line + '\r\n')
+            else:
+                # Scan line, format and write
+                scanning_re = self.in_pre and pre_scan_re or scan_re
+                formatted_line = self.scan(scanning_re, line)
+                self.request.write(formatted_line + '\r\n')
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/migration/_tests/test_conv160a_wiki.py	Fri Aug 22 22:58:18 2008 +0200
@@ -0,0 +1,161 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - tests of wiki content conversion
+
+    TODO:
+    * fix failing tests
+    * fix parser/converter anchor link handling
+    * emit a warning if we find some page name that was renamed as a macro argument?
+    * shall we support camelcase renaming?
+
+    Limitations of this converter:
+    * converter does not touch "pre sections", thus markup examples in {{{ }}}
+      or ` ` will have to get handled manually.
+    * converter does not touch macro arguments, they will have to get handled
+      manually
+    * converter does not touch CamelCase links (but there should be no need to do)
+
+    @copyright: 2007 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+import py
+#py.test.skip("broken")
+
+from MoinMoin.script.migration._conv160a_wiki import convert_wiki
+
+class TestWikiConversion:
+    """ test the wiki markup conversion 1.6.0a -> 1.6.0 """
+    def test_absolute(self):
+        request = self.request
+        pagename = 'TestPage'
+        rename_some_page = {
+                ('PAGE', 'some_page'): 'some page',
+        }
+        rename_some_file = {
+                ('FILE', pagename, 'with_underscore'): 'without underscore',
+                ('FILE', pagename, 'with blank'): 'without_blank',
+        }
+
+        tests = [
+            # 1.6.0a specific tests
+            ('["some page" somepage]', {}, '[[some page|somepage]]'),
+            ("['some page' somepage]", {}, '[[some page|somepage]]'),
+            ("MoinMaster:'some page'", {}, '[[MoinMaster:some page]]'),
+            ('MoinMaster:"some page"', {}, '[[MoinMaster:some page]]'),
+            #("MoinMaster:'some page'", {}, '[[MoinMaster:some page]]'),
+            # "nothing changed" checks (except markup)
+            ('', {}, ''),
+            ('CamelCase', {}, 'CamelCase'),
+            # XXX TODO ('MoinMaster:CamelCase', {}, 'MoinMaster:CamelCase'),
+
+            # did not work in 1.6a
+            #('[wiki:LinuxWiki: LinuxWiki.de]', {}, '[[LinuxWiki:|LinuxWiki.de]]'),
+            #('[wiki:/OtherPage]', rename_some_page, '[[/OtherPage]]'),
+            #('[wiki:/OtherPage other page]', rename_some_page, '[[/OtherPage|other page]]'),
+
+            # XXX TODO  ('[wiki:MoinMoin/FrontPage]', {}, 'MoinMoin:FrontPage'),
+            ('some_text', {}, 'some_text'),
+            ('["some_text"]', {}, '[[some_text]]'),
+            ('some_page', rename_some_page, 'some_page'), # not a link
+            ('{{{["some_page"]}}}', rename_some_page, '{{{["some_page"]}}}'), # not a link
+            ('`["some_page"]`', rename_some_page, '`["some_page"]`'), # not a link
+            ('["OtherPage/some_page"]', rename_some_page, '[[OtherPage/some_page]]'), # different link
+            # XXX TODO ('MoinMaster:some_page', rename_some_page, 'MoinMaster:some_page'), # external link
+            ('http://some_server/some_page', rename_some_page, 'http://some_server/some_page'), # external link
+            ('[http://some_server/some_page]', rename_some_page, '[[http://some_server/some_page]]'), # external link
+            ('[#some_page]', rename_some_page, '[[#some_page]]'), # link to anchor that has same name
+            #XXX ('[attachment:some_page.png]', rename_some_page, '[[attachment:some_page.png]]'), # att, not page
+            #XXX ('[attachment:some_page.png test picture]', rename_some_page, '[[attachment:some_page.png|test picture]]'), # att, not page
+            # url unquote stuff (%20 was popular for space)
+            #XXX ('attachment:My%20Attachment.jpg', {}, '{{attachment:My Attachment.jpg}}'), # embed!
+            #XXX ('[attachment:My%20Attachment.jpg]', {}, '[[attachment:My Attachment.jpg]]'), # link!
+            #XXX ('[attachment:My%20Attachment.jpg it works]', {}, '[[attachment:My Attachment.jpg|it works]]'),
+
+            # page rename changes result
+            ('["some_page"]', rename_some_page, '[[some page]]'),
+            ('[:some_page]', rename_some_page, '[[some page]]'),
+            ('[:some_page:]', rename_some_page, '[[some page]]'),
+            ('[:some_page:some text]', rename_some_page, '[[some page|some text]]'),
+            ('Self:some_page', rename_some_page, '[[some page]]'),
+            ('wiki:Self:some_page', rename_some_page, '[[some page]]'),
+            ('[wiki:Self:some_page some text]', rename_some_page, '[[some page|some text]]'),
+            ('wiki:Self:some_page#some_anchor', rename_some_page, '[[some page#some_anchor]]'),
+
+            # other markup changes we do
+            ('[:other page]', {}, '[[other page]]'),
+            ('[:other page:]', {}, '[[other page]]'),
+            ('[:other page:other text]', {}, '[[other page|other text]]'),
+            # XXX TODO ('Self:CamelCase', {}, 'CamelCase'),
+            # XXX TODO ('[wiki:WikiPedia:Lynx_%28web_browser%29 Lynx]', {}, '[[WikiPedia:Lynx_(web_browser)|Lynx]]'),
+            # XXX TODO ('[:Something:Something]', {}, '[[Something]]'), # optimize markup
+
+            # "nothing changed" checks
+            ('attachment:OtherPage/with_underscore', rename_some_file, '[[attachment:OtherPage/with_underscore]]'),
+
+            # file rename changes result
+            # XXX TODO ('attachment:with_underscore', rename_some_file, '[[attachment:without underscore]]'),
+            # XXX TODO ('attachment:TestPage/with_underscore', rename_some_file, '[[attachment:without underscore]]'), # remove superfluous pagename
+
+            # attachment syntax: kill %20
+            # XXX TODO ('attachment:with%20blank', rename_some_file, '[[attachment:without_blank]]'), # plus rename
+            # XXX TODO ('attachment:keep%20blank', rename_some_file, '[[attachment:keep blank]]'), # no rename
+            # XXX TODO ('attachment:TestPage/keep%20blank', rename_some_file, '[[attachment:keep blank]]'), # remove superfluous pagename
+            # XXX TODO ('attachment:OtherPage/keep%20blank', rename_some_file, '[[attachment:OtherPage/keep blank]]'),
+
+            # embed images
+            ('http://server/image.png', {}, '{{http://server/image.png}}'),
+            ('attachment:image.gif', {}, '{{attachment:image.gif}}'),
+            ('inline:image.jpg', {}, '{{attachment:image.jpg}}'), # inline is now implied by {{...}}
+            ('drawing:image', {}, '{{drawing:image}}'),
+
+            # macros
+            ('[[BR]]', {}, '<<BR>>'),
+            ('[[FullSearch(wtf)]]', {}, '<<FullSearch(wtf)>>'),
+            (u'[[ImageLink(töst.png)]]', {}, u'[[attachment:töst.png|{{attachment:töst.png}}]]'),
+            ('[[ImageLink(test.png,OtherPage)]]', {}, '[[OtherPage|{{attachment:test.png}}]]'),
+            ('[[ImageLink(test.png,OtherPage,width=123,height=456)]]', {}, '[[OtherPage|{{attachment:test.png||width=123, height=456}}]]'),
+            ('[[ImageLink(test.png,OtherPage,width=123,height=456,alt=alttext)]]', {}, '[[OtherPage|{{attachment:test.png|alttext|width=123, height=456}}]]'),
+            ('[[ImageLink(test.png,OtherPage,width=123,height=456,alt=alt text with blanks)]]', {}, '[[OtherPage|{{attachment:test.png|alt text with blanks|width=123, height=456}}]]'),
+            ('[[ImageLink(http://server/test.png,OtherPage,width=123,height=456)]]', {}, '[[OtherPage|{{http://server/test.png||width=123, height=456}}]]'),
+            ('[[ImageLink(http://server/test.png,http://server/,width=123)]]', {}, '[[http://server/|{{http://server/test.png||width=123}}]]'),
+            ('[[ImageLink(test.png,attachment:test.png)]]', {}, '[[attachment:test.png|{{attachment:test.png}}]]'),
+            ('[[ImageLink(test.png,inline:test.py)]]', {}, '[[attachment:test.py|{{attachment:test.png}}]]'),
+
+        ]
+        for data, renames, expected in tests:
+            assert convert_wiki(request, pagename, data, renames) == expected
+
+    def test_sisterpage(self):
+        request = self.request
+        top_page = 'toppage'
+        pagename = '%s/subpage' % top_page
+        rename_some_page = {
+                ('PAGE', '%s/sister' % top_page): '%s/renamed_sister' % top_page,
+        }
+        tests = [
+            # "nothing changed" checks
+            ('["../sister_norename"]', rename_some_page, '[[../sister_norename]]'),
+
+            # renames
+            ('["../sister"]', rename_some_page, '[[../renamed_sister]]'),
+        ]
+        for data, renames, expected in tests:
+            assert convert_wiki(request, pagename, data, renames) == expected
+
+    def test_subpage(self):
+        request = self.request
+        pagename = 'toppage'
+        rename_some_page = {
+                ('PAGE', '%s/subpage' % pagename): '%s/renamed_subpage' % pagename,
+        }
+        tests = [
+            # "nothing changed" checks
+            ('["/subpage_norename"]', rename_some_page, '[[/subpage_norename]]'),
+
+            # renames
+            ('["/subpage"]', rename_some_page, '[[/renamed_subpage]]'),
+        ]
+        for data, renames, expected in tests:
+            assert convert_wiki(request, pagename, data, renames) == expected
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/migration/text_moin160a_wiki.py	Fri Aug 22 22:58:18 2008 +0200
@@ -0,0 +1,1148 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - MoinMoin Wiki Markup Parser
+
+    @copyright: 2000, 2001, 2002 by Jürgen Hermann <jh@web.de>,
+                2006 by MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import re
+
+import wikiutil160a as wikiutil
+from MoinMoin import config, macro
+
+Dependencies = []
+
+class Parser:
+    """
+        Object that turns Wiki markup into HTML.
+
+        All formatting commands can be parsed one line at a time, though
+        some state is carried over between lines.
+
+        Methods named like _*_repl() are responsible to handle the named regex
+        patterns defined in print_html().
+    """
+
+    # allow caching
+    caching = 1
+    Dependencies = []
+
+    # some common strings
+    PARENT_PREFIX = wikiutil.PARENT_PREFIX
+    # quoted strings (we require that there is at least one char (that is not the quoting char)
+    # inside to not confuse stuff like '''Contact:''' (just a bold Contact:) with interwiki markup
+    # OtherWiki:'Page with blanks'
+    sq_string = ur"('[^']+?')" # single quoted string
+    dq_string = ur"(\"[^\"]+?\")" # double quoted string
+    q_string = ur"(%s|%s)" % (sq_string, dq_string) # quoted string
+    attachment_schemas = ["attachment", "inline", "drawing"]
+    punct_pattern = re.escape(u'''"\'}]|:,.)?!''')
+    punct_no_quote_pattern = re.escape(u'''}]|:,.)?!''')
+    url_pattern = (u'http|https|ftp|nntp|news|mailto|telnet|wiki|file|irc|' +
+            u'|'.join(attachment_schemas) +
+            (config.url_schemas and u'|' + u'|'.join(config.url_schemas) or ''))
+
+    # some common rules
+    word_rule = ur'(?:(?<![%(u)s%(l)s])|^)%(parent)s(?:%(subpages)s(?:[%(u)s][%(l)s]+){2,})+(?![%(u)s%(l)s]+)' % {
+        'u': config.chars_upper,
+        'l': config.chars_lower,
+        'subpages': wikiutil.CHILD_PREFIX + '?',
+        'parent': ur'(?:%s)?' % re.escape(PARENT_PREFIX),
+    }
+    url_rule = ur'%(url_guard)s(%(url)s)\:(([^\s\<%(punct)s]|([%(punctnq)s][^\s\<%(punct)s]))+|%(q_string)s)' % {
+        'url_guard': ur'(^|(?<!\w))',
+        'url': url_pattern,
+        'punct': punct_pattern,
+        'punctnq': punct_no_quote_pattern,
+        'q_string': q_string,
+    }
+
+    ol_rule = ur"^\s+(?:[0-9]+|[aAiI])\.(?:#\d+)?\s"
+    dl_rule = ur"^\s+.*?::\s"
+
+    # this is used inside <pre> / parser sections (we just want to know when it's over):
+    pre_formatting_rules = ur"""(?P<pre>(\}\}\}))"""
+
+    # the big, fat, ugly one ;)
+    formatting_rules = ur"""(?P<ent_numeric>&#(\d{1,5}|x[0-9a-fA-F]+);)
+(?:(?P<emph_ibb>'''''(?=[^']+'''))
+(?P<emph_ibi>'''''(?=[^']+''))
+(?P<emph_ib_or_bi>'{5}(?=[^']))
+(?P<emph>'{2,3})
+(?P<u>__)
+(?P<sup>\^.*?\^)
+(?P<sub>,,[^,]{1,40},,)
+(?P<tt>\{\{\{.*?\}\}\})
+(?P<parser>(\{\{\{(#!.*|\s*$)))
+(?P<pre>(\{\{\{ ?|\}\}\}))
+(?P<small>(\~- ?|-\~))
+(?P<big>(\~\+ ?|\+\~))
+(?P<strike>(--\(|\)--))
+(?P<remark>(/\* ?| ?\*/))
+(?P<rule>-{4,})
+(?P<comment>^\#\#.*$)
+(?P<macro>\[\[(%%(macronames)s)(?:\(.*?\))?\]\]))
+(?P<ol>%(ol_rule)s)
+(?P<dl>%(dl_rule)s)
+(?P<li>^\s+\*\s*)
+(?P<li_none>^\s+\.\s*)
+(?P<indent>^\s+)
+(?P<tableZ>\|\| $)
+(?P<table>(?:\|\|)+(?:<[^>]*?>)?(?!\|? $))
+(?P<heading>^\s*(?P<hmarker>=+)\s.*\s(?P=hmarker) $)
+(?P<interwiki>[A-Z][a-zA-Z]+\:(%(q_string)s|([^\s'\"\:\<\|]([^\s%(punct)s]|([%(punct)s][^\s%(punct)s]))+)))
+(?P<word>%(word_rule)s)
+(?P<url_bracket>\[((%(url)s)\:|#|\:)[^\s\]]+(\s[^\]]+)?\])
+(?P<url>%(url_rule)s)
+(?P<email>[-\w._+]+\@[\w-]+(\.[\w-]+)+)
+(?P<smiley>(?<=\s)(%(smiley)s)(?=\s))
+(?P<smileyA>^(%(smiley)s)(?=\s))
+(?P<ent_symbolic>&[a-zA-Z]+;)
+(?P<ent>[<>&])
+(?P<wikiname_bracket>\[%(q_string)s.*?\])
+(?P<tt_bt>`.*?`)"""  % {
+
+        'url': url_pattern,
+        'punct': punct_pattern,
+        'q_string': q_string,
+        'ol_rule': ol_rule,
+        'dl_rule': dl_rule,
+        'url_rule': url_rule,
+        'word_rule': word_rule,
+        'smiley': u'|'.join(map(re.escape, config.smileys))}
+
+    # Don't start p before these
+    no_new_p_before = ("heading rule table tableZ tr td "
+                       "ul ol dl dt dd li li_none indent "
+                       "macro parser pre")
+    no_new_p_before = no_new_p_before.split()
+    no_new_p_before = dict(zip(no_new_p_before, [1] * len(no_new_p_before)))
+
+    def __init__(self, raw, request, **kw):
+        self.raw = raw
+        self.request = request
+        self.form = request.form # Macro object uses this
+        self._ = request.getText
+        self.cfg = request.cfg
+        self.line_anchors = kw.get('line_anchors', True)
+        self.macro = None
+        self.start_line = kw.get('start_line', 0)
+
+        # currently, there is only a single, optional argument to this parser and
+        # (when given), it is used as class(es) for a div wrapping the formatter output
+        # either use a single class like "comment" or multiple like "comment/red/dotted"
+        self.wrapping_div_class = kw.get('format_args', '').strip().replace('/', ' ')
+
+        self.is_em = 0 # must be int
+        self.is_b = 0 # must be int
+        self.is_u = False
+        self.is_strike = False
+        self.is_big = False
+        self.is_small = False
+        self.is_remark = False
+
+        self.lineno = 0
+        self.in_list = 0 # between <ul/ol/dl> and </ul/ol/dl>
+        self.in_li = 0 # between <li> and </li>
+        self.in_dd = 0 # between <dd> and </dd>
+
+        # states of the parser concerning being inside/outside of some "pre" section:
+        # None == we are not in any kind of pre section (was: 0)
+        # 'search_parser' == we didn't get a parser yet, still searching for it (was: 1)
+        # 'found_parser' == we found a valid parser (was: 2)
+        # 'no_parser' == we have no (valid) parser, use a normal <pre>...</pre> (was: 3)
+        self.in_pre = None
+
+        self.in_table = 0
+        self.inhibit_p = 0 # if set, do not auto-create a <p>aragraph
+        self.titles = request._page_headings
+
+        # holds the nesting level (in chars) of open lists
+        self.list_indents = []
+        self.list_types = []
+
+        self.formatting_rules = self.formatting_rules % {'macronames': u'|'.join(macro.getNames(self.cfg))}
+
+    def _close_item(self, result):
+        #result.append("<!-- close item begin -->\n")
+        if self.in_table:
+            result.append(self.formatter.table(0))
+            self.in_table = 0
+        if self.in_li:
+            self.in_li = 0
+            if self.formatter.in_p:
+                result.append(self.formatter.paragraph(0))
+            result.append(self.formatter.listitem(0))
+        if self.in_dd:
+            self.in_dd = 0
+            if self.formatter.in_p:
+                result.append(self.formatter.paragraph(0))
+            result.append(self.formatter.definition_desc(0))
+        #result.append("<!-- close item end -->\n")
+
+
+    def interwiki(self, target_and_text, **kw):
+        # TODO: maybe support [wiki:Page http://wherever/image.png] ?
+        scheme, rest = target_and_text.split(':', 1)
+        wikiname, pagename, text = wikiutil.split_wiki(rest)
+        if not pagename:
+            pagename = self.formatter.page.page_name
+        if not text:
+            text = pagename
+        #self.request.log("interwiki: split_wiki -> %s.%s.%s" % (wikiname,pagename,text))
+
+        if wikiname.lower() == 'self': # [wiki:Self:LocalPage text] or [:LocalPage:text]
+            return self._word_repl(pagename, text)
+
+        # check for image URL, and possibly return IMG tag
+        if not kw.get('pretty_url', 0) and wikiutil.isPicture(pagename):
+            dummy, wikiurl, dummy, wikitag_bad = wikiutil.resolve_wiki(self.request, rest)
+            href = wikiutil.join_wiki(wikiurl, pagename)
+            #self.request.log("interwiki: join_wiki -> %s.%s.%s" % (wikiurl,pagename,href))
+            return self.formatter.image(src=href)
+
+        return (self.formatter.interwikilink(1, wikiname, pagename) +
+                self.formatter.text(text) +
+                self.formatter.interwikilink(0, wikiname, pagename))
+
+    def attachment(self, target_and_text, **kw):
+        """ This gets called on attachment URLs """
+        _ = self._
+        #self.request.log("attachment: target_and_text %s" % target_and_text)
+        scheme, fname, text = wikiutil.split_wiki(target_and_text)
+        if not text:
+            text = fname
+
+        if scheme == 'drawing':
+            return self.formatter.attachment_drawing(fname, text)
+
+        # check for image, and possibly return IMG tag (images are always inlined)
+        if not kw.get('pretty_url', 0) and wikiutil.isPicture(fname):
+            return self.formatter.attachment_image(fname)
+
+        # inline the attachment
+        if scheme == 'inline':
+            return self.formatter.attachment_inlined(fname, text)
+
+        return self.formatter.attachment_link(fname, text)
+
+    def _u_repl(self, word):
+        """Handle underline."""
+        self.is_u = not self.is_u
+        return self.formatter.underline(self.is_u)
+
+    def _strike_repl(self, word):
+        """Handle strikethrough."""
+        # XXX we don't really enforce the correct sequence --( ... )-- here
+        self.is_strike = not self.is_strike
+        return self.formatter.strike(self.is_strike)
+
+    def _remark_repl(self, word):
+        """Handle remarks."""
+        # XXX we don't really enforce the correct sequence /* ... */ here
+        self.is_remark = not self.is_remark
+        span_kw = {
+            'style': self.request.user.show_comments and "display:''" or "display:none",
+            'class': "comment",
+        }
+        return self.formatter.span(self.is_remark, **span_kw)
+
+    def _small_repl(self, word):
+        """Handle small."""
+        if word.strip() == '~-' and self.is_small:
+            return self.formatter.text(word)
+        if word.strip() == '-~' and not self.is_small:
+            return self.formatter.text(word)
+        self.is_small = not self.is_small
+        return self.formatter.small(self.is_small)
+
+    def _big_repl(self, word):
+        """Handle big."""
+        if word.strip() == '~+' and self.is_big:
+            return self.formatter.text(word)
+        if word.strip() == '+~' and not self.is_big:
+            return self.formatter.text(word)
+        self.is_big = not self.is_big
+        return self.formatter.big(self.is_big)
+
+    def _emph_repl(self, word):
+        """Handle emphasis, i.e. '' and '''."""
+        ##print "#", self.is_b, self.is_em, "#"
+        if len(word) == 3:
+            self.is_b = not self.is_b
+            if self.is_em and self.is_b:
+                self.is_b = 2
+            return self.formatter.strong(self.is_b)
+        else:
+            self.is_em = not self.is_em
+            if self.is_em and self.is_b:
+                self.is_em = 2
+            return self.formatter.emphasis(self.is_em)
+
+    def _emph_ibb_repl(self, word):
+        """Handle mixed emphasis, i.e. ''''' followed by '''."""
+        self.is_b = not self.is_b
+        self.is_em = not self.is_em
+        if self.is_em and self.is_b:
+            self.is_b = 2
+        return self.formatter.emphasis(self.is_em) + self.formatter.strong(self.is_b)
+
+    def _emph_ibi_repl(self, word):
+        """Handle mixed emphasis, i.e. ''''' followed by ''."""
+        self.is_b = not self.is_b
+        self.is_em = not self.is_em
+        if self.is_em and self.is_b:
+            self.is_em = 2
+        return self.formatter.strong(self.is_b) + self.formatter.emphasis(self.is_em)
+
+    def _emph_ib_or_bi_repl(self, word):
+        """Handle mixed emphasis, exactly five '''''."""
+        ##print "*", self.is_b, self.is_em, "*"
+        b_before_em = self.is_b > self.is_em > 0
+        self.is_b = not self.is_b
+        self.is_em = not self.is_em
+        if b_before_em:
+            return self.formatter.strong(self.is_b) + self.formatter.emphasis(self.is_em)
+        else:
+            return self.formatter.emphasis(self.is_em) + self.formatter.strong(self.is_b)
+
+
+    def _sup_repl(self, word):
+        """Handle superscript."""
+        return self.formatter.sup(1) + \
+            self.formatter.text(word[1:-1]) + \
+            self.formatter.sup(0)
+
+    def _sub_repl(self, word):
+        """Handle subscript."""
+        return self.formatter.sub(1) + \
+            self.formatter.text(word[2:-2]) + \
+            self.formatter.sub(0)
+
+
+    def _rule_repl(self, word):
+        """Handle sequences of dashes."""
+        result = self._undent() + self._closeP()
+        if len(word) <= 4:
+            result = result + self.formatter.rule()
+        else:
+            # Create variable rule size 1 - 6. Actual size defined in css.
+            size = min(len(word), 10) - 4
+            result = result + self.formatter.rule(size)
+        return result
+
+
+    def _word_repl(self, word, text=None):
+        """Handle WikiNames."""
+
+        # check for parent links
+        # !!! should use wikiutil.AbsPageName here, but setting `text`
+        # correctly prevents us from doing this for now
+        if word.startswith(wikiutil.PARENT_PREFIX):
+            if not text:
+                text = word
+            word = '/'.join(filter(None, self.formatter.page.page_name.split('/')[:-1] + [word[wikiutil.PARENT_PREFIX_LEN:]]))
+
+        if not text:
+            # if a simple, self-referencing link, emit it as plain text
+            if word == self.formatter.page.page_name:
+                return self.formatter.text(word)
+            text = word
+        if word.startswith(wikiutil.CHILD_PREFIX):
+            word = self.formatter.page.page_name + '/' + word[wikiutil.CHILD_PREFIX_LEN:]
+
+        # handle anchors
+        parts = word.split("#", 1)
+        anchor = ""
+        if len(parts) == 2:
+            word, anchor = parts
+
+        return (self.formatter.pagelink(1, word, anchor=anchor) +
+                self.formatter.text(text) +
+                self.formatter.pagelink(0, word))
+
+    def _notword_repl(self, word):
+        """Handle !NotWikiNames."""
+        return self.formatter.nowikiword(word[1:])
+
+    def _interwiki_repl(self, word):
+        """Handle InterWiki links."""
+        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, word)
+        if wikitag_bad:
+            return self.formatter.text(word)
+        else:
+            return self.interwiki("wiki:" + word)
+
+    def _url_repl(self, word):
+        """Handle literal URLs including inline images."""
+        scheme = word.split(":", 1)[0]
+
+        if scheme == "wiki":
+            return self.interwiki(word)
+
+        if scheme in self.attachment_schemas:
+            return self.attachment(word)
+
+        if wikiutil.isPicture(word):
+            word = wikiutil.mapURL(self.request, word)
+            # Get image name http://here.com/dir/image.gif -> image
+            name = word.split('/')[-1]
+            name = ''.join(name.split('.')[:-1])
+            return self.formatter.image(src=word, alt=name)
+        else:
+            return (self.formatter.url(1, word, css=scheme) +
+                    self.formatter.text(word) +
+                    self.formatter.url(0))
+
+
+    def _wikiname_bracket_repl(self, text):
+        """Handle special-char wikinames with link text, like:
+           ["Jim O'Brian" Jim's home page] or ['Hello "world"!' a page with doublequotes]i
+        """
+        word = text[1:-1] # strip brackets
+        first_char = word[0]
+        if first_char in wikiutil.QUOTE_CHARS:
+            # split on closing quote
+            target, linktext = word[1:].split(first_char, 1)
+        else: # not quoted
+            # split on whitespace
+            target, linktext = word.split(None, 1)
+        if target:
+            linktext = linktext.strip()
+            return self._word_repl(target, linktext)
+        else:
+            return self.formatter.text(text)
+
+
+    def _url_bracket_repl(self, word):
+        """Handle bracketed URLs."""
+        word = word[1:-1] # strip brackets
+
+        # Local extended link? [:page name:link text] XXX DEPRECATED
+        if word[0] == ':':
+            words = word[1:].split(':', 1)
+            if len(words) == 1:
+                words = words * 2
+            target_and_text = 'wiki:Self:%s %s' % (wikiutil.quoteName(words[0]), words[1])
+            return self.interwiki(target_and_text, pretty_url=1)
+
+        scheme_and_rest = word.split(":", 1)
+        if len(scheme_and_rest) == 1: # no scheme
+            # Traditional split on space
+            words = word.split(None, 1)
+            if len(words) == 1:
+                words = words * 2
+
+            if words[0].startswith('#'): # anchor link
+                return (self.formatter.url(1, words[0]) +
+                        self.formatter.text(words[1]) +
+                        self.formatter.url(0))
+        else:
+            scheme, rest = scheme_and_rest
+            if scheme == "wiki":
+                return self.interwiki(word, pretty_url=1)
+            if scheme in self.attachment_schemas:
+                return self.attachment(word, pretty_url=1)
+
+            words = word.split(None, 1)
+            if len(words) == 1:
+                words = words * 2
+
+        if wikiutil.isPicture(words[1]) and re.match(self.url_rule, words[1]):
+            return (self.formatter.url(1, words[0], css='external', do_escape=0) +
+                    self.formatter.image(title=words[0], alt=words[0], src=words[1]) +
+                    self.formatter.url(0))
+        else:
+            return (self.formatter.url(1, words[0], css=scheme, do_escape=0) +
+                    self.formatter.text(words[1]) +
+                    self.formatter.url(0))
+
+
+    def _email_repl(self, word):
+        """Handle email addresses (without a leading mailto:)."""
+        return (self.formatter.url(1, "mailto:" + word, css='mailto') +
+                self.formatter.text(word) +
+                self.formatter.url(0))
+
+
+    def _ent_repl(self, word):
+        """Handle SGML entities."""
+        return self.formatter.text(word)
+        #return {'&': '&amp;',
+        #        '<': '&lt;',
+        #        '>': '&gt;'}[word]
+
+    def _ent_numeric_repl(self, word):
+        """Handle numeric (decimal and hexadecimal) SGML entities."""
+        return self.formatter.rawHTML(word)
+
+    def _ent_symbolic_repl(self, word):
+        """Handle symbolic SGML entities."""
+        return self.formatter.rawHTML(word)
+
+    def _indent_repl(self, match):
+        """Handle pure indentation (no - * 1. markup)."""
+        result = []
+        if not (self.in_li or self.in_dd):
+            self._close_item(result)
+            self.in_li = 1
+            css_class = None
+            if self.line_was_empty and not self.first_list_item:
+                css_class = 'gap'
+            result.append(self.formatter.listitem(1, css_class=css_class, style="list-style-type:none"))
+        return ''.join(result)
+
+    def _li_none_repl(self, match):
+        """Handle type=none (" .") lists."""
+        result = []
+        self._close_item(result)
+        self.in_li = 1
+        css_class = None
+        if self.line_was_empty and not self.first_list_item:
+            css_class = 'gap'
+        result.append(self.formatter.listitem(1, css_class=css_class, style="list-style-type:none"))
+        return ''.join(result)
+
+    def _li_repl(self, match):
+        """Handle bullet (" *") lists."""
+        result = []
+        self._close_item(result)
+        self.in_li = 1
+        css_class = None
+        if self.line_was_empty and not self.first_list_item:
+            css_class = 'gap'
+        result.append(self.formatter.listitem(1, css_class=css_class))
+        return ''.join(result)
+
+    def _ol_repl(self, match):
+        """Handle numbered lists."""
+        return self._li_repl(match)
+
+    def _dl_repl(self, match):
+        """Handle definition lists."""
+        result = []
+        self._close_item(result)
+        self.in_dd = 1
+        result.extend([
+            self.formatter.definition_term(1),
+            self.formatter.text(match[1:-3].lstrip(' ')),
+            self.formatter.definition_term(0),
+            self.formatter.definition_desc(1),
+        ])
+        return ''.join(result)
+
+
+    def _indent_level(self):
+        """Return current char-wise indent level."""
+        return len(self.list_indents) and self.list_indents[-1]
+
+
+    def _indent_to(self, new_level, list_type, numtype, numstart):
+        """Close and open lists."""
+        openlist = []   # don't make one out of these two statements!
+        closelist = []
+
+        if self._indent_level() != new_level and self.in_table:
+            closelist.append(self.formatter.table(0))
+            self.in_table = 0
+
+        while self._indent_level() > new_level:
+            self._close_item(closelist)
+            if self.list_types[-1] == 'ol':
+                tag = self.formatter.number_list(0)
+            elif self.list_types[-1] == 'dl':
+                tag = self.formatter.definition_list(0)
+            else:
+                tag = self.formatter.bullet_list(0)
+            closelist.append(tag)
+
+            del self.list_indents[-1]
+            del self.list_types[-1]
+
+            if self.list_types: # we are still in a list
+                if self.list_types[-1] == 'dl':
+                    self.in_dd = 1
+                else:
+                    self.in_li = 1
+
+        # Open new list, if necessary
+        if self._indent_level() < new_level:
+            self.list_indents.append(new_level)
+            self.list_types.append(list_type)
+
+            if self.formatter.in_p:
+                closelist.append(self.formatter.paragraph(0))
+
+            if list_type == 'ol':
+                tag = self.formatter.number_list(1, numtype, numstart)
+            elif list_type == 'dl':
+                tag = self.formatter.definition_list(1)
+            else:
+                tag = self.formatter.bullet_list(1)
+            openlist.append(tag)
+
+            self.first_list_item = 1
+            self.in_li = 0
+            self.in_dd = 0
+
+        # If list level changes, close an open table
+        if self.in_table and (openlist or closelist):
+            closelist[0:0] = [self.formatter.table(0)]
+            self.in_table = 0
+
+        self.in_list = self.list_types != []
+        return ''.join(closelist) + ''.join(openlist)
+
+
+    def _undent(self):
+        """Close all open lists."""
+        result = []
+        #result.append("<!-- _undent start -->\n")
+        self._close_item(result)
+        for type in self.list_types[::-1]:
+            if type == 'ol':
+                result.append(self.formatter.number_list(0))
+            elif type == 'dl':
+                result.append(self.formatter.definition_list(0))
+            else:
+                result.append(self.formatter.bullet_list(0))
+        #result.append("<!-- _undent end -->\n")
+        self.list_indents = []
+        self.list_types = []
+        return ''.join(result)
+
+
+    def _tt_repl(self, word):
+        """Handle inline code."""
+        return self.formatter.code(1) + \
+            self.formatter.text(word[3:-3]) + \
+            self.formatter.code(0)
+
+
+    def _tt_bt_repl(self, word):
+        """Handle backticked inline code."""
+        # if len(word) == 2: return "" // removed for FCK editor
+        return self.formatter.code(1, css="backtick") + \
+            self.formatter.text(word[1:-1]) + \
+            self.formatter.code(0)
+
+
+    def _getTableAttrs(self, attrdef):
+        # skip "|" and initial "<"
+        while attrdef and attrdef[0] == "|":
+            attrdef = attrdef[1:]
+        if not attrdef or attrdef[0] != "<":
+            return {}, ''
+        attrdef = attrdef[1:]
+
+        # extension for special table markup
+        def table_extension(key, parser, attrs, wiki_parser=self):
+            """ returns: tuple (found_flag, msg)
+                found_flag: whether we found something and were able to process it here
+                  true for special stuff like 100% or - or #AABBCC
+                  false for style xxx="yyy" attributes
+                msg: "" or an error msg
+            """
+            _ = wiki_parser._
+            found = False
+            msg = ''
+            if key[0] in "0123456789":
+                token = parser.get_token()
+                if token != '%':
+                    wanted = '%'
+                    msg = _('Expected "%(wanted)s" after "%(key)s", got "%(token)s"') % {
+                        'wanted': wanted, 'key': key, 'token': token}
+                else:
+                    try:
+                        dummy = int(key)
+                    except ValueError:
+                        msg = _('Expected an integer "%(key)s" before "%(token)s"') % {
+                            'key': key, 'token': token}
+                    else:
+                        found = True
+                        attrs['width'] = '"%s%%"' % key
+            elif key == '-':
+                arg = parser.get_token()
+                try:
+                    dummy = int(arg)
+                except ValueError:
+                    msg = _('Expected an integer "%(arg)s" after "%(key)s"') % {
+                        'arg': arg, 'key': key}
+                else:
+                    found = True
+                    attrs['colspan'] = '"%s"' % arg
+            elif key == '|':
+                arg = parser.get_token()
+                try:
+                    dummy = int(arg)
+                except ValueError:
+                    msg = _('Expected an integer "%(arg)s" after "%(key)s"') % {
+                        'arg': arg, 'key': key}
+                else:
+                    found = True
+                    attrs['rowspan'] = '"%s"' % arg
+            elif key == '(':
+                found = True
+                attrs['align'] = '"left"'
+            elif key == ':':
+                found = True
+                attrs['align'] = '"center"'
+            elif key == ')':
+                found = True
+                attrs['align'] = '"right"'
+            elif key == '^':
+                found = True
+                attrs['valign'] = '"top"'
+            elif key == 'v':
+                found = True
+                attrs['valign'] = '"bottom"'
+            elif key == '#':
+                arg = parser.get_token()
+                try:
+                    if len(arg) != 6: raise ValueError
+                    dummy = int(arg, 16)
+                except ValueError:
+                    msg = _('Expected a color value "%(arg)s" after "%(key)s"') % {
+                        'arg': arg, 'key': key}
+                else:
+                    found = True
+                    attrs['bgcolor'] = '"#%s"' % arg
+            return found, self.formatter.rawHTML(msg)
+
+        # scan attributes
+        attr, msg = wikiutil.parseAttributes(self.request, attrdef, '>', table_extension)
+        if msg:
+            msg = '<strong class="highlight">%s</strong>' % msg
+        #self.request.log("parseAttributes returned %r" % attr)
+        return attr, msg
+
+    def _tableZ_repl(self, word):
+        """Handle table row end."""
+        if self.in_table:
+            result = ''
+            # REMOVED: check for self.in_li, p should always close
+            if self.formatter.in_p:
+                result = self.formatter.paragraph(0)
+            result += self.formatter.table_cell(0) + self.formatter.table_row(0)
+            return result
+        else:
+            return self.formatter.text(word)
+
+    def _table_repl(self, word):
+        """Handle table cell separator."""
+        if self.in_table:
+            result = []
+            # check for attributes
+            attrs, attrerr = self._getTableAttrs(word)
+
+            # start the table row?
+            if self.table_rowstart:
+                self.table_rowstart = 0
+                result.append(self.formatter.table_row(1, attrs))
+            else:
+                # Close table cell, first closing open p
+                # REMOVED check for self.in_li, paragraph should close always!
+                if self.formatter.in_p:
+                    result.append(self.formatter.paragraph(0))
+                result.append(self.formatter.table_cell(0))
+
+            # check for adjacent cell markers
+            if word.count("|") > 2:
+                if not attrs.has_key('align') and \
+                   not (attrs.has_key('style') and 'text-align' in attrs['style'].lower()):
+                    # add center alignment if we don't have some alignment already
+                    attrs['align'] = '"center"'
+                if not attrs.has_key('colspan'):
+                    attrs['colspan'] = '"%d"' % (word.count("|")/2)
+
+            # return the complete cell markup
+            result.append(self.formatter.table_cell(1, attrs) + attrerr)
+            result.append(self._line_anchordef())
+            return ''.join(result)
+        else:
+            return self.formatter.text(word)
+
+
+    def _heading_repl(self, word):
+        """Handle section headings."""
+        import sha
+
+        h = word.strip()
+        level = 1
+        while h[level:level+1] == '=':
+            level += 1
+        depth = min(5, level)
+
+        # FIXME: needed for Included pages but might still result in unpredictable results
+        # when included the same page multiple times
+        title_text = h[level:-level].strip()
+        pntt = self.formatter.page.page_name + title_text
+        self.titles.setdefault(pntt, 0)
+        self.titles[pntt] += 1
+
+        unique_id = ''
+        if self.titles[pntt] > 1:
+            unique_id = '-%d' % self.titles[pntt]
+        result = self._closeP()
+        result += self.formatter.heading(1, depth, id="head-"+sha.new(pntt.encode(config.charset)).hexdigest()+unique_id)
+
+        return (result + self.formatter.text(title_text) +
+                self.formatter.heading(0, depth))
+
+    def _parser_repl(self, word):
+        """Handle parsed code displays."""
+        if word.startswith('{{{'):
+            word = word[3:]
+
+        self.parser = None
+        self.parser_name = None
+        s_word = word.strip()
+        if s_word == '#!':
+            # empty bang paths lead to a normal code display
+            # can be used to escape real, non-empty bang paths
+            word = ''
+            self.in_pre = 'no_parser'
+            return self._closeP() + self.formatter.preformatted(1)
+        elif s_word.startswith('#!'):
+            # First try to find a parser for this
+            parser_name = s_word[2:].split()[0]
+            self.setParser(parser_name)
+
+        if self.parser:
+            self.parser_name = parser_name
+            self.in_pre = 'found_parser'
+            self.parser_lines = [word]
+            return ''
+        elif s_word:
+            self.in_pre = 'no_parser'
+            return self._closeP() + self.formatter.preformatted(1) + \
+                   self.formatter.text(s_word + ' (-)')
+        else:
+            self.in_pre = 'search_parser'
+            return ''
+
+    def _pre_repl(self, word):
+        """Handle code displays."""
+        word = word.strip()
+        if word == '{{{' and not self.in_pre:
+            self.in_pre = 'no_parser'
+            return self._closeP() + self.formatter.preformatted(1)
+        elif word == '}}}' and self.in_pre:
+            self.in_pre = None
+            self.inhibit_p = 0
+            return self.formatter.preformatted(0)
+        return self.formatter.text(word)
+
+
+    def _smiley_repl(self, word):
+        """Handle smileys."""
+        return self.formatter.smiley(word)
+
+    _smileyA_repl = _smiley_repl
+
+
+    def _comment_repl(self, word):
+        # if we are in a paragraph, we must close it so that normal text following
+        # in the line below the comment will reopen a new paragraph.
+        if self.formatter.in_p:
+            self.formatter.paragraph(0)
+        self.line_is_empty = 1 # markup following comment lines treats them as if they were empty
+        return self.formatter.comment(word)
+
+    def _closeP(self):
+        if self.formatter.in_p:
+            return self.formatter.paragraph(0)
+        return ''
+
+    def _macro_repl(self, word):
+        """Handle macros ([[macroname]])."""
+        macro_name = word[2:-2]
+        self.inhibit_p = 0 # 1 fixes UserPreferences, 0 fixes paragraph formatting for macros
+
+        # check for arguments
+        args = None
+        if macro_name.count("("):
+            macro_name, args = macro_name.split('(', 1)
+            args = args[:-1]
+
+        # create macro instance
+        if self.macro is None:
+            self.macro = macro.Macro(self)
+        return self.formatter.macro(self.macro, macro_name, args)
+
+    def scan(self, scan_re, line, inhibit_p=False):
+        """ Scans one line
+        Append text before match, invoke replace() with match, and add text after match.
+        """
+        result = []
+        lastpos = 0
+
+        ###result.append(u'<span class="info">[scan: <tt>"%s"</tt>]</span>' % line)
+
+        for match in scan_re.finditer(line):
+            # Add text before the match
+            if lastpos < match.start():
+
+                ###result.append(u'<span class="info">[add text before match: <tt>"%s"</tt>]</span>' % line[lastpos:match.start()])
+
+                if not (inhibit_p or self.inhibit_p or self.in_pre or self.formatter.in_p):
+                    result.append(self.formatter.paragraph(1, css_class="line862"))
+                result.append(self.formatter.text(line[lastpos:match.start()]))
+
+            # Replace match with markup
+            if not (inhibit_p or self.inhibit_p or self.in_pre or self.formatter.in_p or
+                    self.in_table or self.in_list):
+                result.append(self.formatter.paragraph(1, css_class="line867"))
+            result.append(self.replace(match, inhibit_p))
+            lastpos = match.end()
+
+        ###result.append('<span class="info">[no match, add rest: <tt>"%s"<tt>]</span>' % line[lastpos:])
+
+        # Add paragraph with the remainder of the line
+        if not (inhibit_p or self.in_pre or self.in_li or self.in_dd or self.inhibit_p or
+                self.formatter.in_p) and lastpos < len(line):
+            result.append(self.formatter.paragraph(1, css_class="line874"))
+        result.append(self.formatter.text(line[lastpos:]))
+        return u''.join(result)
+
+    def replace(self, match, inhibit_p=False):
+        """ Replace match using type name """
+        result = []
+        for type, hit in match.groupdict().items():
+            if hit is not None and not type in ["hmarker", ]:
+
+                ##result.append(u'<span class="info">[replace: %s: "%s"]</span>' % (type, hit))
+                # Open p for certain types
+                if not (inhibit_p or self.inhibit_p or self.formatter.in_p
+                        or self.in_pre or (type in self.no_new_p_before)):
+                    result.append(self.formatter.paragraph(1, css_class="line891"))
+
+                # Get replace method and replace hit
+                replace = getattr(self, '_' + type + '_repl')
+                result.append(replace(hit))
+                return ''.join(result)
+        else:
+            # We should never get here
+            import pprint
+            raise Exception("Can't handle match " + `match`
+                + "\n" + pprint.pformat(match.groupdict())
+                + "\n" + pprint.pformat(match.groups()) )
+
+        return ""
+
+    def _line_anchordef(self):
+        if self.line_anchors and not self.line_anchor_printed:
+            self.line_anchor_printed = 1
+            return self.formatter.line_anchordef(self.lineno)
+        else:
+            return ''
+
+    def format(self, formatter, inhibit_p=False):
+        """ For each line, scan through looking for magic
+            strings, outputting verbatim any intervening text.
+        """
+        self.formatter = formatter
+        self.hilite_re = self.formatter.page.hilite_re
+
+        # prepare regex patterns
+        rules = self.formatting_rules.replace('\n', '|')
+        if self.cfg.bang_meta:
+            rules = ur'(?P<notword>!%(word_rule)s)|%(rules)s' % {
+                'word_rule': self.word_rule,
+                'rules': rules,
+            }
+        pre_rules = self.pre_formatting_rules.replace('\n', '|')
+        self.request.clock.start('compile_huge_and_ugly')
+        scan_re = re.compile(rules, re.UNICODE)
+        pre_scan_re = re.compile(pre_rules, re.UNICODE)
+        number_re = re.compile(self.ol_rule, re.UNICODE)
+        term_re = re.compile(self.dl_rule, re.UNICODE)
+        indent_re = re.compile(ur"^\s*", re.UNICODE)
+        eol_re = re.compile(r'\r?\n', re.UNICODE)
+        self.request.clock.stop('compile_huge_and_ugly')
+
+        # get text and replace TABs
+        rawtext = self.raw.expandtabs()
+
+        # go through the lines
+        self.lineno = self.start_line
+        self.lines = eol_re.split(rawtext)
+        self.line_is_empty = 0
+
+        self.in_processing_instructions = 1
+
+        if self.wrapping_div_class:
+            div_kw = {'css_class': self.wrapping_div_class, }
+            if 'comment' in self.wrapping_div_class.split():
+                # show comment divs depending on user profile (and wiki configuration)
+                div_kw['style'] = self.request.user.show_comments and "display:''" or "display:none"
+            self.request.write(self.formatter.div(1, **div_kw))
+
+        # Main loop
+        for line in self.lines:
+            self.lineno += 1
+            self.line_anchor_printed = 0
+            if not self.in_table:
+                self.request.write(self._line_anchordef())
+            self.table_rowstart = 1
+            self.line_was_empty = self.line_is_empty
+            self.line_is_empty = 0
+            self.first_list_item = 0
+            self.inhibit_p = 0
+
+            # ignore processing instructions
+            if self.in_processing_instructions:
+                found = False
+                for pi in ("##", "#format", "#refresh", "#redirect", "#deprecated",
+                           "#pragma", "#form", "#acl", "#language"):
+                    if line.lower().startswith(pi):
+                        self.request.write(self.formatter.comment(line))
+                        found = True
+                        break
+                if not found:
+                    self.in_processing_instructions = 0
+                else:
+                    continue # do not parse this line
+            if self.in_pre:
+                # TODO: move this into function
+                # still looking for processing instructions
+                if self.in_pre == 'search_parser':
+                    self.parser = None
+                    parser_name = ''
+                    if line.strip().startswith("#!"):
+                        parser_name = line.strip()[2:].split()[0]
+                        self.setParser(parser_name)
+
+                    if self.parser:
+                        self.in_pre = 'found_parser'
+                        self.parser_lines = [line]
+                        self.parser_name = parser_name
+                        continue
+                    else:
+                        self.request.write(self._closeP() +
+                                           self.formatter.preformatted(1))
+                        self.in_pre = 'no_parser'
+                if self.in_pre == 'found_parser':
+                    # processing mode
+                    try:
+                        endpos = line.index("}}}")
+                    except ValueError:
+                        self.parser_lines.append(line)
+                        continue
+                    if line[:endpos]:
+                        self.parser_lines.append(line[:endpos])
+
+                    # Close p before calling parser
+                    # TODO: do we really need this?
+                    self.request.write(self._closeP())
+                    res = self.formatter.parser(self.parser_name, self.parser_lines)
+                    self.request.write(res)
+                    del self.parser_lines
+                    self.in_pre = None
+                    self.parser = None
+
+                    # send rest of line through regex machinery
+                    line = line[endpos+3:]
+                    if not line.strip(): # just in the case "}}} " when we only have blanks left...
+                        continue
+            else:
+                # we don't have \n as whitespace any more
+                # This is the space between lines we join to one paragraph
+                line += ' '
+
+                # Paragraph break on empty lines
+                if not line.strip():
+                    if self.in_table:
+                        self.request.write(self.formatter.table(0))
+                        self.request.write(self._line_anchordef())
+                        self.in_table = 0
+                    # CHANGE: removed check for not self.list_types
+                    # p should close on every empty line
+                    if self.formatter.in_p:
+                        self.request.write(self.formatter.paragraph(0))
+                    self.line_is_empty = 1
+                    continue
+
+                # Check indent level
+                indent = indent_re.match(line)
+                indlen = len(indent.group(0))
+                indtype = "ul"
+                numtype = None
+                numstart = None
+                if indlen:
+                    match = number_re.match(line)
+                    if match:
+                        numtype, numstart = match.group(0).strip().split('.')
+                        numtype = numtype[0]
+
+                        if numstart and numstart[0] == "#":
+                            numstart = int(numstart[1:])
+                        else:
+                            numstart = None
+
+                        indtype = "ol"
+                    else:
+                        match = term_re.match(line)
+                        if match:
+                            indtype = "dl"
+
+                # output proper indentation tags
+                self.request.write(self._indent_to(indlen, indtype, numtype, numstart))
+
+                # Table mode
+                # TODO: move into function?
+                if (not self.in_table and line[indlen:indlen + 2] == "||"
+                    and line.endswith("|| ") and len(line) >= 5 + indlen):
+                    # Start table
+                    if self.list_types and not self.in_li:
+                        self.request.write(self.formatter.listitem(1, style="list-style-type:none"))
+                        ## CHANGE: no automatic p on li
+                        ##self.request.write(self.formatter.paragraph(1))
+                        self.in_li = 1
+
+                    # CHANGE: removed check for self.in_li
+                    # paragraph should end before table, always!
+                    if self.formatter.in_p:
+                        self.request.write(self.formatter.paragraph(0))
+                    attrs, attrerr = self._getTableAttrs(line[indlen+2:])
+                    self.request.write(self.formatter.table(1, attrs) + attrerr)
+                    self.in_table = True # self.lineno
+                elif (self.in_table and not
+                      # intra-table comments should not break a table
+                      (line.startswith("##") or
+                       line[indlen:indlen + 2] == "||" and
+                       line.endswith("|| ") and
+                       len(line) >= 5 + indlen)):
+
+                    # Close table
+                    self.request.write(self.formatter.table(0))
+                    self.request.write(self._line_anchordef())
+                    self.in_table = 0
+
+            # Scan line, format and write
+            scanning_re = self.in_pre and pre_scan_re or scan_re
+            formatted_line = self.scan(scanning_re, line, inhibit_p=inhibit_p)
+            self.request.write(formatted_line)
+            if self.in_pre == 'no_parser':
+                self.request.write(self.formatter.linebreak())
+
+        # Close code displays, paragraphs, tables and open lists
+        self.request.write(self._undent())
+        if self.in_pre: self.request.write(self.formatter.preformatted(0))
+        if self.formatter.in_p: self.request.write(self.formatter.paragraph(0))
+        if self.in_table: self.request.write(self.formatter.table(0))
+
+        if self.wrapping_div_class:
+            self.request.write(self.formatter.div(0))
+
+    # Private helpers ------------------------------------------------------------
+
+    def setParser(self, name):
+        """ Set parser to parser named 'name' """
+        # XXX this is done by the formatter as well
+        try:
+            self.parser = wikiutil.searchAndImportPlugin(self.request.cfg, "parser", name)
+        except wikiutil.PluginMissingError:
+            self.parser = None
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/migration/wikiutil160a.py	Fri Aug 22 22:58:18 2008 +0200
@@ -0,0 +1,1671 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - Wiki Utility Functions
+
+    @copyright: 2000 - 2004 by Jürgen Hermann <jh@web.de>
+                2007 by Reimar Bauer
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import cgi
+import codecs
+import os
+import re
+import time
+import urllib
+
+from MoinMoin import config
+from MoinMoin.util import pysupport, lock
+
+# Exceptions
+class InvalidFileNameError(Exception):
+    """ Called when we find an invalid file name """
+    pass
+
+# constants for page names
+PARENT_PREFIX = "../"
+PARENT_PREFIX_LEN = len(PARENT_PREFIX)
+CHILD_PREFIX = "/"
+CHILD_PREFIX_LEN = len(CHILD_PREFIX)
+
+#############################################################################
+### Getting data from user/Sending data to user
+#############################################################################
+
+def decodeWindowsPath(text):
+    """ Decode Windows path names correctly. This is needed because many CGI
+    servers follow the RFC recommendation and re-encode the path_info variable
+    according to the file system semantics.
+
+    @param text: the text to decode, string
+    @rtype: unicode
+    @return: decoded text
+    """
+
+    import locale
+    cur_charset = locale.getdefaultlocale()[1]
+    try:
+        return unicode(text, 'utf-8')
+    except UnicodeError:
+        try:
+            return unicode(text, cur_charset, 'replace')
+        except LookupError:
+            return unicode(text, 'iso-8859-1', 'replace')
+
+def decodeUnknownInput(text):
+    """ Decode unknown input, like text attachments
+
+    First we try utf-8 because it has special format, and it will decode
+    only utf-8 files. Then we try config.charset, then iso-8859-1 using
+    'replace'. We will never raise an exception, but may return junk
+    data.
+
+    WARNING: Use this function only for data that you view, not for data
+    that you save in the wiki.
+
+    @param text: the text to decode, string
+    @rtype: unicode
+    @return: decoded text (maybe wrong)
+    """
+    # Shortcut for unicode input
+    if isinstance(text, unicode):
+        return text
+
+    try:
+        return unicode(text, 'utf-8')
+    except UnicodeError:
+        if config.charset not in ['utf-8', 'iso-8859-1']:
+            try:
+                return unicode(text, config.charset)
+            except UnicodeError:
+                pass
+        return unicode(text, 'iso-8859-1', 'replace')
+
+
+def decodeUserInput(s, charsets=[config.charset]):
+    """
+    Decodes input from the user.
+
+    @param s: the string to unquote
+    @param charsets: list of charsets to assume the string is in
+    @rtype: unicode
+    @return: the unquoted string as unicode
+    """
+    for charset in charsets:
+        try:
+            return s.decode(charset)
+        except UnicodeError:
+            pass
+    raise UnicodeError('The string %r cannot be decoded.' % s)
+
+
+# this is a thin wrapper around urllib (urllib only handles str, not unicode)
+# with py <= 2.4.1, it would give incorrect results with unicode
+# with py == 2.4.2, it crashes with unicode, if it contains non-ASCII chars
+def url_quote(s, safe='/', want_unicode=False):
+    """
+    Wrapper around urllib.quote doing the encoding/decoding as usually wanted:
+
+    @param s: the string to quote (can be str or unicode, if it is unicode,
+              config.charset is used to encode it before calling urllib)
+    @param safe: just passed through to urllib
+    @param want_unicode: for the less usual case that you want to get back
+                         unicode and not str, set this to True
+                         Default is False.
+    """
+    if isinstance(s, unicode):
+        s = s.encode(config.charset)
+    elif not isinstance(s, str):
+        s = str(s)
+    s = urllib.quote(s, safe)
+    if want_unicode:
+        s = s.decode(config.charset) # ascii would also work
+    return s
+
+def url_quote_plus(s, safe='/', want_unicode=False):
+    """
+    Wrapper around urllib.quote_plus doing the encoding/decoding as usually wanted:
+
+    @param s: the string to quote (can be str or unicode, if it is unicode,
+              config.charset is used to encode it before calling urllib)
+    @param safe: just passed through to urllib
+    @param want_unicode: for the less usual case that you want to get back
+                         unicode and not str, set this to True
+                         Default is False.
+    """
+    if isinstance(s, unicode):
+        s = s.encode(config.charset)
+    elif not isinstance(s, str):
+        s = str(s)
+    s = urllib.quote_plus(s, safe)
+    if want_unicode:
+        s = s.decode(config.charset) # ascii would also work
+    return s
+
+def url_unquote(s, want_unicode=True):
+    """
+    Wrapper around urllib.unquote doing the encoding/decoding as usually wanted:
+
+    @param s: the string to unquote (can be str or unicode, if it is unicode,
+              config.charset is used to encode it before calling urllib)
+    @param want_unicode: for the less usual case that you want to get back
+                         str and not unicode, set this to False.
+                         Default is True.
+    """
+    if isinstance(s, unicode):
+        s = s.encode(config.charset) # ascii would also work
+    s = urllib.unquote(s)
+    if want_unicode:
+        s = s.decode(config.charset)
+    return s
+
+def parseQueryString(qstr, want_unicode=True):
+    """ Parse a querystring "key=value&..." into a dict.
+    """
+    is_unicode = isinstance(qstr, unicode)
+    if is_unicode:
+        qstr = qstr.encode(config.charset)
+    values = {}
+    for key, value in cgi.parse_qs(qstr).items():
+        if len(value) < 2:
+            v = ''.join(value)
+            if want_unicode:
+                try:
+                    v = unicode(v, config.charset)
+                except UnicodeDecodeError:
+                    v = unicode(v, 'iso-8859-1', 'replace')
+            values[key] = v
+    return values
+
+def makeQueryString(qstr=None, want_unicode=False, **kw):
+    """ Make a querystring from arguments.
+
+    kw arguments overide values in qstr.
+
+    If a string is passed in, it's returned verbatim and
+    keyword parameters are ignored.
+
+    @param qstr: dict to format as query string, using either ascii or unicode
+    @param kw: same as dict when using keywords, using ascii or unicode
+    @rtype: string
+    @return: query string ready to use in a url
+    """
+    if qstr is None:
+        qstr = {}
+    if isinstance(qstr, dict):
+        qstr.update(kw)
+        items = ['%s=%s' % (url_quote_plus(key, want_unicode=want_unicode), url_quote_plus(value, want_unicode=want_unicode)) for key, value in qstr.items()]
+        qstr = '&'.join(items)
+    return qstr
+
+
+def quoteWikinameURL(pagename, charset=config.charset):
+    """ Return a url encoding of filename in plain ascii
+
+    Use urllib.quote to quote any character that is not always safe.
+
+    @param pagename: the original pagename (unicode)
+    @param charset: url text encoding, 'utf-8' recommended. Other charset
+                    might not be able to encode the page name and raise
+                    UnicodeError. (default config.charset ('utf-8')).
+    @rtype: string
+    @return: the quoted filename, all unsafe characters encoded
+    """
+    pagename = pagename.encode(charset)
+    return urllib.quote(pagename)
+
+
+def escape(s, quote=0):
+    """ Escape possible html tags
+
+    Replace special characters '&', '<' and '>' by SGML entities.
+    (taken from cgi.escape so we don't have to include that, even if we
+    don't use cgi at all)
+
+    @param s: (unicode) string to escape
+    @param quote: bool, should transform '\"' to '&quot;'
+    @rtype: when called with a unicode object, return unicode object - otherwise return string object
+    @return: escaped version of s
+    """
+    if not isinstance(s, (str, unicode)):
+        s = str(s)
+
+    # Must first replace &
+    s = s.replace("&", "&amp;")
+
+    # Then other...
+    s = s.replace("<", "&lt;")
+    s = s.replace(">", "&gt;")
+    if quote:
+        s = s.replace('"', "&quot;")
+    return s
+
+def clean_comment(comment):
+    """ Clean comment - replace CR, LF, TAB by whitespace, delete control chars
+        TODO: move this to config, create on first call then return cached.
+    """
+    # we only have input fields with max 200 chars, but spammers send us more
+    if len(comment) > 201:
+        comment = u''
+    remap_chars = {
+        ord(u'\t'): u' ',
+        ord(u'\r'): u' ',
+        ord(u'\n'): u' ',
+    }
+    control_chars = u'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f' \
+                    '\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f'
+    for c in control_chars:
+        remap_chars[c] = None
+    comment = comment.translate(remap_chars)
+    return comment
+
+def make_breakable(text, maxlen):
+    """ make a text breakable by inserting spaces into nonbreakable parts
+    """
+    text = text.split(" ")
+    newtext = []
+    for part in text:
+        if len(part) > maxlen:
+            while part:
+                newtext.append(part[:maxlen])
+                part = part[maxlen:]
+        else:
+            newtext.append(part)
+    return " ".join(newtext)
+
+########################################################################
+### Storage
+########################################################################
+
+# Precompiled patterns for file name [un]quoting
+UNSAFE = re.compile(r'[^a-zA-Z0-9_]+')
+QUOTED = re.compile(r'\(([a-fA-F0-9]+)\)')
+
+
+def quoteWikinameFS(wikiname, charset=config.charset):
+    """ Return file system representation of a Unicode WikiName.
+
+    Warning: will raise UnicodeError if wikiname can not be encoded using
+    charset. The default value of config.charset, 'utf-8' can encode any
+    character.
+
+    @param wikiname: Unicode string possibly containing non-ascii characters
+    @param charset: charset to encode string
+    @rtype: string
+    @return: quoted name, safe for any file system
+    """
+    filename = wikiname.encode(charset)
+
+    quoted = []
+    location = 0
+    for needle in UNSAFE.finditer(filename):
+        # append leading safe stuff
+        quoted.append(filename[location:needle.start()])
+        location = needle.end()
+        # Quote and append unsafe stuff
+        quoted.append('(')
+        for character in needle.group():
+            quoted.append('%02x' % ord(character))
+        quoted.append(')')
+
+    # append rest of string
+    quoted.append(filename[location:])
+    return ''.join(quoted)
+
+
+def unquoteWikiname(filename, charsets=[config.charset]):
+    """ Return Unicode WikiName from quoted file name.
+
+    We raise an InvalidFileNameError if we find an invalid name, so the
+    wiki could alarm the admin or suggest the user to rename a page.
+    Invalid file names should never happen in normal use, but are rather
+    cheap to find.
+
+    This function should be used only to unquote file names, not page
+    names we receive from the user. These are handled in request by
+    urllib.unquote, decodePagename and normalizePagename.
+
+    Todo: search clients of unquoteWikiname and check for exceptions.
+
+    @param filename: string using charset and possibly quoted parts
+    @param charsets: list of charsets used by string
+    @rtype: Unicode String
+    @return: WikiName
+    """
+    ### Temporary fix start ###
+    # From some places we get called with Unicode strings
+    if isinstance(filename, type(u'')):
+        filename = filename.encode(config.charset)
+    ### Temporary fix end ###
+
+    parts = []
+    start = 0
+    for needle in QUOTED.finditer(filename):
+        # append leading unquoted stuff
+        parts.append(filename[start:needle.start()])
+        start = needle.end()
+        # Append quoted stuff
+        group = needle.group(1)
+        # Filter invalid filenames
+        if (len(group) % 2 != 0):
+            raise InvalidFileNameError(filename)
+        try:
+            for i in range(0, len(group), 2):
+                byte = group[i:i+2]
+                character = chr(int(byte, 16))
+                parts.append(character)
+        except ValueError:
+            # byte not in hex, e.g 'xy'
+            raise InvalidFileNameError(filename)
+
+    # append rest of string
+    if start == 0:
+        wikiname = filename
+    else:
+        parts.append(filename[start:len(filename)])
+        wikiname = ''.join(parts)
+
+    # FIXME: This looks wrong, because at this stage "()" can be both errors
+    # like open "(" without close ")", or unquoted valid characters in the file name.
+    # Filter invalid filenames. Any left (xx) must be invalid
+    #if '(' in wikiname or ')' in wikiname:
+    #    raise InvalidFileNameError(filename)
+
+    wikiname = decodeUserInput(wikiname, charsets)
+    return wikiname
+
+# time scaling
+def timestamp2version(ts):
+    """ Convert UNIX timestamp (may be float or int) to our version
+        (long) int.
+        We don't want to use floats, so we just scale by 1e6 to get
+        an integer in usecs.
+    """
+    return long(ts*1000000L) # has to be long for py 2.2.x
+
+def version2timestamp(v):
+    """ Convert version number to UNIX timestamp (float).
+        This must ONLY be used for display purposes.
+    """
+    return v / 1000000.0
+
+
+# This is the list of meta attribute names to be treated as integers.
+# IMPORTANT: do not use any meta attribute names with "-" (or any other chars
+# invalid in python attribute names), use e.g. _ instead.
+INTEGER_METAS = ['current', 'revision', # for page storage (moin 2.0)
+                 'data_format_revision', # for data_dir format spec (use by mig scripts)
+                ]
+
+class MetaDict(dict):
+    """ store meta informations as a dict.
+    """
+    def __init__(self, metafilename, cache_directory):
+        """ create a MetaDict from metafilename """
+        dict.__init__(self)
+        self.metafilename = metafilename
+        self.dirty = False
+        lock_dir = os.path.join(cache_directory, '__metalock__')
+        self.rlock = lock.ReadLock(lock_dir, 60.0)
+        self.wlock = lock.WriteLock(lock_dir, 60.0)
+
+        if not self.rlock.acquire(3.0):
+            raise EnvironmentError("Could not lock in MetaDict")
+        try:
+            self._get_meta()
+        finally:
+            self.rlock.release()
+
+    def _get_meta(self):
+        """ get the meta dict from an arbitrary filename.
+            does not keep state, does uncached, direct disk access.
+            @param metafilename: the name of the file to read
+            @return: dict with all values or {} if empty or error
+        """
+
+        try:
+            metafile = codecs.open(self.metafilename, "r", "utf-8")
+            meta = metafile.read() # this is much faster than the file's line-by-line iterator
+            metafile.close()
+        except IOError:
+            meta = u''
+        for line in meta.splitlines():
+            key, value = line.split(':', 1)
+            value = value.strip()
+            if key in INTEGER_METAS:
+                value = int(value)
+            dict.__setitem__(self, key, value)
+
+    def _put_meta(self):
+        """ put the meta dict into an arbitrary filename.
+            does not keep or modify state, does uncached, direct disk access.
+            @param metafilename: the name of the file to write
+            @param metadata: dict of the data to write to the file
+        """
+        meta = []
+        for key, value in self.items():
+            if key in INTEGER_METAS:
+                value = str(value)
+            meta.append("%s: %s" % (key, value))
+        meta = '\r\n'.join(meta)
+
+        metafile = codecs.open(self.metafilename, "w", "utf-8")
+        metafile.write(meta)
+        metafile.close()
+        self.dirty = False
+
+    def sync(self, mtime_usecs=None):
+        """ No-Op except for that parameter """
+        if not mtime_usecs is None:
+            self.__setitem__('mtime', str(mtime_usecs))
+        # otherwise no-op
+
+    def __getitem__(self, key):
+        """ We don't care for cache coherency here. """
+        return dict.__getitem__(self, key)
+
+    def __setitem__(self, key, value):
+        """ Sets a dictionary entry. """
+        if not self.wlock.acquire(5.0):
+            raise EnvironmentError("Could not lock in MetaDict")
+        try:
+            self._get_meta() # refresh cache
+            try:
+                oldvalue = dict.__getitem__(self, key)
+            except KeyError:
+                oldvalue = None
+            if value != oldvalue:
+                dict.__setitem__(self, key, value)
+                self._put_meta() # sync cache
+        finally:
+            self.wlock.release()
+
+
+# Quoting of wiki names, file names, etc. (in the wiki markup) -----------------------------------
+
+QUOTE_CHARS = u"'\""
+
+def quoteName(name):
+    """ put quotes around a given name """
+    for quote_char in QUOTE_CHARS:
+        if quote_char not in name:
+            return u"%s%s%s" % (quote_char, name, quote_char)
+    else:
+        return name # XXX we need to be able to escape the quote char for worst case
+
+def unquoteName(name):
+    """ if there are quotes around the name, strip them """
+    for quote_char in QUOTE_CHARS:
+        if quote_char == name[0] == name[-1]:
+            return name[1:-1]
+    else:
+        return name
+
+#############################################################################
+### InterWiki
+#############################################################################
+INTERWIKI_PAGE = "InterWikiMap"
+
+def generate_file_list(request):
+    """ generates a list of all files. for internal use. """
+
+    # order is important here, the local intermap file takes
+    # precedence over the shared one, and is thus read AFTER
+    # the shared one
+    intermap_files = request.cfg.shared_intermap
+    if not isinstance(intermap_files, list):
+        intermap_files = [intermap_files]
+    else:
+        intermap_files = intermap_files[:]
+    intermap_files.append(os.path.join(request.cfg.data_dir, "intermap.txt"))
+    request.cfg.shared_intermap_files = [filename for filename in intermap_files
+                                         if filename and os.path.isfile(filename)]
+
+
+def get_max_mtime(file_list, page):
+    """ Returns the highest modification time of the files in file_list and the
+    page page. """
+    timestamps = [os.stat(filename).st_mtime for filename in file_list]
+    if page.exists():
+        # exists() is cached and thus cheaper than mtime_usecs()
+        timestamps.append(version2timestamp(page.mtime_usecs()))
+    return max(timestamps)
+
+
+def load_wikimap(request):
+    """ load interwiki map (once, and only on demand) """
+    from MoinMoin.Page import Page
+
+    now = int(time.time())
+    if getattr(request.cfg, "shared_intermap_files", None) is None:
+        generate_file_list(request)
+
+    try:
+        _interwiki_list = request.cfg.cache.interwiki_list
+        old_mtime = request.cfg.cache.interwiki_mtime
+        if request.cfg.cache.interwiki_ts + (1*60) < now: # 1 minutes caching time
+            max_mtime = get_max_mtime(request.cfg.shared_intermap_files, Page(request, INTERWIKI_PAGE))
+            if max_mtime > old_mtime:
+                raise AttributeError # refresh cache
+            else:
+                request.cfg.cache.interwiki_ts = now
+    except AttributeError:
+        _interwiki_list = {}
+        lines = []
+
+        for filename in request.cfg.shared_intermap_files:
+            f = open(filename, "r")
+            lines.extend(f.readlines())
+            f.close()
+
+        # add the contents of the InterWikiMap page
+        lines += Page(request, INTERWIKI_PAGE).get_raw_body().splitlines()
+
+        for line in lines:
+            if not line or line[0] == '#': continue
+            try:
+                line = "%s %s/InterWiki" % (line, request.getScriptname())
+                wikitag, urlprefix, dummy = line.split(None, 2)
+            except ValueError:
+                pass
+            else:
+                _interwiki_list[wikitag] = urlprefix
+
+        del lines
+
+        # add own wiki as "Self" and by its configured name
+        _interwiki_list['Self'] = request.getScriptname() + '/'
+        if request.cfg.interwikiname:
+            _interwiki_list[request.cfg.interwikiname] = request.getScriptname() + '/'
+
+        # save for later
+        request.cfg.cache.interwiki_list = _interwiki_list
+        request.cfg.cache.interwiki_ts = now
+        request.cfg.cache.interwiki_mtime = get_max_mtime(request.cfg.shared_intermap_files, Page(request, INTERWIKI_PAGE))
+
+    return _interwiki_list
+
+def split_wiki(wikiurl):
+    """ Split a wiki url, e.g:
+
+    'MoinMoin:FrontPage' -> "MoinMoin", "FrontPage", ""
+    'FrontPage' -> "Self", "FrontPage", ""
+    'MoinMoin:"Page with blanks" link title' -> "MoinMoin", "Page with blanks", "link title"
+
+    can also be used for:
+
+    'attachment:"filename with blanks.txt" other title' -> "attachment", "filename with blanks.txt", "other title"
+
+    @param wikiurl: the url to split
+    @rtype: tuple
+    @return: (wikiname, pagename, linktext)
+    """
+    try:
+        wikiname, rest = wikiurl.split(":", 1) # e.g. MoinMoin:FrontPage
+    except ValueError:
+        try:
+            wikiname, rest = wikiurl.split("/", 1) # for what is this used?
+        except ValueError:
+            wikiname, rest = 'Self', wikiurl
+    if rest:
+        first_char = rest[0]
+        if first_char in QUOTE_CHARS: # quoted pagename
+            pagename_linktext = rest[1:].split(first_char, 1)
+        else: # not quoted, split on whitespace
+            pagename_linktext = rest.split(None, 1)
+    else:
+        pagename_linktext = "", ""
+    if len(pagename_linktext) == 1:
+        pagename, linktext = pagename_linktext[0], ""
+    else:
+        pagename, linktext = pagename_linktext
+    linktext = linktext.strip()
+    return wikiname, pagename, linktext
+
+def resolve_wiki(request, wikiurl):
+    """ Resolve an interwiki link.
+
+    @param request: the request object
+    @param wikiurl: the InterWiki:PageName link
+    @rtype: tuple
+    @return: (wikitag, wikiurl, wikitail, err)
+    """
+    _interwiki_list = load_wikimap(request)
+    wikiname, pagename, linktext = split_wiki(wikiurl)
+    if _interwiki_list.has_key(wikiname):
+        return (wikiname, _interwiki_list[wikiname], pagename, False)
+    else:
+        return (wikiname, request.getScriptname(), "/InterWiki", True)
+
+def join_wiki(wikiurl, wikitail):
+    """
+    Add a (url_quoted) page name to an interwiki url.
+
+    Note: We can't know what kind of URL quoting a remote wiki expects.
+          We just use a utf-8 encoded string with standard URL quoting.
+
+    @param wikiurl: wiki url, maybe including a $PAGE placeholder
+    @param wikitail: page name
+    @rtype: string
+    @return: generated URL of the page in the other wiki
+    """
+    wikitail = url_quote(wikitail)
+    if '$PAGE' in wikiurl:
+        return wikiurl.replace('$PAGE', wikitail)
+    else:
+        return wikiurl + wikitail
+
+
+#############################################################################
+### Page types (based on page names)
+#############################################################################
+
+def isSystemPage(request, pagename):
+    """ Is this a system page? Uses AllSystemPagesGroup internally.
+
+    @param request: the request object
+    @param pagename: the page name
+    @rtype: bool
+    @return: true if page is a system page
+    """
+    return (request.dicts.has_member('SystemPagesGroup', pagename) or
+        isTemplatePage(request, pagename))
+
+
+def isTemplatePage(request, pagename):
+    """ Is this a template page?
+
+    @param pagename: the page name
+    @rtype: bool
+    @return: true if page is a template page
+    """
+    return request.cfg.cache.page_template_regex.search(pagename) is not None
+
+
+def isGroupPage(request, pagename):
+    """ Is this a name of group page?
+
+    @param pagename: the page name
+    @rtype: bool
+    @return: true if page is a form page
+    """
+    return request.cfg.cache.page_group_regex.search(pagename) is not None
+
+
+def filterCategoryPages(request, pagelist):
+    """ Return category pages in pagelist
+
+    WARNING: DO NOT USE THIS TO FILTER THE FULL PAGE LIST! Use
+    getPageList with a filter function.
+
+    If you pass a list with a single pagename, either that is returned
+    or an empty list, thus you can use this function like a `isCategoryPage`
+    one.
+
+    @param pagelist: a list of pages
+    @rtype: list
+    @return: only the category pages of pagelist
+    """
+    func = request.cfg.cache.page_category_regex.search
+    return filter(func, pagelist)
+
+
+def getLocalizedPage(request, pagename): # was: getSysPage
+    """ Get a system page according to user settings and available translations.
+
+    We include some special treatment for the case that <pagename> is the
+    currently rendered page, as this is the case for some pages used very
+    often, like FrontPage, RecentChanges etc. - in that case we reuse the
+    already existing page object instead creating a new one.
+
+    @param request: the request object
+    @param pagename: the name of the page
+    @rtype: Page object
+    @return: the page object of that system page, using a translated page,
+             if it exists
+    """
+    from MoinMoin.Page import Page
+    i18n_name = request.getText(pagename, formatted=False)
+    pageobj = None
+    if i18n_name != pagename:
+        if request.page and i18n_name == request.page.page_name:
+            # do not create new object for current page
+            i18n_page = request.page
+            if i18n_page.exists():
+                pageobj = i18n_page
+        else:
+            i18n_page = Page(request, i18n_name)
+            if i18n_page.exists():
+                pageobj = i18n_page
+
+    # if we failed getting a translated version of <pagename>,
+    # we fall back to english
+    if not pageobj:
+        if request.page and pagename == request.page.page_name:
+            # do not create new object for current page
+            pageobj = request.page
+        else:
+            pageobj = Page(request, pagename)
+    return pageobj
+
+
+def getFrontPage(request):
+    """ Convenience function to get localized front page
+
+    @param request: current request
+    @rtype: Page object
+    @return localized page_front_page, if there is a translation
+    """
+    return getLocalizedPage(request, request.cfg.page_front_page)
+
+
+def getHomePage(request, username=None):
+    """
+    Get a user's homepage, or return None for anon users and
+    those who have not created a homepage.
+
+    DEPRECATED - try to use getInterwikiHomePage (see below)
+
+    @param request: the request object
+    @param username: the user's name
+    @rtype: Page
+    @return: user's homepage object - or None
+    """
+    from MoinMoin.Page import Page
+    # default to current user
+    if username is None and request.user.valid:
+        username = request.user.name
+
+    # known user?
+    if username:
+        # Return home page
+        page = Page(request, username)
+        if page.exists():
+            return page
+
+    return None
+
+
+def getInterwikiHomePage(request, username=None):
+    """
+    Get a user's homepage.
+
+    cfg.user_homewiki influences behaviour of this:
+    'Self' does mean we store user homepage in THIS wiki.
+    When set to our own interwikiname, it behaves like with 'Self'.
+
+    'SomeOtherWiki' means we store user homepages in another wiki.
+
+    @param request: the request object
+    @param username: the user's name
+    @rtype: tuple (or None for anon users)
+    @return: (wikiname, pagename)
+    """
+    # default to current user
+    if username is None and request.user.valid:
+        username = request.user.name
+    if not username:
+        return None # anon user
+
+    homewiki = request.cfg.user_homewiki
+    if homewiki == request.cfg.interwikiname:
+        homewiki = 'Self'
+
+    return homewiki, username
+
+
+def AbsPageName(request, context, pagename):
+    """
+    Return the absolute pagename for a (possibly) relative pagename.
+
+    @param context: name of the page where "pagename" appears on
+    @param pagename: the (possibly relative) page name
+    @rtype: string
+    @return: the absolute page name
+    """
+    if pagename.startswith(PARENT_PREFIX):
+        pagename = '/'.join(filter(None, context.split('/')[:-1] + [pagename[PARENT_PREFIX_LEN:]]))
+    elif pagename.startswith(CHILD_PREFIX):
+        pagename = context + '/' + pagename[CHILD_PREFIX_LEN:]
+    return pagename
+
+def pagelinkmarkup(pagename):
+    """ return markup that can be used as link to page <pagename> """
+    from MoinMoin.parser.text_moin_wiki import Parser
+    if re.match(Parser.word_rule + "$", pagename):
+        return pagename
+    else:
+        return u'["%s"]' % pagename # XXX use quoteName(pagename) later
+
+#############################################################################
+### mimetype support
+#############################################################################
+import mimetypes
+
+MIMETYPES_MORE = {
+ # OpenOffice 2.x & other open document stuff
+ '.odt': 'application/vnd.oasis.opendocument.text',
+ '.ods': 'application/vnd.oasis.opendocument.spreadsheet',
+ '.odp': 'application/vnd.oasis.opendocument.presentation',
+ '.odg': 'application/vnd.oasis.opendocument.graphics',
+ '.odc': 'application/vnd.oasis.opendocument.chart',
+ '.odf': 'application/vnd.oasis.opendocument.formula',
+ '.odb': 'application/vnd.oasis.opendocument.database',
+ '.odi': 'application/vnd.oasis.opendocument.image',
+ '.odm': 'application/vnd.oasis.opendocument.text-master',
+ '.ott': 'application/vnd.oasis.opendocument.text-template',
+ '.ots': 'application/vnd.oasis.opendocument.spreadsheet-template',
+ '.otp': 'application/vnd.oasis.opendocument.presentation-template',
+ '.otg': 'application/vnd.oasis.opendocument.graphics-template',
+}
+[mimetypes.add_type(mimetype, ext, True) for ext, mimetype in MIMETYPES_MORE.items()]
+
+MIMETYPES_sanitize_mapping = {
+    # this stuff is text, but got application/* for unknown reasons
+    ('application', 'docbook+xml'): ('text', 'docbook'),
+    ('application', 'x-latex'): ('text', 'latex'),
+    ('application', 'x-tex'): ('text', 'tex'),
+    ('application', 'javascript'): ('text', 'javascript'),
+}
+
+MIMETYPES_spoil_mapping = {} # inverse mapping of above
+for key, value in MIMETYPES_sanitize_mapping.items():
+    MIMETYPES_spoil_mapping[value] = key
+
+
+class MimeType(object):
+    """ represents a mimetype like text/plain """
+
+    def __init__(self, mimestr=None, filename=None):
+        self.major = self.minor = None # sanitized mime type and subtype
+        self.params = {} # parameters like "charset" or others
+        self.charset = None # this stays None until we know for sure!
+        self.raw_mimestr = mimestr
+
+        if mimestr:
+            self.parse_mimetype(mimestr)
+        elif filename:
+            self.parse_filename(filename)
+
+    def parse_filename(self, filename):
+        mtype, encoding = mimetypes.guess_type(filename)
+        if mtype is None:
+            mtype = 'application/octet-stream'
+        self.parse_mimetype(mtype)
+
+    def parse_mimetype(self, mimestr):
+        """ take a string like used in content-type and parse it into components,
+            alternatively it also can process some abbreviated string like "wiki"
+        """
+        parameters = mimestr.split(";")
+        parameters = [p.strip() for p in parameters]
+        mimetype, parameters = parameters[0], parameters[1:]
+        mimetype = mimetype.split('/')
+        if len(mimetype) >= 2:
+            major, minor = mimetype[:2] # we just ignore more than 2 parts
+        else:
+            major, minor = self.parse_format(mimetype[0])
+        self.major = major.lower()
+        self.minor = minor.lower()
+        for param in parameters:
+            key, value = param.split('=')
+            if value[0] == '"' and value[-1] == '"': # remove quotes
+                value = value[1:-1]
+            self.params[key.lower()] = value
+        if self.params.has_key('charset'):
+            self.charset = self.params['charset'].lower()