Commits

Roger Haase committed e22169d Merge

merge

Comments (0)

Files changed (202)

 ^instance/
 ^wikiconfig_.+\.py
 ^MoinMoin/translations/.*/LC_MESSAGES/messages.mo$
+^MoinMoin/_tests/wiki/index/
 ^docs/_build/
 .coverage
 ^.project

MoinMoin/_tests/__init__.py

 """
 
 
-import os, shutil
-import socket, errno
+import socket
 from StringIO import StringIO
 
-from flask import current_app as app
 from flask import g as flaskg
 
-from MoinMoin import config, security, user
-from MoinMoin.config import NAME, CONTENTTYPE
+from MoinMoin.constants.contenttypes import CHARSET
+from MoinMoin.constants.keys import NAME, CONTENTTYPE
 from MoinMoin.items import Item
 from MoinMoin.util.crypto import random_string
-from MoinMoin.storage.error import ItemAlreadyExistsError
 
 # Promoting the test user -------------------------------------------
 # Usually the tests run as anonymous user, but for some stuff, you
 # need more privs...
 
+
 def become_valid(username=u"ValidUser"):
     """ modify flaskg.user to make the user valid.
         Note that a valid user will only be in ACL special group "Known", if
         Thus, for testing purposes (e.g. if you need delete rights), it is
         easier to use become_trusted().
     """
-    flaskg.user.profile[NAME] = username
-    flaskg.user.may.name = username
+    flaskg.user.profile[NAME] = [username, ]
+    flaskg.user.may.names = [username, ]  # see security.DefaultSecurityPolicy class
     flaskg.user.valid = 1
 
 
 def update_item(name, meta, data):
     """ creates or updates an item  """
     if isinstance(data, unicode):
-        data = data.encode(config.charset)
+        data = data.encode(CHARSET)
     item = flaskg.storage[name]
 
     meta = meta.copy()
     if NAME not in meta:
-        meta[NAME] = name
+        meta[NAME] = [name, ]
     if CONTENTTYPE not in meta:
         meta[CONTENTTYPE] = u'application/octet-stream'
     rev = item.store_revision(meta, StringIO(data), return_rev=True)
     return rev
 
+
 def create_random_string_list(length=14, count=10):
     """ creates a list of random strings """
     chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
     return [u"{0}".format(random_string(length, chars)) for counter in range(count)]
 
+
 def nuke_item(name):
     """ complete destroys an item """
     item = Item.create(name)

MoinMoin/_tests/_test_template.py

     )
 
     from MoinMoin._tests import wikiconfig
+
     class Config(wikiconfig.Config):
         foo = 'bar'  # we want to have this non-default setting
 

MoinMoin/_tests/ldap_testbase.py

 SLAPD_EXECUTABLE = 'slapd'  # filename of LDAP server executable - if it is not
                             # in your PATH, you have to give full path/filename.
 
-import os, shutil, tempfile, time, base64
+import os
+import shutil
+import tempfile
+import time
+import base64
 from StringIO import StringIO
 import signal
 import subprocess
     def __init__(self,
                  config=None,  # config filename for -f
                  executable=SLAPD_EXECUTABLE,
-                 debug_flags='', # None,  # for -d stats,acl,args,trace,sync,config
+                 debug_flags='',  # None,  # for -d stats,acl,args,trace,sync,config
                  proto='ldap', ip='127.0.0.1', port=3890,  # use -h proto://ip:port
                  service_name=''  # defaults to -n executable:port, use None to not use -n
                 ):
         self.proto = proto
         self.ip = ip
         self.port = port
-        self.url = '{0}://{1}:{2}'.format(proto, ip, port) # can be used for ldap.initialize() call
+        self.url = '{0}://{1}:{2}'.format(proto, ip, port)  # can be used for ldap.initialize() call
         if service_name == '':
             self.service_name = '{0}:{1}'.format(executable, port)
         else:
         started = None
         if timeout:
             lo = ldap.initialize(self.url)
-            ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) # ldap v2 is outdated
+            ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3)  # ldap v2 is outdated
             started = False
             wait_until = time.time() + timeout
             while time.time() < wait_until:
 
     def start_slapd(self):
         """ start a slapd and optionally wait until it talks with us """
-        self.slapd = Slapd(config=self.slapd_conf, port=3890+self.instance)
+        self.slapd = Slapd(config=self.slapd_conf, port=3890 + self.instance)
         started = self.slapd.start(timeout=self.timeout)
         return started
 
     def load_directory(self, ldif_content):
         """ load the directory with the ldif_content (str) """
         lo = ldap.initialize(self.slapd.url)
-        ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) # ldap v2 is outdated
+        ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3)  # ldap v2 is outdated
         lo.simple_bind_s(self.rootdn, self.rootpw)
 
         class LDIFLoader(ldif.LDIFParser):

MoinMoin/_tests/test_error.py

 """
 
 
-import pytest
-
 from MoinMoin import error
 
 

MoinMoin/_tests/test_test_environ.py

 
 from StringIO import StringIO
 
-import pytest
-
 from flask import current_app as app
 from flask import g as flaskg
 
-from MoinMoin.conftest import init_test_app, deinit_test_app
-from MoinMoin.config import NAME, CURRENT, CONTENTTYPE, IS_SYSITEM, SYSITEM_VERSION
-from MoinMoin.storage.error import NoSuchItemError
+from MoinMoin.constants.keys import NAME, CONTENTTYPE
 
 from MoinMoin._tests import wikiconfig
 
         itemname = u"this item shouldn't exist yet"
         assert not storage.has_item(itemname)
         item = storage[itemname]
-        new_rev = item.store_revision({NAME: itemname, CONTENTTYPE: u'text/plain'}, StringIO(''))
+        new_rev = item.store_revision({NAME: [itemname, ], CONTENTTYPE: u'text/plain'}, StringIO(''))
         assert storage.has_item(itemname)
 
 

MoinMoin/_tests/test_user.py

 """
 
 
-import pytest
-
-from flask import current_app as app
 from flask import g as flaskg
 
 from MoinMoin import user
-from MoinMoin.util import crypto
 
 
 class TestSimple(object):
         email = u"foo@example.org"
         # nonexisting user
         u = user.User(name=name, password=password)
-        assert u.name == name
+        assert u.name == [name, ]
         assert not u.valid
         assert not u.exists()
         # create a user
         assert ret is None, "create_user returned: {0}".format(ret)
         # existing user
         u = user.User(name=name, password=password)
-        assert u.name == name
+        assert u.name == [name, ]
         assert u.email == email
         assert u.valid
         assert u.exists()

MoinMoin/_tests/test_wikiutil.py

 
 from flask import current_app as app
 
-from MoinMoin import config, wikiutil
-from MoinMoin._tests import wikiconfig
+from MoinMoin.constants.chartypes import CHARS_SPACES
+from MoinMoin import wikiutil
 
 from werkzeug import MultiDict
 
             (u'a     b     c', u'a b c'),
             (u'a   b  /  c    d  /  e   f', u'a b/c d/e f'),
             # All 30 unicode spaces
-            (config.chars_spaces, u''),
+            (CHARS_SPACES, u''),
             )
         for test, expected in cases:
             result = wikiutil.normalize_pagename(test, app.cfg)
     assert result == expected
 
 def testdrawing2fname():
-    # with extension not in config.drawing_extensions
+    # with extension not in DRAWING_EXTENSIONS
     result = wikiutil.drawing2fname('Moin_drawing.txt')
     expected = 'Moin_drawing.txt.tdraw'
     assert result == expected
-    # with extension in config.drawing_extensions
+    # with extension in DRAWING_EXTENSIONS
     result = wikiutil.drawing2fname('Moindir.Moin_drawing.jpg')
     expected = 'Moindir.Moin_drawing.jpg'
     assert result == expected

MoinMoin/_tests/wikiconfig.py

 from os.path import abspath, dirname, join
 from MoinMoin.config.default import DefaultConfig
 
+
 class Config(DefaultConfig):
     _here = abspath(dirname(__file__))
     _root = abspath(join(_here, '..', '..'))
-    data_dir = join(_here, 'wiki', 'data') # needed for plugins package TODO
+    data_dir = join(_here, 'wiki', 'data')  # needed for plugins package TODO
     index_storage = 'FileStorage', (join(_here, 'wiki', 'index'), ), {}
     content_acl = None
     item_root = 'FrontPage'
 
             Rule('/<itemname:wikipage>')
             Rule('/<itemname:wikipage>/edit')
-
-        :param map: the :class:`Map`.
         """
         regex = '[^/]+?(/[^/]+?)*'
         weight = 200
     # A ns_mapping consists of several lines, where each line is made up like this:
     # mountpoint, unprotected backend
     # Just initialize with unprotected backends.
