changeset 3142:fe93fb0732a2

merged main
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Wed, 27 Feb 2008 15:41:41 +0100
parents 044856398918 (current diff) 232b1fcfadab (diff)
children 16ae95df840a
files
diffstat 8 files changed, 243 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/auth/__init__.py	Wed Feb 27 15:41:13 2008 +0100
+++ b/MoinMoin/auth/__init__.py	Wed Feb 27 15:41:41 2008 +0100
@@ -93,6 +93,11 @@
                       - 'username': username entry field
                       - 'password': password entry field
                       - 'openid_identifier': OpenID entry field
+                      - 'special_no_input': manual login is required
+                            but no form fields need to be filled in
+                            (for example openid with forced provider)
+                            in this case the theme may provide a short-
+                            cut omitting the login form
      * logout_possible: boolean indicating whether this auth methods
                         supports logging out
      * name: name of the auth method, must be the same as given as the
@@ -146,8 +151,11 @@
               'login': '1',
               'stage': auth_name}
     fields.update(extra_fields)
-    qstr = wikiutil.makeQueryString(fields)
-    return ''.join([request.getBaseURL(), '?', qstr])
+    if request.page:
+        return request.page.url(request, querystr=fields, relative=False)
+    else:
+        qstr = wikiutil.makeQueryString(fields)
+        return ''.join([request.getBaseURL(), '?', qstr])
 
 
 class LoginReturn(object):
--- a/MoinMoin/auth/openidrp.py	Wed Feb 27 15:41:13 2008 +0100
+++ b/MoinMoin/auth/openidrp.py	Wed Feb 27 15:41:41 2008 +0100
@@ -17,11 +17,54 @@
 from MoinMoin.auth import MultistageFormLogin, MultistageRedirectLogin
 from MoinMoin.auth import get_multistage_continuation_url
 
+
 class OpenIDAuth(BaseAuth):
     login_inputs = ['openid_identifier']
     name = 'openid'
     logout_possible = True
 
+    def __init__(self, modify_request=None,
+                       update_user=None,
+                       create_user=None,
+                       forced_service=None):
+        BaseAuth.__init__(self)
+        self._modify_request = modify_request or (lambda x: None)
+        self._update_user = update_user or (lambda i, u: None)
+        self._create_user = create_user or (lambda i, u: None)
+        self._forced_service = forced_service
+        if forced_service:
+            self.login_inputs = ['special_no_input']
+
+    def _handle_user_data(self, request, u):
+        create = not u
+        if create:
+            # pass in a created but unsaved user object
+            u = user.User(request, auth_method=self.name,
+                          auth_username=request.session['openid.id'])
+            # invalid name
+            u.name = ''
+            u = self._create_user(request.session['openid.info'], u)
+
+        if u:
+            self._update_user(request.session['openid.info'], u)
+
+            # just in case the wiki admin screwed up
+            if (not user.isValidName(request, u.name) or
+                (create and user.getUserId(request, u.name))):
+                return None
+
+            if not hasattr(u, 'openids'):
+                u.openids = []
+            if not request.session['openid.id'] in u.openids:
+                u.openids.append(request.session['openid.id'])
+
+            u.save()
+
+            del request.session['openid.id']
+            del request.session['openid.info']
+
+        return u
+
     def _get_account_name(self, request, form, msg=None):
         # now we need to ask the user for a new username
         # that they want to use on this wiki
@@ -96,12 +139,22 @@
         elif info.status == consumer.CANCEL:
             return CancelLogin(_('Verification canceled.'))
         elif info.status == consumer.SUCCESS:
+            request.session['openid.id'] = info.identity_url
+            request.session['openid.info'] = info
+
             # try to find user object
             uid = user.getUserIdByOpenId(request, info.identity_url)
             if uid:
                 u = user.User(request, id=uid, auth_method=self.name,
                               auth_username=info.identity_url)
+            else:
+                u = None
+
+            # create or update the user according to the registration data
+            u = self._handle_user_data(request, u)
+            if u:
                 return ContinueLogin(u)
+
             # if no user found, then we need to ask for a username,
             # possibly associating an existing account.
             request.session['openid.id'] = info.identity_url
@@ -125,13 +178,10 @@
             uid = user.getUserId(request, newname)
         if not uid:
             # we can create a new user with this name :)
-            u = user.User(request, id=uid, auth_method=self.name,
+            u = user.User(request, auth_method=self.name,
                           auth_username=request.session['openid.id'])
             u.name = newname
-            u.openids = [request.session['openid.id']]
-            u.aliasname = request.session['openid.id']
-            del request.session['openid.id']
-            u.save()
+            u = self._handle_user_data(request, u)
             return ContinueLogin(u)
         # requested username already exists. if they know the password,
         # they can associate that account with the openid.
@@ -151,13 +201,7 @@
                       auth_method=self.name,
                       auth_username=request.session['openid.id'])
         if u.valid:
