From 1856b481e61f8da9898e6ffebdd452be6a29920c Mon Sep 17 00:00:00 2001 From: salles Date: Thu, 5 Jan 2006 19:00:34 +0100 Subject: [PATCH] Version originale avant blocage de la cration de compte depuis l'extrieur darcs-hash:20060105180034-72cb0-571e1fc0244483cf22db0ce9051a6e03ff5ebde4.gz --- wiki/userform.py | 566 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 566 insertions(+) create mode 100644 wiki/userform.py diff --git a/wiki/userform.py b/wiki/userform.py new file mode 100644 index 00000000..adb604a5 --- /dev/null +++ b/wiki/userform.py @@ -0,0 +1,566 @@ +# -*- coding: iso-8859-1 -*- +""" + MoinMoin - UserPreferences Form and User Browser + + @copyright: 2001-2004 by Jürgen Hermann + @license: GNU GPL, see COPYING for details. +""" + +import string, time, re, Cookie +from MoinMoin import config, user, util, wikiutil +from MoinMoin.util import web, mail, datetime +from MoinMoin.widget import html + +_debug = 0 + + +############################################################################# +### Form POST Handling +############################################################################# + +def savedata(request): + """ Handle POST request of the user preferences form. + + Return error msg or None. + """ + return UserSettingsHandler(request).handleData() + + +class UserSettingsHandler: + + def __init__(self, request): + """ Initialize user settings form. """ + self.request = request + self._ = request.getText + self.cfg = request.cfg + + def decodePageList(self, key): + """ Decode list of pages from form input + + Each line is a page name, empty lines ignored. + + Items can use '_' as spaces, needed by [name_with_spaces label] + format used in quicklinks. We do not touch those names here, the + underscores are handled later by the theme code. + + @param key: the form key to get + @rtype: list of unicode strings + @return: list of normalized names + """ + text = self.request.form.get(key, [''])[0] + text = text.replace('\r', '') + items = [] + for item in text.split('\n'): + item = item.strip() + if not item: + continue + # Normalize names - except [name_with_spaces label] + if not (item.startswith('[') and item.endswith(']')): + item = self.request.normalizePagename(item) + items.append(item) + return items + + def handleData(self): + _ = self._ + form = self.request.form + + if form.has_key('logout'): + # clear the cookie in the browser and locally. Does not + # check if we have a valid user logged, just make sure we + # don't have one after this call. + self.request.deleteCookie() + return _("Cookie deleted. You are now logged out.") + + if form.has_key('login_sendmail'): + if not self.cfg.mail_smarthost: + return _("""This wiki is not enabled for mail processing. +Contact the owner of the wiki, who can enable email.""") + try: + email = form['email'][0].lower() + except KeyError: + return _("Please provide a valid email address!") + + text = '' + users = user.getUserList(self.request) + for uid in users: + theuser = user.User(self.request, uid) + if theuser.valid and theuser.email.lower() == email: + text = "%s\n\nID: %s\nName: %s\nPassword: %s\nLogin URL: %s/?action=userform&uid=%s" % ( + text, theuser.id, theuser.name, theuser.enc_password, self.request.getBaseURL(), theuser.id) + + if not text: + return _("Found no account matching the given email address '%(email)s'!") % {'email': wikiutil.escape(email)} + + mailok, msg = util.mail.sendmail(self.request, [email], + 'Your wiki account data', text, mail_from=self.cfg.mail_from) + return wikiutil.escape(msg) + + if form.has_key('login'): + # Trying to login with a user name and a password + + # Require valid user name + name = form.get('username', [''])[0] + if not user.isValidName(self.request, name): + return _("""Invalid user name {{{'%s'}}}. +Name may contain any Unicode alpha numeric character, with optional one +space between words. Group page name is not allowed.""") % wikiutil.escape(name) + + # Check that user exists + if not user.getUserId(self.request, name): + return _('Unknown user name: {{{"%s"}}}. Please enter' + ' user name and password.') % name + + # Require password + password = form.get('password',[None])[0] + if not password: + return _("Missing password. Please enter user name and" + " password.") + + # Load the user data and check for validness + theuser = user.User(self.request, name=name, password=password) + if not theuser.valid: + return _("Sorry, wrong password.") + + # Save the user and send a cookie + self.request.user = theuser + self.request.setCookie() + + elif form.has_key('uid'): + # Trying to login with the login URL, soon to be removed! + try: + uid = form['uid'][0] + except KeyError: + return _("Bad relogin URL.") + + # Load the user data and check for validness + theuser = user.User(self.request, uid) + if not theuser.valid: + return _("Unknown user.") + + # Save the user and send a cookie + self.request.user = theuser + self.request.setCookie() + + else: + # Save user profile + theuser = user.User(self.request) + + # Require non-empty name + try: + theuser.name = form['username'][0] + except KeyError: + return _("Empty user name. Please enter a user name.") + + # Don't allow users with invalid names + if not user.isValidName(self.request, theuser.name): + return _("""Invalid user name {{{'%s'}}}. +Name may contain any Unicode alpha numeric character, with optional one +space between words. Group page name is not allowed.""") % wikiutil.escape(theuser.name) + + # Is this an existing user trying to change information or a new user? + # Name required to be unique. Check if name belong to another user. + newuser = 1 + if user.getUserId(self.request, theuser.name): + if theuser.name != self.request.user.name: + return _("This user name already belongs to somebody else.") + else: + newuser = 0 + + # try to get the password and pw repeat + password = form.get('password', [''])[0] + password2 = form.get('password2',[''])[0] + + # Check if password is given and matches with password repeat + if password != password2: + return _("Passwords don't match!") + if not password and newuser: + return _("Please specify a password!") + # Encode password + if password and not password.startswith('{SHA}'): + try: + theuser.enc_password = user.encodePassword(password) + except UnicodeError, err: + # Should never happen + return "Can't encode password: %s" % str(err) + + # try to get the (optional) email + email = form.get('email', [''])[0] + theuser.email = email.strip() + + # Require email if acl is enabled + if not theuser.email and self.cfg.acl_enabled: + return _("Please provide your email address. If you loose your" + " login information, you can get it by email.") + + # Email required to be unique + # See also MoinMoin/scripts/moin_usercheck.py + if theuser.email: + users = user.getUserList(self.request) + for uid in users: + if uid == theuser.id: + continue + thisuser = user.User(self.request, uid) + if thisuser.email == theuser.email: + return _("This email already belongs to somebody else.") + + + # editor size + theuser.edit_rows = util.web.getIntegerInput(self.request, 'edit_rows', theuser.edit_rows, 10, 60) + + # time zone + theuser.tz_offset = util.web.getIntegerInput(self.request, 'tz_offset', theuser.tz_offset, -84600, 84600) + + # datetime format + try: + dt_d_combined = UserSettings._date_formats.get(form['datetime_fmt'][0], '') + theuser.datetime_fmt, theuser.date_fmt = dt_d_combined.split(' & ') + except (KeyError, ValueError): + pass + + # try to get the (optional) theme + theme_name = form.get('theme_name', [self.cfg.theme_default])[0] + if theme_name != theuser.theme_name: + # if the theme has changed, load the new theme + # so the user has a direct feedback + # WARNING: this should be refactored (i.e. theme load + # after userform handling), cause currently the + # already loaded theme is just replaced (works cause + # nothing has been emitted yet) + theuser.theme_name = theme_name + if self.request.loadTheme(theuser.theme_name) > 0: + theme_name = wikiutil.escape(theme_name) + return _("The theme '%(theme_name)s' could not be loaded!") % locals() + + # User CSS URL + theuser.css_url = form.get('css_url', [''])[0] + + # try to get the (optional) preferred language + theuser.language = form.get('language', [''])[0] + + # checkbox options + if not newuser: + for key, label in user.User._checkbox_fields: + value = form.get(key, ["0"])[0] + try: + value = int(value) + except ValueError: + pass + else: + setattr(theuser, key, value) + + # quicklinks for navibar + theuser.quicklinks = self.decodePageList('quicklinks') + + # subscription for page change notification + theuser.subscribed_pages = self.decodePageList('subscribed_pages') + + # save data and send cookie + theuser.save() + self.request.user = theuser + self.request.setCookie() + + result = _("User preferences saved!") + if _debug: + result = result + util.dumpFormData(form) + return result + + +############################################################################# +### Form Generation +############################################################################# + +class UserSettings: + """ User login and settings management. """ + + _date_formats = { # datetime_fmt & date_fmt + 'iso': '%Y-%m-%d %H:%M:%S & %Y-%m-%d', + 'us': '%m/%d/%Y %I:%M:%S %p & %m/%d/%Y', + 'euro': '%d.%m.%Y %H:%M:%S & %d.%m.%Y', + 'rfc': '%a %b %d %H:%M:%S %Y & %a %b %d %Y', + } + + def __init__(self, request): + """ Initialize user settings form. + """ + self.request = request + self._ = request.getText + self.cfg = request.cfg + + def _tz_select(self): + """ Create time zone selection. """ + tz = 0 + if self.request.user.valid: + tz = int(self.request.user.tz_offset) + + options = [] + now = time.time() + for halfhour in range(-47, 48): + offset = halfhour * 1800 + t = now + offset + + options.append(( + str(offset), + '%s [%s%s:%s]' % ( + time.strftime(self.cfg.datetime_fmt, util.datetime.tmtuple(t)), + "+-"[offset < 0], + string.zfill("%d" % (abs(offset) / 3600), 2), + string.zfill("%d" % (abs(offset) % 3600 / 60), 2), + ), + )) + + return util.web.makeSelection('tz_offset', options, str(tz)) + + + def _dtfmt_select(self): + """ Create date format selection. """ + _ = self._ + try: + dt_d_combined = '%s & %s' % (self.request.user.datetime_fmt, self.request.user.date_fmt) + selected = [ + k for k, v in self._date_formats.items() + if v == dt_d_combined][0] + except IndexError: + selected = '' + options = [('', _('Default'))] + self._date_formats.items() + + return util.web.makeSelection('datetime_fmt', options, selected) + + + def _lang_select(self): + """ Create language selection. """ + from MoinMoin import i18n + from MoinMoin.i18n import NAME + _ = self._ + cur_lang = self.request.user.valid and self.request.user.language or '' + langs = i18n.wikiLanguages().items() + langs.sort(lambda x,y,NAME=NAME: cmp(x[1][NAME], y[1][NAME])) + options = [('', _('', formatted=False))] + for lang in langs: + name = lang[1][NAME] + options.append((lang[0], name)) + + return util.web.makeSelection('language', options, cur_lang) + + def _theme_select(self): + """ Create theme selection. """ + cur_theme = self.request.user.valid and self.request.user.theme_name or self.cfg.theme_default + options = [] + for theme in wikiutil.getPlugins('theme', self.request.cfg): + options.append((theme, theme)) + + return util.web.makeSelection('theme_name', options, cur_theme) + + def make_form(self): + """ Create the FORM, and the TABLE with the input fields + """ + sn = self.request.getScriptname() + pi = self.request.getPathinfo() + action = u"%s%s" % (sn, pi) + self._form = html.FORM(action=action) + self._table = html.TABLE(border="0") + + # Use the user interface language and direction + lang_attr = self.request.theme.ui_lang_attr() + self._form.append(html.Raw('
' % lang_attr)) + + self._form.append(html.INPUT(type="hidden", name="action", value="userform")) + self._form.append(self._table) + self._form.append(html.Raw("
")) + + + def make_row(self, label, cell, **kw): + """ Create a row in the form table. + """ + self._table.append(html.TR().extend([ + html.TD(**kw).extend([html.B().append(label), ' ']), + html.TD().extend(cell), + ])) + + + def asHTML(self): + """ Create the complete HTML form code. """ + _ = self._ + self.make_form() + + if self.request.user.valid: + # User preferences interface + buttons = [ + ('save', _('Save')), + ('logout', _('Logout')), + ] + else: + # Login / register interface + buttons = [ + # IMPORTANT: login should be first to be the default + # button when a user click enter. + ('login', _('Login')), + ("save", _('Create Profile')), + ] + if self.cfg.mail_smarthost: + buttons.append(("login_sendmail", _('Mail me my account data'))) + + self.make_row(_('Name'), [ + html.INPUT( + type="text", size="36", name="username", value=self.request.user.name + ), + ' ', _('(Use FirstnameLastname)', formatted=False), + ]) + + self.make_row(_('Password'), [ + html.INPUT( + type="password", size="36", name="password", + ), + ' ', + ]) + + self.make_row(_('Password repeat'), [ + html.INPUT( + type="password", size="36", name="password2", + ), + ' ', _('(Only when changing passwords)'), + ]) + + self.make_row(_('Email'), [ + html.INPUT( + type="text", size="36", name="email", value=self.request.user.email + ), + ' ', + ]) + + # Show options only if already logged in + if self.request.user.valid: + + if not self.cfg.theme_force: + self.make_row(_('Preferred theme'), [self._theme_select()]) + + self.make_row(_('User CSS URL'), [ + html.INPUT( + type="text", size="40", name="css_url", value=self.request.user.css_url + ), + ' ', _('(Leave it empty for disabling user CSS)'), + ]) + + self.make_row(_('Editor size'), [ + html.INPUT(type="text", size="3", maxlength="3", + name="edit_rows", value=str(self.request.user.edit_rows)), + ]) + + self.make_row(_('Time zone'), [ + _('Your time is'), ' ', + self._tz_select(), + html.BR(), + _('Server time is'), ' ', + time.strftime(self.cfg.datetime_fmt, util.datetime.tmtuple()), + ' (UTC)', + ]) + + self.make_row(_('Date format'), [self._dtfmt_select()]) + + self.make_row(_('Preferred language'), [self._lang_select()]) + + # boolean user options + bool_options = [] + checkbox_fields = user.User._checkbox_fields + _ = self.request.getText + checkbox_fields.sort(lambda a, b: cmp(a[1](_), b[1](_))) + for key, label in checkbox_fields: + bool_options.extend([ + html.INPUT(type="checkbox", name=key, value="1", + checked=getattr(self.request.user, key, 0)), + ' ', label(_), html.BR(), + ]) + self.make_row(_('General options'), bool_options, valign="top") + + self.make_row(_('Quick links'), [ + html.TEXTAREA(name="quicklinks", rows="6", cols="50") + .append('\n'.join(self.request.user.getQuickLinks())), + ], valign="top") + + # subscribed pages + if self.cfg.mail_smarthost: + # Get list of subscribe pages, DO NOT sort! it should + # stay in the order the user entered it in his input + # box. + notifylist = self.request.user.getSubscriptionList() + + warning = [] + if not self.request.user.email: + warning = [ + html.BR(), + html.SMALL(Class="warning").append( + _("This list does not work, unless you have" + " entered a valid email address!") + )] + + self.make_row( + html.Raw(_('Subscribed wiki pages (one regex per line)')), + [html.TEXTAREA(name="subscribed_pages", rows="6", cols="50").append( + '\n'.join(notifylist)), + ] + warning, + valign="top" + ) + + # Add buttons + button_cell = [] + for name, label in buttons: + button_cell.extend([ + html.INPUT(type="submit", name=name, value=label), + ' ', + ]) + self.make_row('', button_cell) + + return unicode(self._form) + + +def getUserForm(request): + """ Return HTML code for the user settings. """ + return UserSettings(request).asHTML() + + +############################################################################# +### User account administration +############################################################################# + +def do_user_browser(request): + """ Browser for SystemAdmin macro. """ + from MoinMoin.util.dataset import TupleDataset, Column + from MoinMoin.Page import Page + _ = request.getText + + data = TupleDataset() + data.columns = [ + #Column('id', label=('ID'), align='right'), + Column('name', label=('Username')), + Column('email', label=('Email')), + #Column('action', label=_('Action')), + ] + + # Iterate over users + for uid in user.getUserList(request): + account = user.User(request, uid) + + userhomepage = Page(request, account.name) + if userhomepage.exists(): + namelink = userhomepage.link_to(request) + else: + namelink = account.name + + data.addRow(( + #request.formatter.code(1) + uid + request.formatter.code(0), + request.formatter.rawHTML(namelink), + (request.formatter.url(1, 'mailto:' + account.email, 'external', pretty_url=1, unescaped=1) + + request.formatter.text(account.email) + + request.formatter.url(0)), + #'', + )) + + if data: + from MoinMoin.widget.browser import DataBrowserWidget + + browser = DataBrowserWidget(request) + browser.setData(data) + return browser.toHTML() + + # No data + return '' +