-    app.router = routing.Backend(app.cfg.namespace_mapping)
+    app.router = routing.Backend(app.cfg.namespace_mapping, app.cfg.backend_mapping)
     if app.cfg.create_storage:
         app.router.create()
     app.router.open()
 
     # if we still have no user obj, create a dummy:
     if not userobj:
-        userobj = user.User(auth_method='invalid')
+        userobj = user.User(name=u'anonymous', auth_method='invalid')
     # if we have a valid user we store it in the session
     if userobj.valid:
         session['user.itemid'] = userobj.itemid

MoinMoin/apps/admin/templates/admin/userbrowser.html

     </tr>
     {% for u in user_accounts %}
     <tr>
-        <td><a href="{{ url_for('frontend.show_item', item_name=u.name) }}">{{ u.name }}</a>{{ u.disabled and " (%s)" % _("disabled") or ""}}</td>
+        <td><a href="{{ url_for('frontend.show_item', item_name=u.name[0]) }}">{{ u.name|join(',') }}</a>{{ u.disabled and " (%s)" % _("disabled") or ""}}</td>
         <td>{{ u.groups|join(',') }}</td>
         <td>
             {% if u.email %}

MoinMoin/apps/admin/templates/admin/wikiconfig.html

 </td>
 </tr>
 {% endfor %}
-</tdbody>
+</tbody>
 </table>
 {% endblock %}

MoinMoin/apps/admin/views.py

 from MoinMoin.themes import render_template
 from MoinMoin.apps.admin import admin
 from MoinMoin import user
-from MoinMoin.storage.error import NoSuchRevisionError
-from MoinMoin.config import NAME, ITEMID, SIZE, EMAIL
-from MoinMoin.config import SUPERUSER
+from MoinMoin.constants.keys import NAME, ITEMID, SIZE, EMAIL
+from MoinMoin.constants.rights import SUPERUSER
 from MoinMoin.security import require_permission
 
+
 @admin.route('/superuser')
 @require_permission(SUPERUSER)
 def index():
     return render_template('admin/index.html', title_name=_(u"Admin"))
 
+
 @admin.route('/user')
 def index_user():
     return render_template('user/index_user.html', title_name=_(u"User"))
     User Account Browser
     """
     groups = flaskg.groups
-    revs = user.search_users() # all users
+    revs = user.search_users()  # all users
     user_accounts = [dict(uid=rev.meta[ITEMID],
                           name=rev.meta[NAME],
                           email=rev.meta[EMAIL],
 @admin.route('/sysitems_upgrade', methods=['GET', 'POST', ])
 @require_permission(SUPERUSER)
 def sysitems_upgrade():
-    from MoinMoin.storage.backends import upgrade_sysitems
+    from MoinMoin.storage.backends import upgrade_sysitems  # XXX broken import, either fix or kill this
     from MoinMoin.storage.error import BackendError
     if request.method == 'GET':
         action = 'syspages_upgrade'
 
 from MoinMoin.config import default as defaultconfig
 
+
 @admin.route('/wikiconfig', methods=['GET', ])
 @require_permission(SUPERUSER)
 def wikiconfig():
     headings = [_('Size'),
                 _('Item name'),
                ]
-    rows = [(rev.meta[SIZE], rev.meta[NAME])
+    rows = [(rev.meta[SIZE], rev.name)
             for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname)]
     rows = sorted(rows, reverse=True)
     return render_template('user/itemsize.html',

MoinMoin/apps/feed/_tests/test_feed.py

 
 from flask import url_for
 
-from MoinMoin.items import Item
-from MoinMoin.config import CONTENTTYPE, COMMENT
+from MoinMoin.constants.keys import COMMENT
 from MoinMoin._tests import update_item, wikiconfig
 
 class TestFeeds(object):

MoinMoin/apps/feed/views.py

 from flask import request, Response
 from flask import current_app as app
 from flask import g as flaskg
-from flask import url_for
 
 from werkzeug.contrib.atom import AtomFeed
 from jinja2 import Markup
 
 from MoinMoin.i18n import _, L_, N_
 from MoinMoin.apps.feed import feed
-from MoinMoin.config import (NAME, NAME_EXACT, WIKINAME, ACL, ACTION, ADDRESS,
-                            HOSTNAME, USERID, COMMENT, MTIME, REVID, ALL_REVS,
-                            PARENTID, LATEST_REVS)
+from MoinMoin.constants.keys import NAME, NAME_EXACT, WIKINAME, COMMENT, MTIME, REVID, ALL_REVS, PARENTID, LATEST_REVS
 from MoinMoin.themes import get_editor_info, render_template
 from MoinMoin.items import Item
 from MoinMoin.util.crypto import cache_key
 from MoinMoin.util.interwiki import url_for_item
 
+
 @feed.route('/atom/<itemname:item_name>')
 @feed.route('/atom', defaults=dict(item_name=''))
 def atom(item_name):
             query = And([query, Term(NAME_EXACT, item_name), ])
         history = flaskg.storage.search(query, idx_name=ALL_REVS, sortedby=[MTIME], reverse=True, limit=100)
         for rev in history:
-            name = rev.meta[NAME]
+            name = rev.name
             item = rev.item
             this_revid = rev.meta[REVID]
             previous_revid = rev.meta.get(PARENTID)
                     content = hl_item.content._render_data_diff_atom(previous_rev, this_rev)
                 else:
                     # full html rendering for new items
-                    content = render_template('atom.html', get='first_revision', rev=this_rev, content=Markup(hl_item.content._render_data()), revision=this_revid)
+                    content = render_template('atom.html', get='first_revision', rev=this_rev,
+                                              content=Markup(hl_item.content._render_data()), revision=this_revid)
                 content_type = 'html'
             except Exception as e:
                 logging.exception("content rendering crashed")
             if rev_comment:
                 # Trim down extremely long revision comment
                 if len(rev_comment) > 80:
-                    content = render_template('atom.html', get='comment_cont_merge', comment=rev_comment[79:], content=Markup(content))
+                    content = render_template('atom.html', get='comment_cont_merge', comment=rev_comment[79:],
+                                              content=Markup(content))
                     rev_comment = u"{0}...".format(rev_comment[:79])
                 feed_title = u"{0} - {1}".format(author.get(NAME, ''), rev_comment)
             else:

MoinMoin/apps/frontend/_tests/test_frontend.py

 
 from StringIO import StringIO
 
-import pytest
-
 from flask import url_for
 from flask import g as flaskg
 from werkzeug import ImmutableMultiDict, FileStorage
 
 from MoinMoin.apps.frontend import views
 from MoinMoin import user
-from MoinMoin.util import crypto
-from MoinMoin._tests import wikiconfig
 
 
 class TestFrontend(object):
             viewopts = {}
         if params is None:
             params = {}
-        print 'GET %s' % url_for(viewname, **viewopts)
+
         with self.app.test_client() as c:
-            rv = c.get(url_for(viewname, **viewopts), data=params)
-            assert rv.status == status
-            assert rv.headers['Content-Type'] in content_types
-            for item in data:
-                assert item in rv.data
-            return rv
+            for method in ['HEAD', 'GET']:
+                print '%s %s' % (method, url_for(viewname, **viewopts))
+                rv = c.open(url_for(viewname, **viewopts), method=method, data=params)
+                assert rv.status == status
+                assert rv.headers['Content-Type'] in content_types
+                if method == 'GET':
+                    for item in data:
+                        assert item in rv.data
+        return rv
 
     def _test_view_post(self, viewname, status='302 FOUND', content_types=('text/html; charset=utf-8', ), data=('<html>', '</html>'), form=None, viewopts=None):
         if viewopts is None:

MoinMoin/apps/frontend/views.py

 import mimetypes
 import json
 from datetime import datetime
-from itertools import chain
 from collections import namedtuple
 from functools import wraps, partial
 
-from flask import request, url_for, flash, Response, make_response, redirect, session, abort, jsonify
+from flask import request, url_for, flash, Response, make_response, redirect, abort, jsonify
 from flask import current_app as app
 from flask import g as flaskg
 from flask.ext.babel import format_date
 from flask.ext.themes import get_themes_list
 
-from flatland import Form, Enum
+from flatland import Form, Enum, List
 from flatland.validation import Validator
 
 from jinja2 import Markup
 logging = log.getLogger(__name__)
 
 from MoinMoin.i18n import _, L_, N_
-from MoinMoin.themes import render_template, get_editor_info, contenttype_to_class
+from MoinMoin.themes import render_template, contenttype_to_class
 from MoinMoin.apps.frontend import frontend
-from MoinMoin.forms import OptionalText, RequiredText, URL, YourOpenID, YourEmail, RequiredPassword, Checkbox, InlineCheckbox, Select, Tags, Natural, Submit, Hidden, MultiSelect
+from MoinMoin.forms import (OptionalText, RequiredText, URL, YourOpenID, YourEmail, RequiredPassword, Checkbox,
+                            InlineCheckbox, Select, Names, Tags, Natural, Submit, Hidden, MultiSelect)
 from MoinMoin.items import BaseChangeForm, Item, NonExistent
 from MoinMoin.items.content import content_registry
-from MoinMoin import config, user, util
+from MoinMoin import user, util
 from MoinMoin.constants.keys import *
+from MoinMoin.constants.chartypes import CHARS_UPPER, CHARS_LOWER
 from MoinMoin.util import crypto
 from MoinMoin.util.interwiki import url_for_item
-from MoinMoin.search import SearchForm, ValidSearch
-from MoinMoin.security.textcha import TextCha, TextChaizedForm, TextChaValid
-from MoinMoin.storage.error import NoSuchItemError, NoSuchRevisionError
+from MoinMoin.search import SearchForm
+from MoinMoin.security.textcha import TextCha, TextChaizedForm
 from MoinMoin.signalling import item_displayed, item_modified
 from MoinMoin.storage.middleware.protecting import AccessDenied
 
     item_name = app.cfg.item_root
     return redirect(url_for_item(item_name))
 
+
 @frontend.route('/robots.txt')
 def robots():
     return Response("""\
     # TAGS might be there multiple times, thus we need multi:
     lookup_form = LookupForm.from_flat(request.values.items(multi=True))
     valid = lookup_form.validate()
