changeset 1083:cf4a2f11c959

Merge with main.
author Alexander Schremmer <alex AT alexanderweb DOT de>
date Fri, 28 Jul 2006 17:03:03 +0200
parents ba25ee4ea61d (current diff) c1f5ee67a02b (diff)
children 815f69e6c563
files MoinMoin/xmlrpc/__init__.py
diffstat 43 files changed, 275 insertions(+), 323 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Fri Jul 28 17:02:13 2006 +0200
+++ b/Makefile	Fri Jul 28 17:03:03 2006 +0200
@@ -33,7 +33,7 @@
 
 # Create documentation
 epydoc: patchlevel
-	@epydoc -o ../html -n MoinMoin -u http://moinmoin.wikiwikiweb.de MoinMoin
+	@epydoc -o ../html-1.6 -n MoinMoin -u http://moinmoin.wikiwikiweb.de MoinMoin
 
 # Create new underlay directory from MoinMaster
 # Should be used only on TW machine
--- a/MoinMoin/Page.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/Page.py	Fri Jul 28 17:03:03 2006 +0200
@@ -964,22 +964,21 @@
     def send_raw(self):
         """ Output the raw page data (action=raw) """
         request = self.request
-        request.http_headers(["Content-type: text/plain;charset=%s" % config.charset])
+        request.setHttpHeader("Content-type: text/plain; charset=%s" % config.charset)
         if self.exists():
             # use the correct last-modified value from the on-disk file
             # to ensure cacheability where supported. Because we are sending
             # RAW (file) content, the file mtime is correct as Last-Modified header.
-            request.http_headers(["Last-Modified: " +
-                 timefuncs.formathttpdate(os.path.getmtime(self._text_filename()))])
-
+            request.setHttpHeader("Status: 200 OK")
+            request.setHttpHeader("Last-Modified: %s" % timefuncs.formathttpdate(os.path.getmtime(self._text_filename())))
             text = self.get_raw_body()
             text = self.encodeTextMimeType(text)
-            request.write(text)
         else:
-            request.http_headers(['Status: 404 NOTFOUND'])
-            request.setResponseCode(404)
-            request.write(u"Page %s not found." % self.page_name)
+            request.setHttpHeader('Status: 404 NOTFOUND')
+            text = u"Page %s not found." % self.page_name
 
+        request.emit_http_headers()
+        request.write(text)
 
     def send_page(self, request, msg=None, **keywords):
         """ Output the formatted page.
@@ -990,7 +989,7 @@
 
         @param request: the request object
         @param msg: if given, display message in header area
-        @keyword content_only: if 1, omit page header and footer
+        @keyword content_only: if 1, omit http headers, page header and footer
         @keyword content_id: set the id of the enclosing div
         @keyword count_hit: if 1, add an event to the log
         @keyword send_missing_page: if 1, assume that page to be sent is MissingPage
@@ -1042,8 +1041,6 @@
         if self.hilite_re:
             self.formatter.set_highlight_re(self.hilite_re)
         
-        request.http_headers(["Content-Type: %s; charset=%s" % (self.output_mimetype, self.output_charset)])
-
         # default is wiki markup
         pi_format = self.cfg.default_markup or "wiki"
         pi_formatargs = ''
@@ -1162,25 +1159,26 @@
         doc_leader = self.formatter.startDocument(self.page_name)
         page_exists = self.exists()
         if not content_only:
-            # send the document leader
-
-            # use "nocache" headers if we're using a method that
-            # is not simply "display", or if a user is logged in
-            # (which triggers personalisation features)
-
+            request.setHttpHeader("Content-Type: %s; charset=%s" % (self.output_mimetype, self.output_charset))
             if page_exists:
+                request.setHttpHeader('Status: 200 OK')
                 if not request.cacheable or request.user.valid:
-                    request.http_headers(request.nocache)
+                    # use "nocache" headers if we're using a method that
+                    # is not simply "display", or if a user is logged in
+                    # (which triggers personalisation features)
+                    for header in request.nocache:
+                        request.setHttpHeader(header)
                 else:
                     # TODO: we need to know if a page generates dynamic content
                     # if it does, we must not use the page file mtime as last modified value
                     # XXX The following code is commented because it is incorrect for dynamic pages:
                     #lastmod = os.path.getmtime(self._text_filename())
-                    #request.http_headers(["Last-Modified: %s" % timefuncs.formathttpdate(lastmod)])
-                    request.http_headers()
+                    #request.setHttpHeader("Last-Modified: %s" % timefuncs.formathttpdate(lastmod))
+                    pass
             else:
-                request.http_headers(['Status: 404 NOTFOUND'])
-                request.setResponseCode(404)
+                request.setHttpHeader('Status: 404 NOTFOUND')
+            request.emit_http_headers()
+
             request.write(doc_leader)
 
             # send the page header
--- a/MoinMoin/PageEditor.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/PageEditor.py	Fri Jul 28 17:03:03 2006 +0200
@@ -141,7 +141,7 @@
 
         form = self.request.form
         _ = self._
-        self.request.http_headers(self.request.nocache)
+        self.request.emit_http_headers(self.request.nocache)
 
         raw_body = ''
         msg = None
--- a/MoinMoin/PageGraphicalEditor.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/PageGraphicalEditor.py	Fri Jul 28 17:03:03 2006 +0200
@@ -54,7 +54,7 @@
         request = self.request
         form = self.request.form
         _ = self._
-        self.request.http_headers(self.request.nocache)
+        self.request.emit_http_headers(self.request.nocache)
 
         raw_body = ''
         msg = None
--- a/MoinMoin/__init__.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/__init__.py	Fri Jul 28 17:03:03 2006 +0200
@@ -1,6 +1,6 @@
 # -*- coding: iso-8859-1 -*-
 """