-            if not hasattr(u, 'openids'):
-                u.openids = []
-            u.openids.append(request.session['openid.id'])
-            if not u.aliasname:
-                u.aliasname = request.session['openid.id']
-            u.save()
-            del request.session['openid.id']
+            self._handle_user_data(request, u)
             return ContinueLogin(u, _('Your account is now associated to your OpenID.'))
         else:
             msg = _('The password you entered is not valid.')
@@ -197,8 +241,9 @@
             return ContinueLogin(user_obj)
 
         openid_id = kw.get('openid_identifier')
+
         # nothing entered? continue...
-        if not openid_id:
+        if not self._forced_service and not openid_id:
             return ContinueLogin(user_obj)
 
         _ = request.getText
@@ -212,7 +257,14 @@
                                         MoinOpenIDStore(request))
 
         try:
-            oidreq = oidconsumer.begin(openid_id)
+            fserv = self._forced_service
+            if fserv:
+                if isinstance(fserv, str) or isinstance(fserv, unicode):
+                    oidreq = oidconsumer.begin(fserv)
+                else:
+                    oidreq = oidconsumer.beginWithoutDiscovery(fserv)
+            else:
+                oidreq = oidconsumer.begin(openid_id)
         except HTTPFetchingError:
             return ContinueLogin(None, _('Failed to resolve OpenID.'))
         except DiscoveryFailure:
@@ -221,6 +273,8 @@
             if oidreq is None:
                 return ContinueLogin(None, _('No OpenID.'))
 
+            self._modify_request(oidreq)
+
             return_to = get_multistage_continuation_url(request, self.name,
                                                         {'oidstage': '1'})
             trust_root = request.getBaseURL()
--- a/MoinMoin/formatter/text_html.py	Wed Feb 27 15:41:13 2008 +0100
+++ b/MoinMoin/formatter/text_html.py	Wed Feb 27 15:41:41 2008 +0100
@@ -1241,7 +1241,7 @@
     _allowed_table_attrs = {
         'table': ['class', 'id', 'style'],
         'row': ['class', 'id', 'style'],
-        '': ['colspan', 'rowspan', 'class', 'id', 'style'],
+        '': ['colspan', 'rowspan', 'class', 'id', 'style', 'abbr'],
     }
 
     def _checkTableAttr(self, attrs, prefix):
--- a/MoinMoin/theme/__init__.py	Wed Feb 27 15:41:13 2008 +0100
+++ b/MoinMoin/theme/__init__.py	Wed Feb 27 15:41:41 2008 +0100
@@ -285,9 +285,13 @@
                 userlinks.append(d['page'].link_to(request, text=_('Logout', formatted=False),
                                                    querystr={'action': 'logout', 'logout': 'logout'}, id='logout', rel='nofollow'))
         else:
+            query = {'action': 'login'}
+            # special direct-login link if the auth methods want no input
+            if request.cfg.auth_login_inputs == ['special_no_input']:
+                query['login'] = '1'
             if request.cfg.auth_have_login:
                 userlinks.append(d['page'].link_to(request, text=_("Login", formatted=False),
-                                                   querystr={'action': 'login'}, id='login', rel='nofollow'))
+                                                   querystr=query, id='login', rel='nofollow'))
 
         userlinks = [u'<li>%s</li>' % link for link in userlinks]
         html = u'<ul id="username">%s</ul>' % ''.join(userlinks)
--- a/MoinMoin/user.py	Wed Feb 27 15:41:13 2008 +0100
+++ b/MoinMoin/user.py	Wed Feb 27 15:41:41 2008 +0100
@@ -975,7 +975,7 @@
     def isSuperUser(self):
         """ Check if this user is superuser """
         request = self._request
-        if request.cfg.DesktopEdition and request.remote_addr == '127.0.0.1' and request.user.valid:
+        if request.cfg.DesktopEdition and request.remote_addr == '127.0.0.1' and request.user and request.user.valid:
             # the DesktopEdition gives any local user superuser powers
             return True
         superusers = request.cfg.superuser
--- a/MoinMoin/widget/browser.py	Wed Feb 27 15:41:13 2008 +0100
+++ b/MoinMoin/widget/browser.py	Wed Feb 27 15:41:41 2008 +0100
@@ -16,10 +16,15 @@
         base.Widget.__init__(self, request, **kw)
         self.data = None
         self.data_id = 'dbw.'
+        # prefixed with __ are untranslated and to be used in the JS
         self._all = _('[all]')
+        self.__all = '[all]'
         self._notempty = _('[not empty]')
+        self.__notempty = '[notempty]'
         self._empty = _('[empty]')
+        self.__empty = '[empty]'
         self._filter = _('filter')