-    lookup_form['submit'].set_default() # XXX from_flat() kills all values
+    lookup_form['submit'].set_default()  # XXX from_flat() kills all values
     if valid:
         history = bool(request.values.get('history'))
         idx_name = ALL_REVS if history else LATEST_REVS
             transcluded_names.update(transclusions)
         return transcluded_names
 
+
 @frontend.route('/+search/<itemname:item_name>', methods=['GET', 'POST'])
 @frontend.route('/+search', defaults=dict(item_name=u''), methods=['GET', 'POST'])
 def search(item_name):
     search_form = SearchForm.from_flat(request.values)
     valid = search_form.validate()
-    search_form['submit'].set_default() # XXX from_flat() kills all values
+    search_form['submit'].set_default()  # XXX from_flat() kills all values
     query = search_form['q'].value
     if valid:
         history = bool(request.values.get('history'))
         q = qp.parse(query)
 
         _filter = None
-        if item_name: # Only search this item and subitems
+        if item_name:  # Only search this item and subitems
             prefix_name = item_name + u'/'
             terms = [Term(NAME_EXACT, item_name), Prefix(NAME_EXACT, prefix_name), ]
 
             key_terms_is_fast = False
             if key_terms_is_fast:
                 flaskg.clock.start('search suggestions')
-                name_suggestions = u', '.join([word for word, score in results.key_terms(NAME, docs=20, numterms=10)])
-                content_suggestions = u', '.join([word for word, score in results.key_terms(CONTENT, docs=20, numterms=10)])
+                name_suggestions = u', '.join([word
+                                               for word, score in results.key_terms(NAME, docs=20, numterms=10)])
+                content_suggestions = u', '.join([word
+                                                  for word, score in results.key_terms(CONTENT, docs=20, numterms=10)])
                 flaskg.clock.stop('search suggestions')
             else:
                 name_suggestions = u''
         rev = item[rev]
     except KeyError:
         abort(404, item_name)
-    content = convert_to_indexable(rev.meta, rev.data)
+    content = convert_to_indexable(rev.meta, rev.data, item_name)
     return Response(content, 200, mimetype='text/plain')
 
 
 @presenter('meta', add_trail=True)
 def show_item_meta(item):
     show_revision = request.view_args['rev'] != CURRENT
-    show_navigation = False # TODO
+    show_navigation = False  # TODO
     first_rev = None
     last_rev = None
     if show_navigation:
                            show_navigation=show_navigation,
                           )
 
+
 @frontend.route('/+content/+<rev>/<itemname:item_name>')
 @frontend.route('/+content/<itemname:item_name>', defaults=dict(rev=CURRENT))
 def content_item(item_name, rev):
     """ same as show_item, but we only show the content """
-    # first check whether we have a valid search query:
-    search_form = SearchForm.from_flat(request.values)
-    if search_form.validate():
-        return _search(search_form, item_name)
-    search_form['submit'].set_default() # XXX from_flat() kills all values
     item_displayed.send(app._get_current_object(),
                         item_name=item_name)
     try:
                            data_rendered=Markup(item.content._render_data()),
                            )
 
+
 @presenter('get')
 def get_item(item):
     return item.content.do_get()
 
+
 @presenter('download')
 def download_item(item):
     mimetype = request.values.get("mimetype")
     return item.content.do_get(force_attachment=True, mimetype=mimetype)
 
+
 @frontend.route('/+convert/<itemname:item_name>')
 def convert_item(item_name):
     """
 class TargetChangeForm(BaseChangeForm):
     target = RequiredText.using(label=L_('Target')).with_properties(placeholder=L_("The name of the target item"))
 
+
 class RevertItemForm(BaseChangeForm):
     name = 'revert_item'
 
+
 class DeleteItemForm(BaseChangeForm):
     name = 'delete_item'
 
+
 class DestroyItemForm(BaseChangeForm):
     name = 'destroy_item'
 
+
 class RenameItemForm(TargetChangeForm):
     name = 'rename_item'
 
         abort(403)
     if isinstance(item, NonExistent):
         abort(404, item_name)
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = RevertItemForm.from_defaults()
         TextCha(form).amend_form()
     elif request.method == 'POST':
         abort(403)
     if isinstance(item, NonExistent):
         abort(404, item_name)
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = RenameItemForm.from_defaults()
         TextCha(form).amend_form()
         form['target'] = item.name
         abort(403)
     if isinstance(item, NonExistent):
         abort(404, item_name)
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = DeleteItemForm.from_defaults()
         TextCha(form).amend_form()
     elif request.method == 'POST':
                            form=form,
                           )
 
+
 @frontend.route('/+ajaxdelete/<itemname:item_name>', methods=['POST'])
 @frontend.route('/+ajaxdelete', defaults=dict(item_name=''), methods=['POST'])
 def ajaxdelete(item_name):
 
     return jsonify(response)
 
+
 @frontend.route('/+ajaxdestroy/<itemname:item_name>', methods=['POST'])
 @frontend.route('/+ajaxdestroy', defaults=dict(item_name=''), methods=['POST'])
 def ajaxdestroy(item_name):
 def destroy_item(item_name, rev):
     if rev is None:
         # no revision given
-        _rev = CURRENT # for item creation
+        _rev = CURRENT  # for item creation
         destroy_item = True
     else:
         _rev = rev
         abort(403)
     if isinstance(item, NonExistent):
         abort(404, item_name)
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = DestroyItemForm.from_defaults()
         TextCha(form).amend_form()
     elif request.method == 'POST':
     """
     data_file = request.files.get('data_file')
     subitem_name = data_file.filename