-MoinMoin Version 1.6.0alpha bf18e19e618d+ tip
+MoinMoin Version 1.6.0alpha 61142a50c41b+ tip
 
 @copyright: 2000-2006 by Jürgen Hermann <jh@web.de>
 @license: GNU GPL, see COPYING for details.
--- a/MoinMoin/_tests/test_request.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/_tests/test_request.py	Fri Jul 28 17:03:03 2006 +0200
@@ -123,30 +123,3 @@
                              'wrong date string')
 
 
-class GetPageNameFromQueryString(unittest.TestCase):
-    """ Test urls like http://netloc/wiki?pagename """
-
-    def setUp(self):
-        self.savedQuery = self.request.query_string
-
-    def tearDown(self):
-        self.request.query_string = self.savedQuery
-
-    def testAscii(self):
-        """ request: getPageNameFromQueryString: ascii """
-        name = expected = u'page name'
-        self.runTest(name, expected)
-
-    def testNonAscii(self):
-        """ request: getPageNameFromQueryString: non ascii """
-        name = expected = u'דף עברי'
-        self.runTest(name, expected)
-
-    def runTest(self, name, expected):
-        import urllib
-        # query as made by most browsers when you type the url into the
-        # location box.
-        query = urllib.quote(name.encode('utf-8'))
-        self.request.query_string = query
-        self.assertEqual(self.request.getPageNameFromQueryString(), expected)
-
--- a/MoinMoin/action/AttachFile.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/AttachFile.py	Fri Jul 28 17:03:03 2006 +0200
@@ -493,7 +493,7 @@
     elif request.form['do'][0] == 'savedrawing':
         if request.user.may.write(pagename):
             save_drawing(pagename, request)
-            request.http_headers()
+            request.emit_http_headers()
             request.write("OK")
         else:
             msg = _('You are not allowed to save a drawing on this page.')
@@ -541,7 +541,7 @@
 def upload_form(pagename, request, msg=''):
     _ = request.getText
 
-    request.http_headers()
+    request.emit_http_headers()
     # Use user interface language for this generated page
     request.setContentLanguage(request.lang)
     request.theme.send_title(_('Attachments for "%(pagename)s"') % {'pagename': pagename}, pagename=pagename, msg=msg)
@@ -653,8 +653,7 @@
 
     mt = wikiutil.MimeType(filename=filename)
 
