Back to index

moin  1.9.0~rc2
prefs.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - Preferences Form
00004 
00005     @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
00006                 2003-2007 MoinMoin:ThomasWaldmann
00007     @license: GNU GPL, see COPYING for details.
00008 """
00009 
00010 import time
00011 from MoinMoin import user, util, wikiutil, events
00012 from MoinMoin.theme import load_theme_fallback
00013 from MoinMoin.widget import html
00014 from MoinMoin.userprefs import UserPrefBase
00015 
00016 
00017 #################################################################
00018 # This is still a mess.
00019 #
00020 # The plan for refactoring would be:
00021 # split the plugin into multiple preferences pages:
00022 #    - account details (name, email, timezone, ...)
00023 #    - wiki settings (editor, fancy diffs, theme, ...)
00024 #    - quick links (or leave in wiki settings?)
00025 ####
00026 
00027 
00028 class Settings(UserPrefBase):
00029     def __init__(self, request):
00030         """ Initialize user settings form. """
00031         UserPrefBase.__init__(self, request)
00032         self.request = request
00033         self._ = request.getText
00034         self.cfg = request.cfg
00035         _ = self._
00036         self.title = _("Preferences")
00037         self.name = 'prefs'
00038 
00039     def _decode_pagelist(self, key):
00040         """ Decode list of pages from form input
00041 
00042         Each line is a page name, empty lines ignored.
00043 
00044         @param key: the form key to get
00045         @rtype: list of unicode strings
00046         @return: list of normalized names
00047         """
00048         text = self.request.form.get(key, '')
00049         text = text.replace('\r', '')
00050         items = []
00051         for item in text.split('\n'):
00052             item = item.strip()
00053             if not item:
00054                 continue
00055             items.append(item)
00056         return items
00057 
00058     def _save_user_prefs(self):
00059         _ = self._
00060         form = self.request.form
00061         request = self.request
00062 
00063         if request.method != 'POST':
00064             return
00065 
00066         if not 'name' in request.user.auth_attribs:
00067             # Require non-empty name
00068             new_name = form.get('name', request.user.name)
00069 
00070             # Don't allow changing the name to an invalid one
00071             if not user.isValidName(request, new_name):
00072                 return 'error', _("""Invalid user name {{{'%s'}}}.
00073 Name may contain any Unicode alpha numeric character, with optional one
00074 space between words. Group page name is not allowed.""", wiki=True) % wikiutil.escape(new_name)
00075 
00076             # Is this an existing user trying to change information or a new user?
00077             # Name required to be unique. Check if name belong to another user.
00078             existing_id = user.getUserId(request, new_name)
00079             if existing_id is not None and existing_id != request.user.id:
00080                 return 'error', _("This user name already belongs to somebody else.")
00081 
00082             if not new_name:
00083                 return 'error', _("Empty user name. Please enter a user name.")
00084 
00085             # done sanity checking the name, set it
00086             request.user.name = new_name
00087 
00088 
00089         if not 'email' in request.user.auth_attribs:
00090             # try to get the email
00091             new_email = wikiutil.clean_input(form.get('email', request.user.email))
00092             new_email = new_email.strip()
00093 
00094             # Require email
00095             if not new_email and 'email' not in request.cfg.user_form_remove:
00096                 return 'error', _("Please provide your email address. If you lose your"
00097                                   " login information, you can get it by email.")
00098 
00099             # Email should be unique - see also MoinMoin/script/accounts/moin_usercheck.py
00100             if new_email and request.cfg.user_email_unique:
00101                 other = user.get_by_email_address(request, new_email)
00102                 if other is not None and other.id != request.user.id:
00103                     return 'error', _("This email already belongs to somebody else.")
00104 
00105             # done checking the email, set it
00106             request.user.email = new_email
00107 
00108 
00109         if not 'jid' in request.user.auth_attribs:
00110             # try to get the jid
00111             new_jid = wikiutil.clean_input(form.get('jid', '')).strip()
00112 
00113             jid_changed = request.user.jid != new_jid
00114             previous_jid = request.user.jid
00115 
00116             if new_jid and request.cfg.user_jid_unique:
00117                 other = user.get_by_jabber_id(request, new_jid)
00118                 if other is not None and other.id != request.user.id:
00119                     return 'error', _("This jabber id already belongs to somebody else.")
00120 
00121             if jid_changed:
00122                 set_event = events.JabberIDSetEvent(request, new_jid)
00123                 unset_event = events.JabberIDUnsetEvent(request, previous_jid)
00124                 events.send_event(unset_event)
00125                 events.send_event(set_event)
00126 
00127             # done checking the JID, set it
00128             request.user.jid = new_jid
00129 
00130 
00131         if not 'aliasname' in request.user.auth_attribs:
00132             # aliasname
00133             request.user.aliasname = wikiutil.clean_input(form.get('aliasname', ''))
00134 
00135         # editor size
00136         request.user.edit_rows = util.web.getIntegerInput(request, 'edit_rows',
00137                                                           request.user.edit_rows, 10, 60)
00138 
00139         # try to get the editor
00140         request.user.editor_default = form.get('editor_default', self.cfg.editor_default)
00141         request.user.editor_ui = form.get('editor_ui', self.cfg.editor_ui)
00142 
00143         # time zone
00144         request.user.tz_offset = util.web.getIntegerInput(request, 'tz_offset',
00145                                                           request.user.tz_offset, -84600, 84600)
00146 
00147         # datetime format
00148         try:
00149             dt_d_combined = Settings._date_formats.get(form['datetime_fmt'], '')
00150             request.user.datetime_fmt, request.user.date_fmt = dt_d_combined.split(' & ')
00151         except (KeyError, ValueError):
00152             request.user.datetime_fmt = '' # default
00153             request.user.date_fmt = '' # default
00154 
00155         # try to get the (optional) theme
00156         theme_name = form.get('theme_name', self.cfg.theme_default)
00157         if theme_name != request.user.theme_name:
00158             # if the theme has changed, load the new theme
00159             # so the user has a direct feedback
00160             # WARNING: this should be refactored (i.e. theme load
00161             # after userform handling), cause currently the
00162             # already loaded theme is just replaced (works cause
00163             # nothing has been emitted yet)
00164             request.user.theme_name = theme_name
00165             if load_theme_fallback(request, theme_name) > 0:
00166                 theme_name = wikiutil.escape(theme_name)
00167                 return 'error', _("The theme '%(theme_name)s' could not be loaded!") % locals()
00168 
00169         # try to get the (optional) preferred language
00170         request.user.language = form.get('language', '')
00171         if request.user.language == u'': # For language-statistics
00172             from MoinMoin import i18n
00173             request.user.real_language = i18n.get_browser_language(request)
00174         else:
00175             request.user.real_language = ''
00176 
00177         # I want to handle all inputs from user_form_fields, but
00178         # don't want to handle the cases that have already been coded
00179         # above.
00180         # This is a horribly fragile kludge that's begging to break.
00181         # Something that might work better would be to define a
00182         # handler for each form field, instead of stuffing them all in
00183         # one long and inextensible method.  That would allow for
00184         # plugins to provide methods to validate their fields as well.
00185         already_handled = ['name', 'email',
00186                            'aliasname', 'edit_rows', 'editor_default',
00187                            'editor_ui', 'tz_offset', 'datetime_fmt',
00188                            'theme_name', 'language', 'real_language', 'jid']
00189         for field in self.cfg.user_form_fields:
00190             key = field[0]
00191             if ((key in self.cfg.user_form_disable)
00192                 or (key in already_handled)):
00193                 continue
00194             default = self.cfg.user_form_defaults[key]
00195             value = form.get(key, default)
00196             setattr(request.user, key, value)
00197 
00198         # checkbox options
00199         for key, label in self.cfg.user_checkbox_fields:
00200             if key not in self.cfg.user_checkbox_disable and key not in self.cfg.user_checkbox_remove:
00201                 value = form.get(key, "0")
00202                 try:
00203                     value = int(value)
00204                 except ValueError:
00205                     pass
00206                 else:
00207                     setattr(request.user, key, value)
00208 
00209         # quicklinks for navibar
00210         request.user.quicklinks = self._decode_pagelist('quicklinks')
00211 
00212         # save data
00213         request.user.save()
00214         if request.user.disabled:
00215             # set valid to false so the current request won't
00216             # show the user as logged-in any more
00217             request.user.valid = False
00218 
00219         result = _("User preferences saved!")
00220         return result
00221 
00222 
00223     def handle_form(self):
00224         _ = self._
00225         form = self.request.form
00226 
00227         if 'cancel' in form:
00228             return
00229 
00230         if 'save' in form: # Save user profile
00231             return self._save_user_prefs()
00232 
00233     # form generation part
00234 
00235     _date_formats = { # datetime_fmt & date_fmt
00236         'iso': '%Y-%m-%d %H:%M:%S & %Y-%m-%d',
00237         'us': '%m/%d/%Y %I:%M:%S %p & %m/%d/%Y',
00238         'euro': '%d.%m.%Y %H:%M:%S & %d.%m.%Y',
00239         'rfc': '%a %b %d %H:%M:%S %Y & %a %b %d %Y',
00240     }
00241 
00242     def _tz_select(self, enabled=True):
00243         """ Create time zone selection. """
00244         tz = 0
00245         if self.request.user.valid:
00246             tz = int(self.request.user.tz_offset)
00247 
00248         options = []
00249         now = time.time()
00250         for halfhour in range(-47, 48):
00251             offset = halfhour * 1800
00252             t = now + offset
00253 
00254             options.append((
00255                 str(offset),
00256                 '%s [%s%s:%s]' % (
00257                     time.strftime(self.cfg.datetime_fmt, util.timefuncs.tmtuple(t)),
00258                     "+-"[offset < 0],
00259                     "%02d" % (abs(offset) / 3600),
00260                     "%02d" % (abs(offset) % 3600 / 60),
00261                 ),
00262             ))
00263 
00264         return util.web.makeSelection('tz_offset', options, str(tz), 1, False, enabled)
00265 
00266 
00267     def _dtfmt_select(self):
00268         """ Create date format selection. """
00269         _ = self._
00270         try:
00271             dt_d_combined = '%s & %s' % (self.request.user.datetime_fmt, self.request.user.date_fmt)
00272             selected = [
00273                 k for k, v in self._date_formats.items()
00274                     if v == dt_d_combined][0]
00275         except IndexError:
00276             selected = ''
00277         options = [('', _('Default'))] + self._date_formats.items()
00278 
00279         return util.web.makeSelection('datetime_fmt', options, selected)
00280 
00281 
00282     def _lang_select(self, enabled=True):
00283         """ Create language selection. """
00284         from MoinMoin import i18n
00285         _ = self._
00286         cur_lang = self.request.user.valid and self.request.user.language or ''
00287         langs = i18n.wikiLanguages().items()
00288         langs.sort(lambda x, y: cmp(x[1]['x-language'], y[1]['x-language']))
00289         options = [('', _('<Browser setting>'))]
00290         for lang in langs:
00291             name = lang[1]['x-language']
00292             options.append((lang[0], name))
00293 
00294         return util.web.makeSelection('language', options, cur_lang, 1, False, enabled)
00295 
00296     def _theme_select(self):
00297         """ Create theme selection. """
00298         cur_theme = self.request.user.valid and self.request.user.theme_name or self.cfg.theme_default
00299         options = [("<default>", "<%s>" % self._("Default"))]
00300         for theme in wikiutil.getPlugins('theme', self.request.cfg):
00301             options.append((theme, theme))
00302 
00303         return util.web.makeSelection('theme_name', options, cur_theme)
00304 
00305     def _editor_default_select(self):
00306         """ Create editor selection. """
00307         editor_default = self.request.user.valid and self.request.user.editor_default or self.cfg.editor_default
00308         options = [("<default>", "<%s>" % self._("Default"))]
00309         for editor in ['text', 'gui', ]:
00310             options.append((editor, editor))
00311         return util.web.makeSelection('editor_default', options, editor_default)
00312 
00313     def _editor_ui_select(self):
00314         """ Create editor selection. """
00315         editor_ui = self.request.user.valid and self.request.user.editor_ui or self.cfg.editor_ui
00316         options = [("<default>", "<%s>" % self._("Default")),
00317                    ("theonepreferred", self._("the one preferred")),
00318                    ("freechoice", self._("free choice")),
00319                   ]
00320         return util.web.makeSelection('editor_ui', options, editor_ui)
00321 
00322 
00323     def create_form(self):
00324         """ Create the complete HTML form code. """
00325         _ = self._
00326         request = self.request
00327         self._form = self.make_form()
00328 
00329         if request.user.valid:
00330             buttons = [('save', _('Save')), ('cancel', _('Cancel')), ]
00331             uf_remove = self.cfg.user_form_remove
00332             uf_disable = self.cfg.user_form_disable
00333             for attr in request.user.auth_attribs:
00334                 uf_disable.append(attr)
00335             for key, label, type, length, textafter in self.cfg.user_form_fields:
00336                 default = self.cfg.user_form_defaults[key]
00337                 if not key in uf_remove:
00338                     if key in uf_disable:
00339                         self.make_row(_(label),
00340                                   [html.INPUT(type=type, size=length, name=key, disabled="disabled",
00341                                    value=getattr(request.user, key)), ' ', _(textafter), ])
00342                     else:
00343                         self.make_row(_(label),
00344                                   [html.INPUT(type=type, size=length, name=key, value=getattr(request.user, key)), ' ', _(textafter), ])
00345 
00346             if not self.cfg.theme_force and not "theme_name" in self.cfg.user_form_remove:
00347                 self.make_row(_('Preferred theme'), [self._theme_select()])
00348 
00349             if not self.cfg.editor_force:
00350                 if not "editor_default" in self.cfg.user_form_remove:
00351                     self.make_row(_('Editor Preference'), [self._editor_default_select()])
00352                 if not "editor_ui" in self.cfg.user_form_remove:
00353                     self.make_row(_('Editor shown on UI'), [self._editor_ui_select()])
00354 
00355             if not "tz_offset" in self.cfg.user_form_remove:
00356                 self.make_row(_('Time zone'), [
00357                     _('Your time is'), ' ',
00358                     self._tz_select(),
00359                     html.BR(),
00360                     _('Server time is'), ' ',
00361                     time.strftime(self.cfg.datetime_fmt, util.timefuncs.tmtuple()),
00362                     ' (UTC)',
00363                 ])
00364 
00365             if not "datetime_fmt" in self.cfg.user_form_remove:
00366                 self.make_row(_('Date format'), [self._dtfmt_select()])
00367 
00368             if not "language" in self.cfg.user_form_remove:
00369                 self.make_row(_('Preferred language'), [self._lang_select()])
00370 
00371             # boolean user options
00372             bool_options = []
00373             checkbox_fields = self.cfg.user_checkbox_fields
00374             checkbox_fields.sort(lambda a, b: cmp(a[1](_), b[1](_)))
00375             for key, label in checkbox_fields:
00376                 if not key in self.cfg.user_checkbox_remove:
00377                     bool_options.extend([
00378                         html.INPUT(type="checkbox", name=key, value="1",
00379                             checked=getattr(request.user, key, 0),
00380                             disabled=key in self.cfg.user_checkbox_disable and True or None),
00381                         ' ', label(_), html.BR(),
00382                     ])
00383             self.make_row(_('General options'), bool_options, valign="top")
00384 
00385             self.make_row(_('Quick links'), [
00386                 html.TEXTAREA(name="quicklinks", rows="6", cols="50")
00387                     .append('\n'.join(request.user.getQuickLinks())),
00388             ], valign="top")
00389 
00390             self._form.append(html.INPUT(type="hidden", name="action", value="userprefs"))
00391             self._form.append(html.INPUT(type="hidden", name="handler", value="prefs"))
00392 
00393         # Add buttons
00394         button_cell = []
00395         for name, label in buttons:
00396             if not name in self.cfg.user_form_remove:
00397                 button_cell.extend([
00398                     html.INPUT(type="submit", name=name, value=label),
00399                     ' ',
00400                 ])
00401         self.make_row('', button_cell)
00402 
00403         return unicode(self._form)