-    contenttype = data_file.content_type # guess by browser, based on file name
+    contenttype = data_file.content_type  # guess by browser, based on file name
     data = data_file.stream
     if item_name:
         subitem_prefix = item_name + u'/'
     contenttype_group_descriptions[g] = ', '.join([e.display_name for e in content_registry.groups[g]])
 contenttype_groups.append('unknown items')
 
-ContenttypeGroup = MultiSelect.of(Enum.using(valid_values=contenttype_groups).with_properties(descriptions=contenttype_group_descriptions)).using(optional=True)
+ContenttypeGroup = MultiSelect.of(Enum.using(valid_values=contenttype_groups).with_properties(
+                                  descriptions=contenttype_group_descriptions)).using(optional=True)
+
 
 class IndexForm(Form):
     contenttype = ContenttypeGroup
     submit = Submit.using(default=L_('Filter'))
 
+
 @frontend.route('/+index/', defaults=dict(item_name=''), methods=['GET', 'POST'])
 @frontend.route('/+index/<itemname:item_name>', methods=['GET', 'POST'])
 def index(item_name):
     try:
-        item = Item.create(item_name) # when item_name='', it gives toplevel index
+        item = Item.create(item_name)  # when item_name='', it gives toplevel index
     except AccessDenied:
         abort(403)
 
     # values, eg. calling items with multi=True. See Werkzeug documentation for
     # more.
     form = IndexForm.from_flat(request.args.items(multi=True))
-    form['submit'].set_default() # XXX from_flat() kills all values
+    form['submit'].set_default()  # XXX from_flat() kills all values
     if not form['contenttype']:
         form['contenttype'].set(contenttype_groups)
 
     selected_groups = form['contenttype'].value
     startswith = request.values.get("startswith")
 
-    initials = item.name_initial(item.get_subitem_revs())
-    initials = [initial.upper() for initial in initials]
-    initials = list(set(initials))
-    initials = sorted(initials)
+    initials = item.name_initial(item.get_subitem_revs(), uppercase=True)
 
     dirs, files = item.get_index(startswith, selected_groups)
     # index = sorted(index, key=lambda e: e.relname.lower())
     q = And([Term(WIKINAME, app.cfg.interwikiname),
              Term(USERID, userid)])
     revs = flaskg.storage.search(q, idx_name=ALL_REVS)
-    return [rev.meta[NAME] for rev in revs]
+    return [rev.name for rev in revs]
 
 
 @frontend.route('/+backrefs/<itemname:item_name>')
     q = And([Term(WIKINAME, app.cfg.interwikiname),
              Or([Term(ITEMTRANSCLUSIONS, item_name), Term(ITEMLINKS, item_name)])])
     revs = flaskg.storage.search(q)
-    return [rev.meta[NAME] for rev in revs]
+    return [rev.name for rev in revs]
 
 
 @frontend.route('/+history/<itemname:item_name>')
     history = [dict((k, v) for k, v in rev.meta.iteritems() if k != CONTENT) for rev in revs]
     history_page = util.getPageContent(history, offset, results_per_page)
     return render_template('history.html',
-                           item_name=item_name, # XXX no item here
+                           item_name=item_name,  # XXX no item here
                            history_page=history_page,
                            bookmark_time=bookmark_time,
                           )
                            bookmark_time=bookmark_time,
                           )
 
+
 def _compute_item_sets():
     """
     compute sets of existing, linked, transcluded and no-revision item names
     existing = set()
     revs = flaskg.storage.documents(wikiname=app.cfg.interwikiname)
     for rev in revs:
-        existing.add(rev.meta[NAME])
+        existing.add(rev.name)
         linked.update(rev.meta.get(ITEMLINKS, []))
         transcluded.update(rev.meta.get(ITEMTRANSCLUSIONS, []))
     return existing, linked, transcluded
             return False
         if element['password1'].value != element['password2'].value:
             return self.note_error(element, state, 'passwords_mismatch_msg')
+        return True
 
-        return True
 
 class RegistrationForm(TextChaizedForm):
     """a simple user registration form"""
 
     openid = YourOpenID
 
+
 def _using_moin_auth():
     """Check if MoinAuth is being used for authentication.
 
         template = 'register.html'
         FormClass = RegistrationForm
 
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = FormClass.from_defaults()
         if isOpenID:
             oid = request.values.get('openid_openid')
                     if is_ok:
                         flash(_('Account verification required, please see the email we sent to your address.'), "info")
                     else:
-                        flash(_('An error occurred while sending the verification email: "%(message)s" Please contact an administrator to activate your account.',
+                        flash(_('An error occurred while sending the verification email: "%(message)s" '
+                                'Please contact an administrator to activate your account.',
                             message=msg), "error")
                 else:
                     flash(_('Account created, please log in now.'), "info")
 
 @frontend.route('/+verifyemail', methods=['GET'])
 def verifyemail():
-    u = None
+    u = token = None
     if 'username' in request.values and 'token' in request.values:
         u = user.User(auth_username=request.values['username'])
         token = request.values['token']
-    if u and u.disabled and u.validate_recovery_token(token):
+    if u and u.disabled and token and u.validate_recovery_token(token):
         u.profile[DISABLED] = False
         u.save()
         flash(_("Your account has been activated, you can log in now."), "info")
     else:
-        flash(_('Your token is invalid!'), "error")
+        flash(_('Your username and/or token is invalid!'), "error")
     return redirect(url_for('.show_root'))
 
 
     if not _using_moin_auth():
         return Response('No MoinAuth in auth list', 403)
 
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = PasswordLostForm.from_defaults()
     elif request.method == 'POST':
         form = PasswordLostForm.from_flat(request.form)
                            form=form,
                           )
 
+
 class ValidPasswordRecovery(Validator):
     """Validator for a valid password recovery form
     """
 
         return True
 
+
 class PasswordRecoveryForm(Form):
     """a simple password recovery form"""
     name = 'recoverpass'
 
     username = RequiredText.using(label=L_('Name')).with_properties(placeholder=L_("Your login name"))
-    token = RequiredText.using(label=L_('Recovery token')).with_properties(placeholder=L_("The recovery token that has been sent to you"))
-    password1 = RequiredPassword.using(label=L_('New password')).with_properties(placeholder=L_("The login password you want to use"))
-    password2 = RequiredPassword.using(label=L_('New password (repeat)')).with_properties(placeholder=L_("Repeat the same password"))
+    token = RequiredText.using(label=L_('Recovery token')).with_properties(
+        placeholder=L_("The recovery token that has been sent to you"))
+    password1 = RequiredPassword.using(label=L_('New password')).with_properties(
+        placeholder=L_("The login password you want to use"))
+    password2 = RequiredPassword.using(label=L_('New password (repeat)')).with_properties(
+        placeholder=L_("Repeat the same password"))
     submit = Submit.using(default=L_('Change password'))
 
     validators = [ValidPasswordRecovery()]
     if not _using_moin_auth():
         return Response('No MoinAuth in auth list', 403)
 
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = PasswordRecoveryForm.from_defaults()
         form.update(request.values)
     elif request.method == 'POST':
     if flaskg._login_multistage_name == 'openid':
         return Response(flaskg._login_multistage, mimetype='text/html')
 
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = LoginForm.from_defaults()
         for authmethod in app.cfg.auth:
             hint = authmethod.login_hint()
     name = 'usersettings_password'
     validators = [ValidChangePass()]
 
-    password_current = RequiredPassword.using(label=L_('Current Password')).with_properties(placeholder=L_("Your current login password"))
-    password1 = RequiredPassword.using(label=L_('New password')).with_properties(placeholder=L_("The login password you want to use"))
-    password2 = RequiredPassword.using(label=L_('New password (repeat)')).with_properties(placeholder=L_("Repeat the same password"))
+    password_current = RequiredPassword.using(label=L_('Current Password')).with_properties(
+        placeholder=L_("Your current login password"))
+    password1 = RequiredPassword.using(label=L_('New password')).with_properties(
+        placeholder=L_("The login password you want to use"))
+    password2 = RequiredPassword.using(label=L_('New password (repeat)')).with_properties(
+        placeholder=L_("Repeat the same password"))
     submit = Submit.using(default=L_('Change password'))
 
+
 class UserSettingsNotificationForm(Form):
     name = 'usersettings_notification'
     email = YourEmail
 
     # these forms can't be global because we need app object, which is only available within a request:
     class UserSettingsPersonalForm(Form):
-        name = 'usersettings_personal' # "name" is duplicate
-        name = RequiredText.using(label=L_('Name')).with_properties(placeholder=L_("The login name you want to use"))
-        aliasname = OptionalText.using(label=L_('Alias-Name')).with_properties(placeholder=L_("Your alias name (informational)"))
+        name = 'usersettings_personal'  # "name" is duplicate
+        name = Names.using(label=L_('Names')).with_properties(placeholder=L_("The login names you want to use"))
+        display_name = OptionalText.using(label=L_('Display-Name')).with_properties(
+            placeholder=L_("Your display name (informational)"))
         openid = YourOpenID.using(optional=True)
         #timezones_keys = sorted(Locale('en').time_zones.keys())
         timezones_keys = [unicode(tz) for tz in pytz.common_timezones]
         themes_available = sorted([(unicode(t.identifier), t.name) for t in get_themes_list()],
                                   key=lambda x: x[1])
         themes_keys = [t[0] for t in themes_available]
-        theme_name = Select.using(label=L_('Theme name')).with_properties(labels=dict(themes_available)).valued(*themes_keys)
-        css_url = URL.using(label=L_('User CSS URL'), optional=True).with_properties(placeholder=L_("Give the URL of your custom CSS (optional)"))
-        edit_rows = Natural.using(label=L_('Editor size')).with_properties(placeholder=L_("Editor textarea height (0=auto)"))
-        results_per_page = Natural.using(label=L_('History results per page')).with_properties(placeholder=L_("Number of results per page (0=no paging)"))
+        theme_name = Select.using(label=L_('Theme name')).with_properties(
+            labels=dict(themes_available)).valued(*themes_keys)
+        css_url = URL.using(label=L_('User CSS URL'), optional=True).with_properties(
+            placeholder=L_("Give the URL of your custom CSS (optional)"))
+        edit_rows = Natural.using(label=L_('Editor size')).with_properties(
+            placeholder=L_("Editor textarea height (0=auto)"))
+        results_per_page = Natural.using(label=L_('History results per page')).with_properties(
+            placeholder=L_("Number of results per page (0=no paging)"))
         submit = Submit.using(default=L_('Save'))
 
     form_classes = dict(
                     response['flash'].append((_("Your password has been changed."), "info"))
                 else:
                     if part == 'personal':
-                        if form['openid'].value and form['openid'].value != flaskg.user.openid and user.search_users(openid=form['openid'].value):
+                        if (form['openid'].value and form['openid'].value != flaskg.user.openid and
+                            user.search_users(openid=form['openid'].value)):
                             # duplicate openid
                             response['flash'].append((_("This openid is already in use."), "error"))
                             success = False
-                        if form['name'].value != flaskg.user.name and user.search_users(name_exact=form['name'].value):
-                            # duplicate name
-                            response['flash'].append((_("This username is already in use."), "error"))
-                            success = False
+                        if set(form['name'].value) != set(flaskg.user.name):
+                            new_names = set(form['name'].value) - set(flaskg.user.name)
+                            for name in new_names:
+                                if user.search_users(name_exact=name):
+                                    # duplicate name
+                                    response['flash'].append((_("The username %(name)r is already in use.", name=name),
+                                                              "error"))
+                                    success = False
                     if part == 'notification':
                         if (form['email'].value != flaskg.user.email and
                             user.search_users(email=form['email'].value) and app.cfg.user_email_unique):
                         d.pop('submit')
                         for k, v in d.items():
                             flaskg.user.profile[k] = v
-                        if part == 'notification' and app.cfg.user_email_verification and form['email'].value != user_old_email:
+                        if (part == 'notification' and app.cfg.user_email_verification and
+                            form['email'].value != user_old_email):
                             # disable account
                             flaskg.user.profile[DISABLED] = True
                             # send verification mail
                             is_ok, msg = flaskg.user.mail_email_verification()
                             if is_ok:
                                 flaskg.user.logout_session()
-                                response['flash'].append((_('Your account has been disabled because you changed your email address. Please see the email we sent to your address to reactivate it.'), "info"))
+                                response['flash'].append((_('Your account has been disabled because you changed your '
+                                                            'email address. Please see the email we sent to your '
+                                                            'address to reactivate it.'), "info"))
                                 response['redirect'] = url_for('.show_root')
                             else:
                                 # sending the verification email didn't work. reset email change and alert the user.
                                 flaskg.user.profile[DISABLED] = False
                                 flaskg.user.profile[EMAIL] = user_old_email
                                 flaskg.user.save()
-                                response['flash'].append((_('Your email address was not changed because sending the verification email failed. Please try again later.'), "error"))
+                                response['flash'].append((_('Your email address was not changed because sending the '
+                                                            'verification email failed. Please try again later.'),
+                                                          "error"))
                         else:
                             flaskg.user.save()
 
                 item_names.append(name)
     return render_template("link_list_item_panel.html",
                            headline=_("Items with similar names to '%(item_name)s'", item_name=item_name),
-                           item_name=item_name, # XXX no item
+                           item_name=item_name,  # XXX no item
                            item_names=item_names)
 
 
     :rtype: tuple
     :returns: start word, end word, matches dict
     """
-    item_names = [rev.meta[NAME] for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname)]
+    item_names = [rev.name for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname)
+                  if rev.name is not None]
     if item_name in item_names:
         item_names.remove(item_name)
     # Get matches using wiki way, start and end of word
     :returns: start, end, matches dict
     """
     if start_re is None:
-        start_re = re.compile(u'([{0}][{1}]+)'.format(config.chars_upper,
-                                                     config.chars_lower))
+        start_re = re.compile(u'([{0}][{1}]+)'.format(CHARS_UPPER, CHARS_LOWER))
     if end_re is None:
-        end_re = re.compile(u'([{0}][{1}]+)$'.format(config.chars_upper,
-                                                    config.chars_lower))
+        end_re = re.compile(u'([{0}][{1}]+)$'.format(CHARS_UPPER, CHARS_LOWER))
 
     # If we don't get results with wiki words matching, fall back to
     # simple first word and last word, using spaces.
     if not flaskg.storage[item_name]:
         abort(404, item_name)
     sitemap = NestedItemListBuilder().recurse_build([item_name])
-    del sitemap[0] # don't show current item name as sole toplevel list item
+    del sitemap[0]  # don't show current item name as sole toplevel list item
     return render_template('sitemap.html',
-                           item_name=item_name, # XXX no item
+                           item_name=item_name,  # XXX no item
                            sitemap=sitemap,
                           )
 
     def __init__(self):
         self.children = set()
         self.numnodes = 0
-        self.maxnodes = 35 # approx. max count of nodes, not strict
+        self.maxnodes = 35  # approx. max count of nodes, not strict
 
     def recurse_build(self, names):
         result = []
     tags_counts = {}
     for rev in revs:
         tags = rev.meta.get(TAGS, [])
-        logging.debug("name {0!r} rev {1} tags {2!r}".format(rev.meta[NAME], rev.meta[REVID], tags))
+        logging.debug("name {0!r} rev {1} tags {2!r}".format(rev.name, rev.meta[REVID], tags))
         for tag in tags:
             tags_counts[tag] = tags_counts.setdefault(tag, 0) + 1
     tags_counts = sorted(tags_counts.items())
             scale = weight_max / 2
         else:
             scale = weight_max / (count_max - count_min)
+
         def cls(count):
             # return the css class for this tag
             weight = scale * (count - count_min)
-            return "weight{0}".format(int(weight)) # weight0, ..., weight9
+            return "weight{0}".format(int(weight))  # weight0, ..., weight9
         tags = [(cls(count), tag) for tag, count in tags_counts]
     else:
         tags = []
     """
     query = And([Term(WIKINAME, app.cfg.interwikiname), Term(TAGS, tag), ])
     revs = flaskg.storage.search(query, sortedby=NAME_EXACT, limit=None)
-    item_names = [rev.meta[NAME] for rev in revs]
+    item_names = [rev.name for rev in revs]
     return render_template("link_list_no_item_panel.html",
                            headline=_("Items tagged with %(tag)s", tag=tag),
                            item_name=tag,

MoinMoin/apps/misc/views.py

 
 from MoinMoin.apps.misc import misc
 
-from MoinMoin.config import NAME, MTIME
+from MoinMoin.constants.keys import MTIME
 from MoinMoin.themes import render_template
-from MoinMoin import wikiutil
 
 SITEMAP_HAS_SYSTEM_ITEMS = True
 
+
 @misc.route('/sitemap')
 def sitemap():
     """
 
     sitemap = []
     for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname):
-        name = rev.meta[NAME]
+        name = rev.name
         mtime = rev.meta[MTIME]
-        if False: # was: wikiutil.isSystemItem(name)   XXX add back later, when we have that in the index
+        if False:  # was: wikiutil.isSystemItem(name)   XXX add back later, when we have that in the index
             if not SITEMAP_HAS_SYSTEM_ITEMS:
                 continue
             # system items are rather boring
     See: http://usemod.com/cgi-bin/mb.pl?SisterSitesImplementationGuide
     """
     # XXX we currently also get deleted items, fix this
-    item_names = sorted([rev.meta[NAME] for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname)])
+    item_names = sorted([rev.name for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname)])
     content = render_template('misc/urls_names.txt', item_names=item_names)
     return Response(content, mimetype='text/plain')

MoinMoin/auth/__init__.py

         self.multistage = multistage
         self.redirect_to = redirect_to
 
+
 class ContinueLogin(LoginReturn):
     """ ContinueLogin - helper for auth method login that just continues """
     def __init__(self, user_obj, message=None):
         LoginReturn.__init__(self, user_obj, True, message=message)
 
+
 class CancelLogin(LoginReturn):
     """ CancelLogin - cancel login showing a message """
     def __init__(self, message):
         LoginReturn.__init__(self, None, False, message=message)
 
+
 class MultistageFormLogin(LoginReturn):
     """ MultistageFormLogin - require user to fill in another form """
     def __init__(self, multistage):
         LoginReturn.__init__(self, None, False, multistage=multistage)
 
+
 class MultistageRedirectLogin(LoginReturn):
     """ MultistageRedirectLogin - redirect user to another site before continuing login """
     def __init__(self, url):
     name = None
     login_inputs = []
     logout_possible = False
+
     def __init__(self, trusted=False, **kw):
         self.trusted = trusted
         if kw:
             raise TypeError("got unexpected arguments %r" % kw)
+
     def login(self, user_obj, **kw):
         return ContinueLogin(user_obj)
+
     def request(self, user_obj, **kw):
         return user_obj, True
+
     def logout(self, user_obj, **kw):
         if self.name and user_obj and user_obj.auth_method == self.name:
             logging.debug("{0}: logout - invalidating user {1!r}".format(self.name, user_obj.name))
             user_obj.valid = False
         return user_obj, True
+
     def login_hint(self):
         return None
 
+
 class MoinAuth(BaseAuth):
     """ handle login from moin login form """
     def __init__(self, **kw):
         Alternatively you can directly give a fixed user name (user_name)
         that will be considered as authenticated.
     """
-    name = 'given' # was 'http' in 1.8.x and before
+    name = 'given'  # was 'http' in 1.8.x and before
 
     def __init__(self,
                  env_var=None,  # environment variable we want to read (default: REMOTE_USER)
             u.create_or_update()
         if u and u.valid:
             logging.debug("returning valid user {0!r}".format(u))
-            return u, True # True to get other methods called, too
+            return u, True  # True to get other methods called, too
         else:
             logging.debug("returning {0!r}".format(user_obj))
             return user_obj, True
 
     return userobj
 
+
 def handle_logout(userobj):
     """ Logout the passed user from every configured authentication method. """
     if userobj is None:
             break
     return userobj
 
+
 def handle_request(userobj):
     """ Handle the per-request callbacks of the configured authentication methods. """
     for authmethod in app.cfg.auth:
             break
     return userobj
 
+
 def setup_from_session():
     userobj = None
     if 'user.itemid' in session:

MoinMoin/auth/_tests/test_auth.py

 Test for auth.__init__
 """
 
-from flask import current_app as app
 from flask import g as flaskg
 
-import pytest
-
 from MoinMoin._tests import wikiconfig
 from MoinMoin.auth import GivenAuth, handle_login, get_multistage_continuation_url
 from MoinMoin.user import create_user
         auth = [GivenAuth(user_name=u'JoeDoe', autocreate=True), ]
 
     def test(self):
-        assert flaskg.user.name == u'JoeDoe'
+        assert flaskg.user.name == [u'JoeDoe', ]
 
 
 class TestGivenAuth(object):
         create_user(u'Test_User', u'test_pass', u'test@moinmoin.org')
         test_user, bool_value = givenauth_obj.request(flaskg.user)
         assert test_user.valid
-        assert test_user.name == u'Test_User'
+        assert test_user.name == [u'Test_User', ]
 
 def test_handle_login():
     # no messages in the beginning
     test_user1 = handle_login(flaskg.user, login_username='test_user', login_password='test_password', stage='moin')
     test_login_message = [u'Invalid username or password.']
     assert flaskg._login_messages == test_login_message
-    assert test_user1.name == u'anonymous'
+    assert test_user1.name0 == u'anonymous'
     assert not test_user1.valid
     # pop the message
     flaskg._login_messages.pop()
     test_user, bool_value = givenauth_obj.request(flaskg.user)
     test_user2 = handle_login(test_user, login_username='Test_User', login_password='test_pass', stage='moin')
     assert not flaskg._login_messages
-    assert test_user2.name == u'Test_User'
+    assert test_user2.name == [u'Test_User', ]
     assert test_user2.valid
 
 def test_get_multistage_continuation_url():

MoinMoin/auth/_tests/test_http.py

 
 from MoinMoin.user import create_user
 from MoinMoin.auth.http import HTTPAuthMoin
-import pytest
+
 
 class TestHTTPAuthMoin(object):
     """ Test: HTTPAuthMoin """
         httpauthmoin_obj = HTTPAuthMoin()
         test_user, bool_val = httpauthmoin_obj.request(flaskg.user)
         assert test_user.valid
-        assert test_user.name == u'ValidUser'
+        assert test_user.name == [u'ValidUser', ]
         assert bool_val
 
         # when auth_method is not 'http'
         flaskg.user.auth_method = 'invalid'
         test_user, bool_val = httpauthmoin_obj.request(flaskg.user)
         assert not test_user.valid
-        assert test_user.name == u'anonymous'
+        assert test_user.name0 == u'anonymous'

MoinMoin/auth/_tests/test_log.py

 
 from MoinMoin.auth.log import AuthLog
 from flask import g as flaskg
-import pytest
+
 
 class TestAuthLog(object):
     """ Test: TestAuthLog """
         result = authlog_obj.login(flaskg.user)
         assert result.continue_flag
         test_user_obj = result.user_obj
-        assert test_user_obj.name == u'anonymous'
+        assert test_user_obj.name0 == u'anonymous'
 
     def test_request(self):
         authlog_obj = AuthLog()
         result = authlog_obj.request(flaskg.user)
         test_user, bool_value = result
-        assert test_user.name == u'anonymous'
+        assert test_user.name0 == u'anonymous'
         assert not test_user.valid
         assert bool_value
 
         authlog_obj = AuthLog()
         result = authlog_obj.logout(flaskg.user)
         test_user, bool_value = result
-        assert test_user.name == u'anonymous'
+        assert test_user.name0 == u'anonymous'
         assert not test_user.valid
         assert bool_value

MoinMoin/auth/http.py

 
 from flask import request
 
-from MoinMoin import config, user
+from MoinMoin import user
 from MoinMoin.i18n import _, L_, N_
 from MoinMoin.auth import BaseAuth, GivenAuth
 
 
         auth = request.authorization
         if auth and auth.username and auth.password is not None:
-            logging.debug("http basic auth, received username: {0!r} password: {1!r}".format(auth.username, auth.password))
+            logging.debug("http basic auth, received username: {0!r} password: {1!r}".format(
+                auth.username, auth.password))
             u = user.User(name=auth.username.decode(self.coding),
                           password=auth.password.decode(self.coding),
                           auth_method=self.name, auth_attribs=[], trusted=self.trusted)
             u.create_or_update()
         if u and u.valid:
             logging.debug("returning valid user {0!r}".format(u))
-            return u, True # True to get other methods called, too
+            return u, True  # True to get other methods called, too
         else:
             logging.debug("returning {0!r}".format(user_obj))
             return user_obj, True

MoinMoin/auth/ldap_login.py

     "Can't contact LDAP server" - more recent debian installations have tls
     support in libldap2 (see dependency on gnutls) and also in python-ldap.
 
-    TODO: allow more configuration (alias name, ...) by using callables as parameters
+    TODO: allow more configuration (display name, ...) by using callables as parameters
 """
 
 from MoinMoin import log
         bind_pw='',
         base_dn='',  # base DN we use for searching
                      #base_dn = 'ou=SOMEUNIT,dc=example,dc=org'
-        scope=ldap.SCOPE_SUBTREE, # scope of the search we do (2 == ldap.SCOPE_SUBTREE)
-        referrals=0, # LDAP REFERRALS (0 needed for AD)
+        scope=ldap.SCOPE_SUBTREE,  # scope of the search we do (2 == ldap.SCOPE_SUBTREE)
+        referrals=0,  # LDAP REFERRALS (0 needed for AD)
         search_filter='(uid=%(username)s)',  # ldap filter used for searching:
                                              #search_filter = '(sAMAccountName=%(username)s)' # (AD)
                                              #search_filter = '(uid=%(username)s)' # (OpenLDAP)
                                              # you can also do more complex filtering like:
                                              # "(&(cn=%(username)s)(memberOf=CN=WikiUsers,OU=Groups,DC=example,DC=org))"
         # some attribute names we use to extract information from LDAP:
-        givenname_attribute=None, # ('givenName') ldap attribute we get the first name from
-        surname_attribute=None, # ('sn') ldap attribute we get the family name from
-        aliasname_attribute=None, # ('displayName') ldap attribute we get the aliasname from
-        email_attribute=None, # ('mail') ldap attribute we get the email address from
-        email_callback=None, # called to make up email address
-        name_callback=None, # called to use a Wiki name different from the login name
-        coding='utf-8', # coding used for ldap queries and result values
-        timeout=10, # how long we wait for the ldap server [s]
-        start_tls=0, # 0 = No, 1 = Try, 2 = Required
+        givenname_attribute=None,  # ('givenName') ldap attribute we get the first name from
+        surname_attribute=None,  # ('sn') ldap attribute we get the family name from
+        displayname_attribute=None,  # ('displayName') ldap attribute we get the display_name from
+        email_attribute=None,  # ('mail') ldap attribute we get the email address from
+        email_callback=None,  # called to make up email address
+        name_callback=None,  # called to use a Wiki name different from the login name
+        coding='utf-8',  # coding used for ldap queries and result values
+        timeout=10,  # how long we wait for the ldap server [s]
+        start_tls=0,  # 0 = No, 1 = Try, 2 = Required
         tls_cacertdir=None,
         tls_cacertfile=None,
         tls_certfile=None,
         tls_keyfile=None,
-        tls_require_cert=0, # 0 == ldap.OPT_X_TLS_NEVER (needed for self-signed certs)
-        bind_once=False, # set to True to only do one bind - useful if configured to bind as the user on the first attempt
-        autocreate=False, # set to True if you want to autocreate user profiles
-        name='ldap', # use e.g. 'ldap_pdc' and 'ldap_bdc' (or 'ldap1' and 'ldap2') if you auth against 2 ldap servers
-        report_invalid_credentials=True, # whether to emit "invalid username or password" msg at login time or not
+        tls_require_cert=0,  # 0 == ldap.OPT_X_TLS_NEVER (needed for self-signed certs)
+        bind_once=False,  # set to True to only do one bind - useful if configured to bind as the user on first attempt
+        autocreate=False,  # set to True if you want to autocreate user profiles
+        name='ldap',  # use e.g. 'ldap_pdc' and 'ldap_bdc' (or 'ldap1' and 'ldap2') if you auth against 2 ldap servers
+        report_invalid_credentials=True,  # whether to emit "invalid username or password" msg at login time or not
         **kw
         ):
         super(LDAPAuth, self).__init__(**kw)
 
         self.givenname_attribute = givenname_attribute
         self.surname_attribute = surname_attribute
-        self.aliasname_attribute = aliasname_attribute
+        self.displayname_attribute = displayname_attribute
         self.email_attribute = email_attribute
         self.email_callback = email_callback
         self.name_callback = name_callback
             try:
                 u = None
                 dn = None
+                server = self.server_uri
                 coding = self.coding
                 logging.debug("Setting misc. ldap options...")
-                ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) # ldap v2 is outdated
+                ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3)  # ldap v2 is outdated
                 ldap.set_option(ldap.OPT_REFERRALS, self.referrals)
                 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout)
 
                         if value is not None:
                             ldap.set_option(option, value)
 