-    # send header
-    request.http_headers([
+    request.emit_http_headers([
         "Content-Type: %s" % mt.content_type(),
         "Content-Length: %d" % os.path.getsize(fpath),
         # TODO: fix the encoding here, plain 8 bit is not allowed according to the RFCs
@@ -824,7 +823,7 @@
     if not filename: return
 
     # send header & title
-    request.http_headers()
+    request.emit_http_headers()
     # Use user interface language for this generated page
     request.setContentLanguage(request.lang)
     title = _('attachment:%(filename)s of %(pagename)s', formatted=True) % {
--- a/MoinMoin/action/Despam.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/Despam.py	Fri Jul 28 17:03:03 2006 +0200
@@ -169,7 +169,7 @@
        # request.form.get('timestamp', [None])[0]
     ok = request.form.get('ok', [0])[0]
 
-    request.http_headers()
+    request.emit_http_headers()
     request.theme.send_title("Despam", pagename=pagename)
     # Start content (important for RTL support)
     request.write(request.formatter.startContent("content"))
--- a/MoinMoin/action/LikePages.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/LikePages.py	Fri Jul 28 17:03:03 2006 +0200
@@ -41,7 +41,7 @@
         return
 
     # more than one match, list 'em
-    request.http_headers()
+    request.emit_http_headers()
 
     # This action generate data using the user language
     request.setContentLanguage(request.lang)
--- a/MoinMoin/action/LocalSiteMap.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/LocalSiteMap.py	Fri Jul 28 17:03:03 2006 +0200
@@ -28,7 +28,7 @@
 
 def execute(pagename, request):
     _ = request.getText
-    request.http_headers()
+    request.emit_http_headers()
 
     # This action generate data using the user language
     request.setContentLanguage(request.lang)
--- a/MoinMoin/action/MyPages.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/MyPages.py	Fri Jul 28 17:03:03 2006 +0200
@@ -58,7 +58,7 @@
 
     from MoinMoin.Page import Page
     from MoinMoin.parser.text_moin_wiki import Parser as WikiParser
-    request.http_headers()
+    request.emit_http_headers()
 
     # This action generate data using the user language
     request.setContentLanguage(request.lang)
--- a/MoinMoin/action/SubscribeUser.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/SubscribeUser.py	Fri Jul 28 17:03:03 2006 +0200
@@ -17,7 +17,7 @@
 
 def show_form(pagename, request):
     _ = request.getText
-    request.http_headers()
+    request.emit_http_headers()
     request.theme.send_title(_("Subscribe users to the page %s") % pagename, pagename=pagename)
 
     request.write("""
@@ -32,7 +32,7 @@
 
 def show_result(pagename, request):
     _ = request.getText
-    request.http_headers()
+    request.emit_http_headers()
 
     request.theme.send_title(_("Subscribed for %s:") % pagename, pagename=pagename)
 
--- a/MoinMoin/action/__init__.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/__init__.py	Fri Jul 28 17:03:03 2006 +0200
@@ -252,7 +252,7 @@
 
 def do_content(pagename, request):
     """ same as do_show, but we only show the content """
-    request.http_headers()
+    request.emit_http_headers()
     page = Page(request, pagename)
     request.write('<!-- Transclusion of %s -->' % request.getQualifiedURL(page.url(request)))
     page.send_page(request, count_hit=0, content_only=1)
@@ -426,7 +426,7 @@
     """ dump the form data we received in this request for debugging """
     data = util.dumpFormData(request.form)
 
-    request.http_headers()
+    request.emit_http_headers()
     request.write("<html><body>%s</body></html>" % data)
 
 
--- a/MoinMoin/action/backup.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/backup.py	Fri Jul 28 17:03:03 2006 +0200
@@ -28,7 +28,7 @@
     """ Send compressed tar file """
     dateStamp = time.strftime("%Y-%m-%d--%H-%M-%S-UTC", time.gmtime())
     filename = "%s-%s.tar.%s" % (request.cfg.siteid, dateStamp, request.cfg.backup_compression)
-    request.http_headers([
+    request.emit_http_headers([
         "Content-Type: application/octet-stream",
         "Content-Disposition: inline; filename=\"%s\"" % filename, ])
 
@@ -70,7 +70,7 @@
 
 def sendBackupForm(request, pagename):
     _ = request.getText
-    request.http_headers()
+    request.emit_http_headers()
     request.setContentLanguage(request.lang)
     title = _('Wiki Backup / Restore')
     request.theme.send_title(title, form=request.form, pagename=pagename)
--- a/MoinMoin/action/diff.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/diff.py	Fri Jul 28 17:03:03 2006 +0200
@@ -71,7 +71,7 @@
     # This action generate content in the user language
     request.setContentLanguage(request.lang)
 
-    request.http_headers()
+    request.emit_http_headers()
     request.theme.send_title(_('Diff for "%s"') % (pagename,), pagename=pagename, allow_doubleclick=1)
 
     if rev1 > 0 and rev2 > 0 and rev1 > rev2 or rev1 == 0 and rev2 > 0:
--- a/MoinMoin/action/edit.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/edit.py	Fri Jul 28 17:03:03 2006 +0200
@@ -137,7 +137,6 @@
         try:
             still_conflict = wikiutil.containsConflictMarker(savetext)
             pg.setConflict(still_conflict)
-            request.http_headers() # XXX WHY? XXX
             savemsg = pg.saveText(savetext, rev, trivial=trivial, comment=comment)
         except pg.EditConflict, e:
             msg = e.message
--- a/MoinMoin/action/fckdialog.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/fckdialog.py	Fri Jul 28 17:03:03 2006 +0200
@@ -15,7 +15,7 @@
 
 def macro_dialog(request):
     help = get_macro_help(request)
-    request.http_headers()
+    request.emit_http_headers()
     request.write(
         '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
 <html>
@@ -169,7 +169,7 @@
         pages = [p.page_name for p in searchresult.hits]
     else:
         pages = [name]
-    request.http_headers()
+    request.emit_http_headers()
     request.write(
         '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
 <html>
@@ -200,7 +200,7 @@
 ''' % "".join(["<option>%s</option>\n" % p for p in pages]))
 
 def link_dialog(request):
-    request.http_headers()
+    request.emit_http_headers()
     # list of wiki pages
     name = request.form.get("pagename", [""])[0]
     if name:
@@ -246,7 +246,7 @@
         scriptname += "/"
     action = scriptname
     basepage = request.page.page_name.encode(config.charset)
-    request.http_headers()
+    request.emit_http_headers()
     request.write('''
 <!--
  * FCKeditor - The text editor for internet
@@ -367,7 +367,7 @@
 ##############################################################################
 
 def attachment_dialog(request):
-    request.http_headers()
+    request.emit_http_headers()
     # list of wiki pages
     name = request.form.get("pagename", [""])[0]
     if name:
@@ -397,7 +397,7 @@
     if not scriptname or scriptname[-1] != "/":
         scriptname += "/"
     action = scriptname
-    request.http_headers()
+    request.emit_http_headers()
     request.write('''
 <!--
  * FCKeditor - The text editor for internet
@@ -462,7 +462,7 @@
 ##############################################################################
 
 def image_dialog(request):
-    request.http_headers()
+    request.emit_http_headers()
     url_prefix = request.cfg.url_prefix
     request.write('''
 <!--
--- a/MoinMoin/action/fullsearch.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/fullsearch.py	Fri Jul 28 17:03:03 2006 +0200
@@ -50,8 +50,7 @@
     if len(striped) == 0:
         err = _('Please use a more selective search term instead '
                 'of {{{"%s"}}}') % needle
-        # send http headers
-        request.http_headers()
+        request.emit_http_headers()
         Page(request, pagename).send_page(request, msg=err)
         return
 
@@ -74,8 +73,7 @@
             request.http_redirect(url)
             return
 
-    # send http headers
-    request.http_headers()
+    request.emit_http_headers()
 
     # This action generate data using the user language
     request.setContentLanguage(request.lang)
--- a/MoinMoin/action/info.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/info.py	Fri Jul 28 17:03:03 2006 +0200
@@ -8,6 +8,7 @@
                 2006 by MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
+import os
 
 from MoinMoin import config, wikiutil, action
 from MoinMoin.Page import Page
@@ -205,7 +206,7 @@
     qpagename = wikiutil.quoteWikinameURL(pagename)
     title = page.split_title(request)
 
-    request.http_headers()
+    request.emit_http_headers()
 
     # This action uses page or wiki language TODO: currently
     # page.language is broken and not available now, when we fix it,
--- a/MoinMoin/action/links.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/links.py	Fri Jul 28 17:03:03 2006 +0200
@@ -19,7 +19,7 @@
     else:
         mimetype = "text/html"
 
-    request.http_headers(["Content-Type: %s; charset=%s" % (mimetype, config.charset)])
+    request.emit_http_headers(["Content-Type: %s; charset=%s" % (mimetype, config.charset)])
 
     if mimetype == "text/html":
         request.theme.send_title(_('Full Link List for "%s"') % request.cfg.sitename)
--- a/MoinMoin/action/login.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/login.py	Fri Jul 28 17:03:03 2006 +0200
@@ -59,7 +59,7 @@
             return self.page.send_page(request, msg=error)
 
         else: # show login form
-            request.http_headers()
+            request.emit_http_headers()
             request.theme.send_title(_("Login"), pagename=self.pagename)
             # Start content (important for RTL support)
             request.write(request.formatter.startContent("content"))
--- a/MoinMoin/action/rss_rc.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/rss_rc.py	Fri Jul 28 17:03:03 2006 +0200
@@ -220,6 +220,6 @@
         httpheaders.append("Last-Modified: %s" % timefuncs.formathttpdate(lastmod))
 
     # send the generated XML document
-    request.http_headers(httpheaders)
+    request.emit_http_headers(httpheaders)
     request.write(out.getvalue())
 
--- a/MoinMoin/action/sitemap.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/sitemap.py	Fri Jul 28 17:03:03 2006 +0200
@@ -64,7 +64,7 @@
     request.user.datetime_fmt = datetime_fmt
     base = request.getBaseURL()
 
-    request.http_headers(["Content-Type: text/xml; charset=UTF-8"])
+    request.emit_http_headers(["Content-Type: text/xml; charset=UTF-8"])
 
     # we emit a piece of data so other side doesn't get bored:
     request.write("""<?xml version="1.0" encoding="UTF-8"?>\r\n""")
--- a/MoinMoin/action/test.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/test.py	Fri Jul 28 17:03:03 2006 +0200
@@ -105,7 +105,7 @@
     def do_action(self):
         """ run tests """
         request = self.request
-        request.http_headers(["Content-type: text/plain; charset=%s" % config.charset])
+        request.emit_http_headers(["Content-type: text/plain; charset=%s" % config.charset])
         request.write('MoinMoin Diagnosis\n======================\n\n')
         runTest(request)
         return True, ""
--- a/MoinMoin/action/thread_monitor.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/thread_monitor.py	Fri Jul 28 17:03:03 2006 +0200
@@ -27,11 +27,11 @@
     else:
         dump_fname = "nowhere"
 
-    request.http_headers()
+    request.emit_http_headers()
     request.write('<html><body>A dump has been saved to %s.</body></html>' % dump_fname)
 
 def execute_wiki(pagename, request):
-    request.http_headers()
+    request.emit_http_headers()
 
     request.theme.send_title("Thread monitor")
     request.write('<pre>')
--- a/MoinMoin/action/titleindex.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/action/titleindex.py	Fri Jul 28 17:03:03 2006 +0200
@@ -22,7 +22,7 @@
     else:
         mimetype = "text/plain"
 
-    request.http_headers(["Content-Type: %s; charset=%s" % (mimetype, config.charset)])
+    request.emit_http_headers(["Content-Type: %s; charset=%s" % (mimetype, config.charset)])
 
     # Get list of user readable pages
     pages = request.rootpage.getPageList()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/multiconfig.py	Fri Jul 28 17:03:03 2006 +0200
@@ -0,0 +1,28 @@
+""" This is just a dummy file to overwrite MoinMoin/multiconfig.py(c) from a
+    previous moin installation.
+
+    The file moved to MoinMoin/config/multiconfig.py and you have to fix your
+    imports as shown below.
+
+    Alternatively, you can temporarily set show_configuration_error = False,
+    so some compatibility code will get activated.
+    But this compatibility code will get removed soon, so you really should
+    update your config as soon as possible.
+"""
+show_configuration_error = True
+
+if show_configuration_error:
+    from MoinMoin.error import ConfigurationError
+    raise ConfigurationError("""\
+Please edit your wikiconfig/farmconfig and fix your DefaultConfig import:\r\n
+\r\n
+Old:   from MoinMoin.multiconfig import DefaultConfig\r\n
+New:   from MoinMoin.config.multiconfig import DefaultConfig\r\n
+\r\n
+If you can't do that, but if you can change the MoinMoin code, see the file
+MoinMoin/multiconfig.py for an alternative, but temporary workaround.
+""")
+
+else:
+    from MoinMoin.config.multiconfig import *
+
--- a/MoinMoin/parser/text_rst.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/parser/text_rst.py	Fri Jul 28 17:03:03 2006 +0200
@@ -381,8 +381,13 @@
                     node['classes'].append(prefix)
             else:
                 # Default case - make a link to a wiki page.
-                page = Page(self.request, refuri)
-                node['refuri'] = page.url(self.request)
+                pagename = refuri
+                anchor = ''
+                if refuri.find('#') != -1:
+                    pagename, anchor = refuri.split('#', 1)
+                    anchor = '#' + anchor
+                page = MoinMoin.Page.Page(self.request, pagename)
+                node['refuri'] = page.url(self.request) + anchor
                 if not page.exists():
                     node['classes'].append('nonexistent')
         html4css1.HTMLTranslator.visit_reference(self, node)
--- a/MoinMoin/request/CGI.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/request/CGI.py	Fri Jul 28 17:03:03 2006 +0200
@@ -63,33 +63,9 @@
             import errno
             if ex.errno != errno.EPIPE: raise
 
-    # Headers ----------------------------------------------------------
-
-    def http_headers(self, more_headers=[]):
-        # Send only once
-        if getattr(self, 'sent_headers', None):
-            return
-
-        self.sent_headers = 1
-        have_ct = 0
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        for header in headers:
+            self.write("%s\r\n" % header)
+        self.write("\r\n")
 
-        # send http headers
-        for header in more_headers + getattr(self, 'user_headers', []):
-            if header.lower().startswith("content-type:"):
-                # don't send content-type multiple times!
-                if have_ct: continue
-                have_ct = 1
-            if type(header) is unicode:
-                header = header.encode('ascii')
-            self.write("%s\r\n" % header)
-
-        if not have_ct:
-            self.write("Content-type: text/html;charset=%s\r\n" % config.charset)
-
-        self.write('\r\n')
-
-        #from pprint import pformat
-        #sys.stderr.write(pformat(more_headers))
-        #sys.stderr.write(pformat(self.user_headers))
-
-
--- a/MoinMoin/request/CLI.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/request/CLI.py	Fri Jul 28 17:03:03 2006 +0200
@@ -78,7 +78,8 @@
     def setHttpHeader(self, header):
         pass
 
-    def http_headers(self, more_headers=[]):
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
         pass
 
     def http_redirect(self, url):
--- a/MoinMoin/request/FCGI.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/request/FCGI.py	Fri Jul 28 17:03:03 2006 +0200
@@ -56,31 +56,9 @@
         RequestBase.finish(self)
         self.fcgreq.finish()
 
-    # Headers ----------------------------------------------------------
-
-    def http_headers(self, more_headers=[]):
-        """ Send out HTTP headers. Possibly set a default content-type. """
-        if getattr(self, 'sent_headers', None):
-            return
-        self.sent_headers = 1
-        have_ct = 0
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        for header in headers:
+            self.write("%s\r\n" % header)
+        self.write("\r\n")
 
-        # send http headers
-        for header in more_headers + getattr(self, 'user_headers', []):
-            if type(header) is unicode:
-                header = header.encode('ascii')
-            if header.lower().startswith("content-type:"):
-                # don't send content-type multiple times!
-                if have_ct: continue
-                have_ct = 1
-            self.write("%s\r\n" % header)
-
-        if not have_ct:
-            self.write("Content-type: text/html;charset=%s\r\n" % config.charset)
-
-        self.write('\r\n')
-
-        #from pprint import pformat
-        #sys.stderr.write(pformat(more_headers))
-        #sys.stderr.write(pformat(self.user_headers))
-
--- a/MoinMoin/request/MODPYTHON.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/request/MODPYTHON.py	Fri Jul 28 17:03:03 2006 +0200
@@ -138,51 +138,14 @@
         from mod_python import apache
         return apache.OK
 
-    # Headers ----------------------------------------------------------
-
-    def setHttpHeader(self, header):
-        """ Filters out content-type and status to set them directly
-            in the mod_python request. Rest is put into the headers_out
-            member of the mod_python request.
-
-            @param header: string, containing valid HTTP header.
-        """
-        if type(header) is unicode:
-            header = header.encode('ascii')
-        key, value = header.split(':', 1)
-        value = value.lstrip()
-        if key.lower() == 'content-type':
-            # save content-type for http_headers
-            if not self._have_ct:
-                # we only use the first content-type!
-                self.mpyreq.content_type = value
-                self._have_ct = 1
-        elif key.lower() == 'status':
-            # save status for finish
-            try:
-                self.mpyreq.status = int(value.split(' ', 1)[0])
-            except:
-                pass
-            else:
-                self._have_status = 1
-        else:
-            # this is a header we sent out
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        st_header, ct_header, other_headers = headers[0], headers[1], headers[2:]
+        status = st_header.split(':', 1)[1].lstrip()
+        self.mpyreq.status = int(status.split(' ', 1)[0])
+        self.mpyreq.content_type = ct_header.split(':', 1)[1].lstrip()
+        for header in other_headers:
             self.mpyreq.headers_out[key] = value
-
-    def http_headers(self, more_headers=[]):
-        """ Sends out headers and possibly sets default content-type
-            and status.
-
-            @param more_headers: list of strings, defaults to []
-        """
-        for header in more_headers + getattr(self, 'user_headers', []):
-            self.setHttpHeader(header)
-        # if we don't had an content-type header, set text/html
-        if self._have_ct == 0:
-            self.mpyreq.content_type = "text/html;charset=%s" % config.charset
-        # if we don't had a status header, set 200
-        if self._have_status == 0:
-            self.mpyreq.status = 200
         # this is for mod_python 2.7.X, for 3.X it's a NOP
         self.mpyreq.send_http_header()
 
--- a/MoinMoin/request/STANDALONE.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/request/STANDALONE.py	Fri Jul 28 17:03:03 2006 +0200
@@ -90,44 +90,16 @@
 
     # Headers ----------------------------------------------------------
 
-    def http_headers(self, more_headers=[]):
-        if getattr(self, 'sent_headers', None):
-            return
-
-        self.sent_headers = 1
-        user_headers = getattr(self, 'user_headers', [])
-
-        # check for status header and send it
-        our_status = 200
-        for header in more_headers + user_headers:
-            if header.lower().startswith("status:"):
-                try:
-                    our_status = int(header.split(':', 1)[1].strip().split(" ", 1)[0])
-                except:
-                    pass
-                # there should be only one!
-                break
-        # send response
-        self.sareq.send_response(our_status)
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        st_header, other_headers = headers[0], headers[1:]
+        status = st_header.split(':', 1)[1].lstrip()
+        status_code, status_msg = status.split(' ', 1)
+        status_code = int(status_code)
+        self.sareq.send_response(status_code, status_msg)
+        for header in other_headers:
+            key, value = header.split(':', 1)
+            value = value.lstrip()
+            self.sareq.send_header(key, value)
+        self.sareq.end_headers()
 
-        # send http headers
-        have_ct = 0
-        for header in more_headers + user_headers:
-            if type(header) is unicode:
-                header = header.encode('ascii')
-            if header.lower().startswith("content-type:"):
-                # don't send content-type multiple times!
-                if have_ct: continue
-                have_ct = 1
-
-            self.write("%s\r\n" % header)
-
-        if not have_ct:
-            self.write("Content-type: text/html;charset=%s\r\n" % config.charset)
-
-        self.write('\r\n')
-
-        #from pprint import pformat
-        #sys.stderr.write(pformat(more_headers))
-        #sys.stderr.write(pformat(self.user_headers))
-
--- a/MoinMoin/request/TWISTED.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/request/TWISTED.py	Fri Jul 28 17:03:03 2006 +0200
@@ -46,7 +46,7 @@
             RequestBase.__init__(self, properties)
 
         except MoinMoinFinish: # might be triggered by http_redirect
-            self.http_headers() # send headers (important for sending MOIN_ID cookie)
+            self.emit_http_headers() # send headers (important for sending MOIN_ID cookie)
             self.finish()
 
         except Exception, err:
@@ -110,34 +110,20 @@
 
     # Headers ----------------------------------------------------------
 
-    def __setHttpHeader(self, header):
-        if type(header) is unicode:
-            header = header.encode('ascii')
-        key, value = header.split(':', 1)
-        value = value.lstrip()
-        if key.lower() == 'set-cookie':
-            key, value = value.split('=', 1)
-            self.twistd.addCookie(key, value)
-        else:
-            self.twistd.setHeader(key, value)
-        #print "request.RequestTwisted.setHttpHeader: %s" % header
-
-    def http_headers(self, more_headers=[]):
-        if getattr(self, 'sent_headers', None):
-            return
-        self.sent_headers = 1
-        have_ct = 0
-
-        # set http headers
-        for header in more_headers + getattr(self, 'user_headers', []):
-            if header.lower().startswith("content-type:"):
-                # don't send content-type multiple times!
-                if have_ct: continue
-                have_ct = 1
-            self.__setHttpHeader(header)
-
-        if not have_ct:
-            self.__setHttpHeader("Content-type: text/html;charset=%s" % config.charset)
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        st_header, other_headers = headers[0], headers[1:]
+        status = st_header.split(':', 1)[1].lstrip()
+        status_code, status_msg = status.split(' ', 1)
+        self.twistd.setResponseCode(status_code, status_message)
+        for header in other_headers:
+            key, value = header.split(':', 1)
+            value = value.lstrip()
+            if key.lower() == 'set-cookie':
+                key, value = value.split('=', 1)
+                self.twistd.addCookie(key, value)
+            else:
+                self.twistd.setHeader(key, value)
 
     def http_redirect(self, url):
         """ Redirect to a fully qualified, or server-rooted URL 
@@ -151,6 +137,3 @@
         #self.twistd.finish()
         raise MoinMoinFinish
 
-    def setResponseCode(self, code, message=None):
-        self.twistd.setResponseCode(code, message)
-
--- a/MoinMoin/request/WSGI.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/request/WSGI.py	Fri Jul 28 17:03:03 2006 +0200
@@ -21,6 +21,7 @@
             self.stdin = env['wsgi.input']
             self.stdout = StringIO.StringIO()
 
+            # used by MoinMoin.server.wsgi:
             self.status = '200 OK'
             self.headers = []
 
@@ -48,33 +49,14 @@
     def reset_output(self):
         self.stdout = StringIO.StringIO()
 
-    def setHttpHeader(self, header):
-        if type(header) is unicode:
-            header = header.encode('ascii')
-
-        key, value = header.split(':', 1)
-        value = value.lstrip()
-        if key.lower() == 'content-type':
-            # save content-type for http_headers
-            if self.hasContentType:
-                # we only use the first content-type!
-                return
-            else:
-                self.hasContentType = True
-
-        elif key.lower() == 'status':
-            # save status for finish
-            self.status = value
-            return
-
-        self.headers.append((key, value))
-
-    def http_headers(self, more_headers=[]):
-        for header in more_headers:
-            self.setHttpHeader(header)
-
-        if not self.hasContentType:
-            self.headers.insert(0, ('Content-Type', 'text/html;charset=%s' % config.charset))
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        st_header, other_headers = headers[0], headers[1:]
+        self.status = st_header.split(':', 1)[1].lstrip()
+        for header in other_headers:
+            key, value = header.split(':', 1)
+            value = value.lstrip()
+            self.headers.append((key, value))
 
     def flush(self):
         pass
@@ -83,6 +65,7 @@
         pass
 
     def output(self):
+        # called by MoinMoin.server.wsgi
         return self.stdout.getvalue()
 
 
--- a/MoinMoin/request/__init__.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/request/__init__.py	Fri Jul 28 17:03:03 2006 +0200
@@ -9,7 +9,7 @@
 
 import os, re, time, sys, cgi, StringIO
 import copy
-from MoinMoin import config, wikiutil, user, caching
+from MoinMoin import config, wikiutil, user, caching, error
 from MoinMoin.util import IsWin9x
 
 
@@ -93,7 +93,6 @@
         # Pages meta data that we collect in one request
         self.pages = {}
 
-        self.sent_headers = 0
         self.user_headers = []
         self.cacheable = 0 # may this output get cached by http proxies/caches?
         self.page = None
@@ -969,13 +968,12 @@
         }
         headers = [
             'Status: %d %s' % (resultcode, statusmsg[resultcode]),
-            'Content-Type: text/plain'
+            'Content-Type: text/plain; charset=utf-8'
         ]
         # when surge protection triggered, tell bots to come back later...
         if resultcode == 503:
             headers.append('Retry-After: %d' % self.cfg.surge_lockout_time)
-        self.http_headers(headers)
-        self.setResponseCode(resultcode)
+        self.emit_http_headers(headers)
         self.write(msg)
         self.forbidden = True
 
@@ -1115,7 +1113,84 @@
         @param url: relative or absolute url, ascii using url encoding.
         """
         url = self.getQualifiedURL(url)
-        self.http_headers(["Status: 302 Found", "Location: %s" % url])
+        self.emit_http_headers(["Status: 302 Found", "Location: %s" % url])
+
+    def http_headers(self, more_headers=[]):
+        """ wrapper for old, deprecated http_headers call,
+            new code only calls emit_http_headers.
+            Remove in moin 1.7.
+        """
+        self.emit_http_headers(more_headers)
+
+    def emit_http_headers(self, more_headers=[]):
+        """ emit http headers after some preprocessing / checking
+
+            Makes sure we only emit headers once.
+            Encodes to ASCII if it gets unicode headers.
+            Make sure we have exactly one Content-Type and one Status header.
+            Make sure Status header string begins with a integer number.
+        
+            For emitting, it calls the server specific _emit_http_headers
+            method.
+
+            @param more_headers: list of additional header strings
+        """
+        headers = more_headers + getattr(self, 'user_headers', [])
+        self.user_headers = []
+
+        # Send headers only once
+        sent_headers = getattr(self, 'sent_headers', 0)
+        self.sent_headers = sent_headers + 1
+        if sent_headers:
+            raise error.InternalError("emit_http_headers called multiple times(%d)! Headers: %r" % (sent_headers, headers))
+        #else:
+        #    self.log("Notice: emit_http_headers called first time. Headers: %r" % headers)
+
+        content_type = None
+        status = None
+        headers = []
+        # assemble complete list of http headers
+        for header in headers:
+            if isinstance(header, unicode):
+                header = header.encode('ascii')
+            key, value = header.split(':', 1)
+            lkey = key.lower()
+            value = value.lstrip()
+            if content_type is None and lkey == "content-type":
+                content_type = value
+            elif status is None and lkey == "status":
+                status = value
+            else:
+                headers.append(header)
+
+        if content_type is None:
+            content_type = "text/html; charset=%s" % config.charset
+        ct_header = "Content-type: %s" % content_type
+
+        if status is None:
+            status = "200 OK"
+        try:
+            int(status.split(" ", 1)[0])
+        except:
+            self.log("emit_http_headers called with invalid header Status: %s" % status)
+            status = "500 Server Error - invalid status header"
+        st_header = "Status: %s" % status
+
+        headers = [st_header, ct_header] + headers # do NOT change order!
+        self._emit_http_headers(headers)
+
+        #from pprint import pformat
+        #sys.stderr.write(pformat(headers))
+
+    def _emit_http_headers(self, headers):
+        """ server specific method to emit http headers.
+        
+            @param headers: a list of http header strings in this FIXED order:
+                1. status header (always present and valid, e.g. "200 OK")
+                2. content type header (always present)
+                3. other headers (optional)
+        """
+        raise NotImplementedError
 
     def setHttpHeader(self, header):
         """ Save header for later send.
@@ -1126,6 +1201,9 @@
         self.user_headers.append(header)
 
     def setResponseCode(self, code, message=None):
+        """ DEPRECATED, will vanish in moin 1.7,
+            just use a Status: <code> <message> header and emit_http_headers.
+        """
         pass
 
     def fail(self, err):
@@ -1139,8 +1217,8 @@
         @param err: Exception instance or subclass.
         """
         self.failed = 1 # save state for self.run()            
-        self.http_headers(['Status: 500 MoinMoin Internal Error'])
-        self.setResponseCode(500)
+        self.emit_http_headers(['Status: 500 MoinMoin Internal Error'])
+        #self.setResponseCode(500)
         self.log('%s: %s' % (err.__class__.__name__, str(err)))
         from MoinMoin import failure
         failure.handle(self)
--- a/MoinMoin/stats/hitcounts.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/stats/hitcounts.py	Fri Jul 28 17:03:03 2006 +0200
@@ -243,12 +243,11 @@
         (request.cfg.chart_options['width'], request.cfg.chart_options['height']),
         image, days)
 
-    # send HTTP headers
     headers = [
         "Content-Type: image/gif",
         "Content-Length: %d" % len(image.getvalue()),
     ]
-    request.http_headers(headers)
+    request.emit_http_headers(headers)
 
     # copy the image
     image.reset()
--- a/MoinMoin/stats/pagesize.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/stats/pagesize.py	Fri Jul 28 17:03:03 2006 +0200
@@ -114,12 +114,11 @@
         (request.cfg.chart_options['width'], request.cfg.chart_options['height']),
         image, labels)
 
-    # send HTTP headers
     headers = [
         "Content-Type: image/gif",
         "Content-Length: %d" % len(image.getvalue()),
     ]
-    request.http_headers(headers)
+    request.emit_http_headers(headers)
 
     # copy the image
     image.reset()
--- a/MoinMoin/stats/useragents.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/stats/useragents.py	Fri Jul 28 17:03:03 2006 +0200
@@ -172,12 +172,11 @@
         (request.cfg.chart_options['width'], request.cfg.chart_options['height']),
         image, labels)
 
-    # send HTTP headers
     headers = [
         "Content-Type: image/gif",
         "Content-Length: %d" % len(image.getvalue()),
     ]
-    request.http_headers(headers)
+    request.emit_http_headers(headers)
 
     # copy the image
     image.reset()
--- a/MoinMoin/xmlrpc/__init__.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/MoinMoin/xmlrpc/__init__.py	Fri Jul 28 17:03:03 2006 +0200
@@ -59,7 +59,7 @@
         @rtype: str
         @return: string in config.charset
         """
-        raise "NotImplementedError"
+        raise NotImplementedError("please implement _instr in derived class")
 
     def _outstr(self, text):
         """ Convert outbound string to utf-8.
@@ -68,7 +68,7 @@
         @rtype: str
         @return: string in utf-8
         """
-        raise "NotImplementedError"
+        raise NotImplementedError("please implement _outstr in derived class")
 
     def _inlob(self, text):
         """ Convert inbound base64-encoded utf-8 to Large OBject.
@@ -131,8 +131,8 @@
             # serialize it
             response = xmlrpclib.dumps(response, methodresponse=1)
 
-        self.request.http_headers([
-            "Content-Type: text/xml;charset=utf-8",
+        self.request.emit_http_headers([
+            "Content-Type: text/xml; charset=utf-8",
             "Content-Length: %d" % len(response),
         ])
         self.request.write(response)
--- a/docs/CHANGES	Fri Jul 28 17:02:13 2006 +0200
+++ b/docs/CHANGES	Fri Jul 28 17:03:03 2006 +0200
@@ -81,11 +81,11 @@
       will be missing and the adaptor script will need a change maybe):
       CGI works
       CLI works
-      STANDALONE not
-      MODPY not
-      WSGI not
-      FCGI not
-      TWISTED not
+      STANDALONE ?
+      MODPY ?
+      WSGI ?
+      FCGI ?
+      TWISTED ?
     * moved util/antispam.py to security/antispam.py,
       moved util/autoadmin.py to security/autoadmin.py,
       moved security.py to security/__init__.py,
@@ -119,6 +119,24 @@
       TODO: write mig script for data_dir
       TODO: make blanks in interwiki pagelinks possible
     * request.action now has the action requested, default: 'show'.
+    * Cleaned up duplicated http_headers code and DEPRECATED this function
+      call (it was sometimes confused with setHttpHeaders call) - it will
+      vanish with moin 1.7, so please fix your custom plugins!
+      The replacement is:
+          request.emit_http_headers(more_headers=[])
+      This call pre-processes the headers list (encoding from unicode, making
+      sure that there is exactly ONE content-type header, etc.) and then
+      calls a server specific helper _emit_http_headers to emit it.
+      CGI works
+      CLI ?
+      STANDALONE ?
+      MODPY ?
+      WSGI ?
+      FCGI ?
+      TWISTED ?
+    * setResponseCode request method DEPRECATED (it only worked for Twisted
+      anyway), just use emit_http_headers and include a Status: XXX header.
+      Method will vanish with moin 1.7. 
 
   New Features:
     * Removed "underscore in URL" == "blank in pagename magic" - it made more
@@ -165,7 +183,7 @@
     * The i18n system no loads *.po files directly (no *.py or *.mo any more)
       and caches the results (farm wide cache/i18n/*).
     * added the diff parser from ParserMarket, thanks to Emilio Lopes, Fabien
-      Ninoles and Jürgen Hermann.
+      Ninoles and Jrgen Hermann.
 
   Bugfixes:
     * on action "info" page, "revert" link will not be displayed for empty page
@@ -186,6 +204,7 @@
     * Added a (less broken) MoinMoin.support.difflib, details see there.
     * BadContent and LocalBadContent now get noindex,nofollow robots header,
       same as POSTs.
+    * Fixed handling of anchors in wiki links for the Restructured text parser.
 
   Other changes:
     * we use (again) the same browser compatibility check as FCKeditor uses
@@ -194,7 +213,8 @@
       at FCKeditor development or browser development.
     * HINT: instead of "from MoinMoin.multiconfig import DefaultConfig" you
       need to use "from MoinMoin.config.multiconfig import DefaultConfig" now.
-      You need to change this in you wikiconfig.py or farmconfig.py file.
+      You need to change this in your wikiconfig.py or farmconfig.py file.
+      See MoinMoin/multiconfig.py for an alternative way if you can't do that.
 
 Version 1.5.4-current:
     * increased maxlength of some input fields from 80 to 200
@@ -859,7 +879,7 @@
 
   International support:    
     * mail_from can be now a unicode name-address 
-      e.g u'Jürgen wiki <noreply@jhwiki.org>'
+      e.g u'Jrgen wiki <noreply@jhwiki.org>'
 
   Theme changes:
     * logo_string is now should be really only the logo (img).
@@ -2352,8 +2372,7 @@
         || {{{ ;)) }}} || ;)) || lol.gif    ||
     * AbandonedPages macro
     * Added definition list markup: {{{<whitespace>term:: definition}}}
-    * Added email notification features contributed by Daniel Saß
-    * SystemInfo: show "Entries in edit log"
+    * Added email notification features contributed by Daniel Sa�    * SystemInfo: show "Entries in edit log"
     * Added "RSS" icon to RecentChanges macro and code to generate a
       RecentChanges RSS channel, see
           http://www.usemod.com/cgi-bin/mb.pl?UnifiedRecentChanges
@@ -2626,7 +2645,7 @@
       there before a new version is written to disk
     * Removed the "Reset" button from EditPage
     * Added "Reduce editor size" link
-    * Added Latin-1 WikiNames (JürgenHermann ;)
+    * Added Latin-1 WikiNames (JrgenHermann ;)
     * Speeded up RecentChanges by looking up hostnames ONCE while saving
     * Show at most 14 (distinct) days in RecentChanges
     * Added icons for common functions, at the top of the page
@@ -2639,7 +2658,7 @@
     * Grey background for code sections
     * Added handling for numbered lists
     * the edit textarea now grows in width with the browser window
-      (thanks to Sebastian Dauß for that idea)
+      (thanks to Sebastian Dau�for that idea)
     * Added page info (revision history) and viewing of old revisions
     * Added page diff, and diff links on page info
     * Added InterWiki support (use "wiki:WikiServer/theirlocalname"; the list
--- a/setup.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/setup.py	Fri Jul 28 17:03:03 2006 +0200
@@ -219,6 +219,7 @@
         'MoinMoin',
         'MoinMoin.action',
         'MoinMoin.auth',
+        'MoinMoin.config',
         'MoinMoin.converter',
         'MoinMoin.filter',
         'MoinMoin.formatter',
--- a/wikiconfig.py	Fri Jul 28 17:02:13 2006 +0200
+++ b/wikiconfig.py	Fri Jul 28 17:03:03 2006 +0200
@@ -6,7 +6,7 @@
 @license: GNU GPL, see COPYING for details.
 """
 
-from MoinMoin.multiconfig import DefaultConfig
+from MoinMoin.config.multiconfig import DefaultConfig
 
 
 class Config(DefaultConfig):