+        self.__filter = 'filter'
 
     def setData(self, dataset):
         """ Sets the data for the browser (see MoinMoin.util.dataset).
@@ -39,7 +44,7 @@
         """
         return 'name="%s%s"' % (self.data_id, elem)
 
-    def _makeoption(self, item, selected):
+    def _makeoption(self, item, selected, ntitem = None):
         """ create an option for a <select> form element
         @param item: string containing the item name to show
         @param selected: indicates whether the item should be default or not
@@ -50,7 +55,9 @@
             selected = ''
         assert(isinstance(item, basestring))
         item = wikiutil.escape(item)
-        return '<option value="%s"%s>%s</option>' % (item, selected, item)
+        if ntitem is None:
+            ntitem = item
+        return '<option value="%s"%s>%s</option>' % (ntitem, selected, item)
 
     def _filteroptions(self, idx):
         """ create options for all elements in the column
@@ -58,9 +65,8 @@
         """
         self.data.reset()
         row = self.data.next()
-        # leave the 'empty' slot blank so we avoid adding
-        # blank items when getting all possibilities
-        unique = [self._all, '', self._notempty]
+        # [empty] is a special already
+        unique = ['']
 
         value = None
         name = '%sfilter%d' % (self.data_id, idx)
@@ -75,17 +81,20 @@
             row = self.data.next()
 
         # fill in the empty field we left blank
-        unique[1] = self._empty
-        sortedlist = unique[3:]
-        sortedlist.sort()
-        unique = unique[:3] + sortedlist
-        return '\n'.join([self._makeoption(item, item == value) for item in unique])
+        del unique[0]
+        unique.sort()
+        result = [self._makeoption(item, item == value) for item in unique]
+        common = [None, None, None]
+        common[0] = self._makeoption(self._all, value == self.__all, self.__all)
+        common[1] = self._makeoption(self._empty, value == self.__empty, self.__empty)
+        common[2] = self._makeoption(self._notempty, value == self.__notempty, self.__notempty)
+        return '\n'.join(common + result)
 
     def format(self):
         fmt = self.request.formatter
 
         result = []
-        result.append(fmt.rawHTML('<form action="%s/%s" method="GET">' % (self.request.getScriptname(), wikiutil.quoteWikinameURL(self.request.page.page_name))))
+        result.append(fmt.rawHTML('<form action="%s/%s" method="GET" name="%s">' % (self.request.getScriptname(), wikiutil.quoteWikinameURL(self.request.page.page_name), self.data_id)))
         result.append(fmt.div(1))
 
         havefilters = False
@@ -96,7 +105,7 @@
         if havefilters:
             result.append(fmt.rawHTML('<input type="submit" value="%s" %s>' % (self._filter, self._name('submit'))))
 
-        result.append(fmt.table(1))
+        result.append(fmt.table(1, id='%stable' % self.data_id))
 
         # add header line
         result.append(fmt.table_row(1))
@@ -111,8 +120,10 @@
 
             if col.autofilter:
                 result.append(fmt.linebreak(False))
-                select = '<select %s>%s</select>' % (self._name('filter%d' % idx),
-                                                     self._filteroptions(idx))
+                select = '<select %s onchange="dbw_update_search(\'%s\');">%s</select>' % (
+                                  self._name('filter%d' % idx),
+                                  self.data_id,
+                                  self._filteroptions(idx))
                 result.append(fmt.rawHTML(select))
 
             result.append(fmt.table_cell(0))
@@ -156,10 +167,11 @@
                 for idx in range(len(row)):
                     if self.data.columns[idx].hidden:
                         continue
-                    result.append(fmt.table_cell(1))
                     if isinstance(row[idx], tuple):
+                        result.append(fmt.table_cell(1, abbr=unicode(row[idx][1])))
                         result.append(unicode(row[idx][0]))
                     else:
+                        result.append(fmt.table_cell(1))
                         result.append(unicode(row[idx]))
                     result.append(fmt.table_cell(0))
                 result.append(fmt.table_row(0))
--- a/MoinMoin/wikiutil.py	Wed Feb 27 15:41:13 2008 +0100
+++ b/MoinMoin/wikiutil.py	Wed Feb 27 15:41:41 2008 +0100
@@ -1578,6 +1578,60 @@
     return arg
 
 
+class IEFArgument:
+    """
+    Base class for new argument parsers for
+    invoke_extension_function.
+    """
+    def __init__(self):
+        pass
+
+    def parse_argument(self, s):
+        """
+        Parse the argument given in s (a string) and return
+        the argument for the extension function.
+        """
+        raise NotImplementedError
+
+    def get_default(self):
+        """
+        Return the default for this argument.
+        """
+        raise NotImplementedError
+
+
+class UnitArgument(IEFArgument):
+    """
+    Argument class for invoke_extension_function that forces
+    having any of the specified units given for a value.
+
+    Note that the default unit is "mm".
+
+    Use, for example, "UnitArgument('7mm', float, ['%', 'mm'])".
+    """
+    def __init__(self, default, argtype, units=['mm']):
+        """
+        Initialise a UnitArgument giving the default,
+        argument type and the permitted units.
+        """
+        IEFArgument.__init__(self)
+        self._units = list(units)
+        self._units.sort(cmp=lambda x, y: len(y) - len(x))
+        self._type = argtype
+        self._default = self.parse_argument(default)
+
+    def parse_argument(self, s):
+        for unit in self._units:
+            if s.endswith(unit):
+                ret = (self._type(s[:len(s) - len(unit)]), unit)
+                return ret
+        ## XXX: how can we translate this?
+        raise ValueError("Invalid unit in value %s" % s)
+
+    def get_default(self):
+        return self._default
+
+
 class required_arg:
     """
     Wrap a type in this class and give it as default argument