-                server = self.server_uri
                 logging.debug("Trying to initialize {0!r}.".format(server))
                 l = ldap.initialize(server)
                 logging.debug("Connected to LDAP server {0!r}.".format(server))
                 logging.debug("Searching {0!r}".format(filterstr))
                 attrs = [getattr(self, attr) for attr in [
                                          'email_attribute',
-                                         'aliasname_attribute',
+                                         'displayname_attribute',
                                          'surname_attribute',
                                          'givenname_attribute',
                                          ] if getattr(self, attr) is not None]
                 result_length = len(lusers)
                 if result_length != 1:
                     if result_length > 1:
-                        logging.warning("Search found more than one ({0}) matches for {1!r}.".format(result_length, filterstr))
+                        logging.warning("Search found more than one ({0}) matches for {1!r}.".format(
+                            result_length, filterstr))
                     if result_length == 0:
                         logging.debug("Search found no matches for {0!r}.".format(filterstr, ))
                     if self.report_invalid_credentials:
                 else:
                     email = self.email_callback(ldap_dict)
 
-                aliasname = ''
+                display_name = ''
                 try:
-                    aliasname = ldap_dict[self.aliasname_attribute][0]
+                    display_name = ldap_dict[self.displayname_attribute][0]
                 except (KeyError, IndexError):
                     pass
-                if not aliasname:
+                if not display_name:
                     sn = ldap_dict.get(self.surname_attribute, [''])[0]
                     gn = ldap_dict.get(self.givenname_attribute, [''])[0]
                     if sn and gn:
-                        aliasname = "{0}, {1}".format(sn, gn)
+                        display_name = "{0}, {1}".format(sn, gn)
                     elif sn:
-                        aliasname = sn
-                aliasname = aliasname.decode(coding)
+                        display_name = sn
+                display_name = display_name.decode(coding)
 
                 if self.name_callback:
                     username = self.name_callback(ldap_dict)
 
                 if email:
-                    u = user.User(auth_username=username, auth_method=self.name, auth_attribs=('name', 'password', 'email', 'mailto_author', ),
+                    u = user.User(auth_username=username, auth_method=self.name,
+                                  auth_attribs=('name', 'password', 'email', 'mailto_author', ),
                                   trusted=self.trusted)
                     u.email = email
                 else:
-                    u = user.User(auth_username=username, auth_method=self.name, auth_attribs=('name', 'password', 'mailto_author', ),
+                    u = user.User(auth_username=username, auth_method=self.name,
+                                  auth_attribs=('name', 'password', 'mailto_author', ),
                                   trusted=self.trusted)
                 u.name = username
-                u.aliasname = aliasname
-                logging.debug("creating user object with name {0!r} email {1!r} alias {2!r}".format(username, email, aliasname))
+                u.display_name = display_name
+                logging.debug("creating user object with name {0!r} email {1!r} display name {2!r}".format(
+                    username, email, display_name))
 
             except ldap.INVALID_CREDENTIALS as err:
-                logging.debug("invalid credentials (wrong password?) for dn {0!r} (username: {1!r})".format(dn, username))
+                logging.debug("invalid credentials (wrong password?) for dn {0!r} (username: {1!r})".format(
+                    dn, username))
                 return CancelLogin(_("Invalid username or password."))
 
             if u and self.autocreate:

MoinMoin/auth/log.py

 
 from MoinMoin.auth import BaseAuth, ContinueLogin
 
+
 class AuthLog(BaseAuth):
     """ just log the call, do nothing else """
     name = "log"

MoinMoin/auth/openidrp.py

 from flask import current_app as app
 from MoinMoin.auth import BaseAuth, get_multistage_continuation_url
 from MoinMoin.auth import ContinueLogin, CancelLogin, MultistageFormLogin, MultistageRedirectLogin
-from MoinMoin.config import ITEMID
+from MoinMoin.constants.keys import ITEMID
 from MoinMoin import user
 from MoinMoin.i18n import _, L_, N_
 

MoinMoin/auth/smb_mount.py

 
 from MoinMoin.auth import BaseAuth, CancelLogin, ContinueLogin
 
+
 class SMBMount(BaseAuth):
     """ auth plugin for (un)mounting an smb share,
         this is a wrapper around mount.cifs -o <options> //server/share mountpoint
         See man mount.cifs for details.
     """
     def __init__(self,
-        server, # mount.cifs //server/share
-        share, # mount.cifs //server/share
-        mountpoint_fn, # function of username to determine the mountpoint, e.g.:
-                       # lambda username: u'/mnt/wiki/%s' % username
-        dir_user, # username to get the uid that is used for mount.cifs -o uid=... (e.g. 'www-data')
-        domain, # mount.cifs -o domain=...
-        dir_mode='0700', # mount.cifs -o dir_mode=...
-        file_mode='0600', # mount.cifs -o file_mode=...
-        iocharset='utf-8', # mount.cifs -o iocharset=... (try 'iso8859-1' if default does not work)
-        coding='utf-8', # encoding used for username/password/cmdline (try 'iso8859-1' if default does not work)
-        log='/dev/null', # logfile for mount.cifs output
+        server,  # mount.cifs //server/share
+        share,  # mount.cifs //server/share
+        mountpoint_fn,  # function of username to determine the mountpoint, e.g.:
+                        # lambda username: u'/mnt/wiki/%s' % username
+        dir_user,  # username to get the uid that is used for mount.cifs -o uid=... (e.g. 'www-data')
+        domain,  # mount.cifs -o domain=...
+        dir_mode='0700',  # mount.cifs -o dir_mode=...
+        file_mode='0600',  # mount.cifs -o file_mode=...
+        iocharset='utf-8',  # mount.cifs -o iocharset=... (try 'iso8859-1' if default does not work)
+        coding='utf-8',  # encoding used for username/password/cmdline (try 'iso8859-1' if default does not work)
+        log='/dev/null',  # logfile for mount.cifs output
         **kw
         ):
         super(SMBMount, self).__init__(**kw)
     def do_smb(self, username, password, login):
         logging.debug("login={0} logout={1}: got name={2!r}".format(login, not login, username))
 
-        import os, pwd, subprocess
+        import os
+        import pwd
+        import subprocess
         web_username = self.dir_user
-        web_uid = pwd.getpwnam(web_username)[2] # XXX better just use current uid?
+        web_uid = pwd.getpwnam(web_username)[2]  # XXX better just use current uid?
 
         mountpoint = self.mountpoint_fn(username)
         if login:
-            cmd = u"sudo mount -t cifs -o user=%(user)s,domain=%(domain)s,uid=%(uid)d,dir_mode=%(dir_mode)s,file_mode=%(file_mode)s,iocharset=%(iocharset)s //%(server)s/%(share)s %(mountpoint)s >>%(log)s 2>&1"
+            cmd = (u"sudo mount -t cifs -o user=%(user)s,domain=%(domain)s,uid=%(uid)d,dir_mode=%(dir_mode)s,file_mode="
+                   u"%(file_mode)s,iocharset=%(iocharset)s //%(server)s/%(share)s %(mountpoint)s >>%(log)s 2>&1")
         else:
             cmd = u"sudo umount %(mountpoint)s >>%(log)s 2>&1"
 
         if login:
             try:
                 if not os.path.exists(mountpoint):
-                    os.makedirs(mountpoint) # the dir containing the mountpoint must be writeable for us!
+                    os.makedirs(mountpoint)  # the dir containing the mountpoint must be writeable for us!
             except OSError:
                 pass
             env['PASSWD'] = password.encode(self.coding)

MoinMoin/config/__init__.py

-# Copyright: 2011 MoinMoin:ThomasWaldmann
+# Copyright: 2011-2013 MoinMoin:ThomasWaldmann
 # License: GNU GPL v2 (or any later version), see LICENSE.txt for details.
 
-"""
-MoinMoin - configuration defaults and support code
-"""
-
-# provide compatibility for stuff not using MoinMoin.constants yet
-from MoinMoin.constants.rights import *
-from MoinMoin.constants.chartypes import *
-from MoinMoin.constants.contenttypes import *
-from MoinMoin.constants.keys import *
-from MoinMoin.constants.misc import *
+# Nothing to see here any more, please do direct imports from MoinMoin.constants.*

MoinMoin/config/default.py

 logging = log.getLogger(__name__)
 
 from MoinMoin.i18n import _, L_, N_
-from MoinMoin import config, error
+from MoinMoin import error
+from MoinMoin.constants.rights import ACL_RIGHTS_CONTENTS, ACL_RIGHTS_FUNCTIONS
 from MoinMoin import datastruct
 from MoinMoin.auth import MoinAuth
 from MoinMoin.util import plugins
         self.mail_enabled = self.mail_enabled and True or False
 
         if self.namespace_mapping is None:
-            raise error.ConfigurationError("No storage configuration specified! You need to define a namespace_mapping. "
-                                           "For further reference, please see HelpOnStorageConfiguration.")
+            raise error.ConfigurationError(
+                "No storage configuration specified! You need to define a namespace_mapping. "
+                "For further reference, please see HelpOnStorageConfiguration.")
+
+        if self.backend_mapping is None:
+            raise error.ConfigurationError(
+                "No storage configuration specified! You need to define a backend_mapping. " +
+                "For further reference, please see HelpOnStorageConfiguration.")
 
         if self.acl_mapping is None:
-            raise error.ConfigurationError("No acl configuration specified! You need to define a acl_mapping. "
-                                           "For further reference, please see HelpOnStorageConfiguration.")
+            raise error.ConfigurationError(
+                "No acl configuration specified! You need to define a acl_mapping. "
+                "For further reference, please see HelpOnStorageConfiguration.")
 
         if self.secrets is None:  # admin did not setup a real secret
-            raise error.ConfigurationError("No secret configured! You need to set secrets = 'somelongsecretstring' in your wiki config.")
+            raise error.ConfigurationError(
+                "No secret configured! You need to set secrets = 'somelongsecretstring' in your wiki config.")
 
         if self.interwikiname is None:  # admin did not setup a real interwikiname
-            raise error.ConfigurationError("No interwikiname configured! You need to set interwikiname = u'YourUniqueStableInterwikiName' in your wiki config.")
+            raise error.ConfigurationError(
+                "No interwikiname configured! "
+                "You need to set interwikiname = u'YourUniqueStableInterwikiName' in your wiki config.")
 
         secret_key_names = ['security/ticket', ]
         if self.textchas:
         secret_min_length = 10
         if isinstance(self.secrets, str):
             if len(self.secrets) < secret_min_length:
-                raise error.ConfigurationError("The secrets = '...' wiki config setting is a way too short string (minimum length is {0} chars)!".format(
-                    secret_min_length))
+                raise error.ConfigurationError(
+                    "The secrets = '...' wiki config setting is a way too short string "
+                    "(minimum length is {0} chars)!".format(secret_min_length))
             # for lazy people: set all required secrets to same value
             secrets = {}
             for key in secret_key_names:
                 if len(secret) < secret_min_length:
                     raise ValueError
             except (KeyError, ValueError):
-                raise error.ConfigurationError("You must set a (at least {0} chars long) secret string for secrets['{1}']!".format(
+                raise error.ConfigurationError(
+                    "You must set a (at least {0} chars long) secret string for secrets['{1}']!".format(
                     secret_min_length, secret_key_name))
 
         from passlib.context import CryptContext
         unknown = ['"{0}"'.format(name) for name in dir(self)
                   if not name.startswith('_') and
                   name not in DefaultConfig.__dict__ and
-                  not isinstance(getattr(self, name), (type(sys), type(DefaultConfig)))]
+                  not isinstance(getattr(self, name), (type(re), type(DefaultConfig)))]
         if unknown:
             msg = """