@@ -1589,7 +1643,8 @@
         Initialise a required_arg
         @param argtype: the type the argument should have
         """
-        if not argtype in (bool, int, long, float, complex, unicode):
+        if not (argtype in (bool, int, long, float, complex, unicode) or
+                isinstance(argtype, IEFArgument)):
             raise TypeError("argtype must be a valid type")
         self.argtype = argtype
 
@@ -1642,6 +1697,11 @@
             return get_float(request, value, name)
         elif default is complex:
             return get_complex(request, value, name)
+        elif isinstance(default, IEFArgument):
+            # defaults handled later
+            if value is None:
+                return None
+            return default.parse_argument(value)
         elif isinstance(default, required_arg):
             return _convert_arg(request, value, default.argtype, name)
         return value
@@ -1736,9 +1796,11 @@
             # went wrong (if it does)
             kwargs[argname] = _convert_arg(request, kwargs[argname],
                                            defaults[argname], argname)
-            if (kwargs[argname] is None
-                and isinstance(defaults[argname], required_arg)):
-                raise ValueError(_('Argument "%s" is required') % argname)
+            if kwargs[argname] is None:
+                if isinstance(defaults[argname], required_arg):
+                    raise ValueError(_('Argument "%s" is required') % argname)
+                if isinstance(defaults[argname], IEFArgument):
+                    kwargs[argname] = defaults[argname].get_default()
 
         if not argname in argnames:
             # move argname into _kwargs parameter
--- a/wiki/htdocs/common/js/common.js	Wed Feb 27 15:41:13 2008 +0100
+++ b/wiki/htdocs/common/js/common.js	Wed Feb 27 15:41:41 2008 +0100
@@ -200,6 +200,9 @@
     
     // Editor stuff
     show_switch2gui();
+
+    // data browser widget
+    dbw_hide_buttons();
 }
 
 
@@ -217,3 +220,65 @@
 // Catch before unloading the page
 window.onbeforeunload = before_unload
 
+function dbw_update_search(dbw_id)
+{
+    var table = document.getElementById(dbw_id+'table');
+    var cell;
+    var shown;
+    var i
+    var cols = table.rows[0].cells.length;
+    var filter = new Array();
+    var dofilter = new Array();
+    var form = document.forms[dbw_id+'form'];
+
+    for (i = 0; i < cols; i++) {
+        dofilter[i] = false;
+        if (form[dbw_id+'filter'+i]) {
+            dofilter[i] = true;
+            filter[i] = form[dbw_id+'filter'+i].value;
+            if (filter[i] == '[all]')
+                dofilter[i] = false;
+            if (filter[i] == '[empty]')
+                filter[i] = '';
+        }
+    }
+
+    for (i = 1; i < table.rows.length; i++) {
+        var show = true;
+        for (col = 0; col < cols; col++) {
+            if (!dofilter[col])
+                continue;
+
+            cell = table.rows[i].cells[col];
+
+            if (filter[col] == '[notempty]') {
+                if (cell.abbr == '') {
+                    show = false;
+                    break;
+                }
+            } else if (filter[col] != cell.abbr) {
+                show = false;
+                break;
+            }
+        }
+        if (show)
+            table.rows[i].style.display = '';
+        else
+            table.rows[i].style.display = 'none';
+    }
+}
+
+function dbw_hide_buttons() {
+    var form;
+    var elem;
+
+    for (var fidx = 0; fidx < document.forms.length; fidx++) {
+        form = document.forms[fidx];
+        for (var eidx = 0; eidx < form.elements.length; eidx++) {
+            elem = form.elements[eidx];
+            name = elem.name;
+            if (name.substr(0,4) == 'dbw.' && name.substr(-7) == '.submit')
+                elem.style.display = 'none';
+        }
+    }
+}