~ubuntu-branches/ubuntu/natty/moin/natty-updates

« back to all changes in this revision

Viewing changes to MoinMoin/userprefs/prefs.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2008-06-22 21:17:13 UTC
  • mfrom: (0.9.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080622211713-fpo2zrq3s5dfecxg
Tags: 1.7.0-3
Simplify /etc/moin/wikilist format: "USER URL" (drop unneeded middle
CONFIG_DIR that was wrongly advertised as DATA_DIR).  Make
moin-mass-migrate handle both formats and warn about deprecation of
the old one.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: iso-8859-1 -*-
 
2
"""
 
3
    MoinMoin - Preferences Form
 
4
 
 
5
    @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
 
6
                2003-2007 MoinMoin:ThomasWaldmann
 
7
    @license: GNU GPL, see COPYING for details.
 
8
"""
 
9
 
 
10
import time
 
11
from MoinMoin import user, util, wikiutil, events
 
12
from MoinMoin.widget import html
 
13
from MoinMoin.userprefs import UserPrefBase
 
14
 
 
15
 
 
16
#################################################################
 
17
# This is still a mess.
 
18
#
 
19
# The plan for refactoring would be:
 
20
# split the plugin into multiple preferences pages:
 
21
#    - account details (name, email, timezone, ...)
 
22
#    - wiki settings (editor, fancy diffs, theme, ...)
 
23
#    - quick links (or leave in wiki settings?)
 
24
####
 
25
 
 
26
 
 
27
_debug = 0
 
28
 
 
29
class Settings(UserPrefBase):
 
30
    def __init__(self, request):
 
31
        """ Initialize user settings form. """
 
32
        UserPrefBase.__init__(self, request)
 
33
        self.request = request
 
34
        self._ = request.getText
 
35
        self.cfg = request.cfg
 
36
        _ = self._
 
37
        self.title = _("Preferences")
 
38
        self.name = 'prefs'
 
39
 
 
40
    def _decode_pagelist(self, key):
 
41
        """ Decode list of pages from form input
 
42
 
 
43
        Each line is a page name, empty lines ignored.
 
44
 
 
45
        @param key: the form key to get
 
46
        @rtype: list of unicode strings
 
47
        @return: list of normalized names
 
48
        """
 
49
        text = self.request.form.get(key, [''])[0]
 
50
        text = text.replace('\r', '')
 
51
        items = []
 
52
        for item in text.split('\n'):
 
53
            item = item.strip()
 
54
            if not item:
 
55
                continue
 
56
            items.append(item)
 
57
        return items
 
58
 
 
59
    def _save_user_prefs(self):
 
60
        _ = self._
 
61
        form = self.request.form
 
62
        request = self.request
 
63
 
 
64
        if request.request_method != 'POST':
 
65
            return
 
66
 
 
67
        if not 'name' in request.user.auth_attribs:
 
68
            # Require non-empty name
 
69
            new_name = form.get('name', [request.user.name])[0]
 
70
 
 
71
            # Don't allow changing the name to an invalid one
 
72
            if not user.isValidName(request, new_name):
 
73
                return 'error', _("""Invalid user name {{{'%s'}}}.
 
74
Name may contain any Unicode alpha numeric character, with optional one
 
75
space between words. Group page name is not allowed.""", wiki=True) % wikiutil.escape(new_name)
 
76
 
 
77
            # Is this an existing user trying to change information or a new user?
 
78
            # Name required to be unique. Check if name belong to another user.
 
79
            existing_id = user.getUserId(request, new_name)
 
80
            if existing_id is not None and existing_id != request.user.id:
 
81
                return 'error', _("This user name already belongs to somebody else.")
 
82
 
 
83
            if not new_name:
 
84
                return 'error', _("Empty user name. Please enter a user name.")
 
85
 
 
86
            # done sanity checking the name, set it
 
87
            request.user.name = new_name
 
88
 
 
89
 
 
90
        if not 'email' in request.user.auth_attribs:
 
91
            # try to get the email
 
92
            new_email = wikiutil.clean_input(form.get('email', [request.user.email])[0])
 
93
            new_email = new_email.strip()
 
94
 
 
95
            # Require email
 
96
            if not new_email and 'email' not in request.cfg.user_form_remove:
 
97
                return 'error', _("Please provide your email address. If you lose your"
 
98
                                  " login information, you can get it by email.")
 
99
 
 
100
            # Email should be unique - see also MoinMoin/script/accounts/moin_usercheck.py
 
101
            if new_email and request.cfg.user_email_unique:
 
102
                other = user.get_by_email_address(request, new_email)
 
103
                if other is not None and other.id != request.user.id:
 
104
                    return 'error', _("This email already belongs to somebody else.")
 
105
 
 
106
            # done checking the email, set it
 
107
            request.user.email = new_email
 
108
 
 
109
 
 
110
        if not 'jid' in request.user.auth_attribs:
 
111
            # try to get the jid
 
112
            new_jid = wikiutil.clean_input(form.get('jid', [''])[0]).strip()
 
113
 
 
114
            jid_changed = request.user.jid != new_jid
 
115
            previous_jid = request.user.jid
 
116
 
 
117
            if new_jid and request.cfg.user_jid_unique:
 
118
                other = user.get_by_jabber_id(request, new_jid)
 
119
                if other is not None and other.id != request.user.id:
 
120
                    return 'error', _("This jabber id already belongs to somebody else.")
 
121
 
 
122
            if jid_changed:
 
123
                set_event = events.JabberIDSetEvent(request, new_jid)
 
124
                unset_event = events.JabberIDUnsetEvent(request, previous_jid)
 
125
                events.send_event(unset_event)
 
126
                events.send_event(set_event)
 
127
 
 
128
            # done checking the JID, set it
 
129
            request.user.jid = new_jid
 
130
 
 
131
 
 
132
        if not 'aliasname' in request.user.auth_attribs:
 
133
            # aliasname
 
134
            request.user.aliasname = wikiutil.clean_input(form.get('aliasname', [''])[0])
 
135
 
 
136
        # editor size
 
137
        request.user.edit_rows = util.web.getIntegerInput(request, 'edit_rows',
 
138
                                                          request.user.edit_rows, 10, 60)
 
139
 
 
140
        # try to get the editor
 
141
        request.user.editor_default = form.get('editor_default', [self.cfg.editor_default])[0]
 
142
        request.user.editor_ui = form.get('editor_ui', [self.cfg.editor_ui])[0]
 
143
 
 
144
        # time zone
 
145
        request.user.tz_offset = util.web.getIntegerInput(request, 'tz_offset',
 
146
                                                          request.user.tz_offset, -84600, 84600)
 
147
 
 
148
        # datetime format
 
149
        try:
 
150
            dt_d_combined = Settings._date_formats.get(form['datetime_fmt'][0], '')
 
151
            request.user.datetime_fmt, request.user.date_fmt = dt_d_combined.split(' & ')
 
152
        except (KeyError, ValueError):
 
153
            request.user.datetime_fmt = '' # default
 
154
            request.user.date_fmt = '' # default
 
155
 
 
156
        # try to get the (optional) theme
 
157
        theme_name = form.get('theme_name', [self.cfg.theme_default])[0]
 
158
        if theme_name != request.user.theme_name:
 
159
            # if the theme has changed, load the new theme
 
160
            # so the user has a direct feedback
 
161
            # WARNING: this should be refactored (i.e. theme load
 
162
            # after userform handling), cause currently the
 
163
            # already loaded theme is just replaced (works cause
 
164
            # nothing has been emitted yet)
 
165
            request.user.theme_name = theme_name
 
166
            if request.loadTheme(theme_name) > 0:
 
167
                theme_name = wikiutil.escape(theme_name)
 
168
                return 'error', _("The theme '%(theme_name)s' could not be loaded!") % locals()
 
169
 
 
170
        # try to get the (optional) preferred language
 
171
        request.user.language = form.get('language', [''])[0]
 
172
        if request.user.language == u'': # For language-statistics
 
173
            from MoinMoin import i18n
 
174
            request.user.real_language = i18n.get_browser_language(request)
 
175
        else:
 
176
            request.user.real_language = ''
 
177
 
 
178
        # I want to handle all inputs from user_form_fields, but
 
179
        # don't want to handle the cases that have already been coded
 
180
        # above.
 
181
        # This is a horribly fragile kludge that's begging to break.
 
182
        # Something that might work better would be to define a
 
183
        # handler for each form field, instead of stuffing them all in
 
184
        # one long and inextensible method.  That would allow for
 
185
        # plugins to provide methods to validate their fields as well.
 
186
        already_handled = ['name', 'email',
 
187
                           'aliasname', 'edit_rows', 'editor_default',
 
188
                           'editor_ui', 'tz_offset', 'datetime_fmt',
 
189
                           'theme_name', 'language', 'real_language', 'jid']
 
190
        for field in self.cfg.user_form_fields:
 
191
            key = field[0]
 
192
            if ((key in self.cfg.user_form_disable)
 
193
                or (key in already_handled)):
 
194
                continue
 
195
            default = self.cfg.user_form_defaults[key]
 
196
            value = form.get(key, [default])[0]
 
197
            setattr(request.user, key, value)
 
198
 
 
199
        # checkbox options
 
200
        for key, label in self.cfg.user_checkbox_fields:
 
201
            if key not in self.cfg.user_checkbox_disable and key not in self.cfg.user_checkbox_remove:
 
202
                value = form.get(key, ["0"])[0]
 
203
                try:
 
204
                    value = int(value)
 
205
                except ValueError:
 
206
                    pass
 
207
                else:
 
208
                    setattr(request.user, key, value)
 
209
 
 
210
        # quicklinks for navibar
 
211
        request.user.quicklinks = self._decode_pagelist('quicklinks')
 
212
 
 
213
        # save data
 
214
        request.user.save()
 
215
        if request.user.disabled:
 
216
            # set valid to false so the current request won't
 
217
            # show the user as logged-in any more
 
218
            request.user.valid = False
 
219
 
 
220
        result = _("User preferences saved!")
 
221
        if _debug:
 
222
            result = result + util.dumpFormData(form)
 
223
        return result
 
224
 
 
225
 
 
226
    def handle_form(self):
 
227
        _ = self._
 
228
        form = self.request.form
 
229
 
 
230
        if form.has_key('cancel'):
 
231
            return
 
232
 
 
233
        if form.has_key('save'): # Save user profile
 
234
            return self._save_user_prefs()
 
235
 
 
236
    # form generation part
 
237
 
 
238
    _date_formats = { # datetime_fmt & date_fmt
 
239
        'iso': '%Y-%m-%d %H:%M:%S & %Y-%m-%d',
 
240
        'us': '%m/%d/%Y %I:%M:%S %p & %m/%d/%Y',
 
241
        'euro': '%d.%m.%Y %H:%M:%S & %d.%m.%Y',
 
242
        'rfc': '%a %b %d %H:%M:%S %Y & %a %b %d %Y',
 
243
    }
 
244
 
 
245
    def _tz_select(self):
 
246
        """ Create time zone selection. """
 
247
        tz = 0
 
248
        if self.request.user.valid:
 
249
            tz = int(self.request.user.tz_offset)
 
250
 
 
251
        options = []
 
252
        now = time.time()
 
253
        for halfhour in range(-47, 48):
 
254
            offset = halfhour * 1800
 
255
            t = now + offset
 
256
 
 
257
            options.append((
 
258
                str(offset),
 
259
                '%s [%s%s:%s]' % (
 
260
                    time.strftime(self.cfg.datetime_fmt, util.timefuncs.tmtuple(t)),
 
261
                    "+-"[offset < 0],
 
262
                    "%02d" % (abs(offset) / 3600),
 
263
                    "%02d" % (abs(offset) % 3600 / 60),
 
264
                ),
 
265
            ))
 
266
 
 
267
        return util.web.makeSelection('tz_offset', options, str(tz))
 
268
 
 
269
 
 
270
    def _dtfmt_select(self):
 
271
        """ Create date format selection. """
 
272
        _ = self._
 
273
        try:
 
274
            dt_d_combined = '%s & %s' % (self.request.user.datetime_fmt, self.request.user.date_fmt)
 
275
            selected = [
 
276
                k for k, v in self._date_formats.items()
 
277
                    if v == dt_d_combined][0]
 
278
        except IndexError:
 
279
            selected = ''
 
280
        options = [('', _('Default'))] + self._date_formats.items()
 
281
 
 
282
        return util.web.makeSelection('datetime_fmt', options, selected)
 
283
 
 
284
 
 
285
    def _lang_select(self):
 
286
        """ Create language selection. """
 
287
        from MoinMoin import i18n
 
288
        _ = self._
 
289
        cur_lang = self.request.user.valid and self.request.user.language or ''
 
290
        langs = i18n.wikiLanguages().items()
 
291
        langs.sort(lambda x, y: cmp(x[1]['x-language'], y[1]['x-language']))
 
292
        options = [('', _('<Browser setting>'))]
 
293
        for lang in langs:
 
294
            name = lang[1]['x-language']
 
295
            options.append((lang[0], name))
 
296
 
 
297
        return util.web.makeSelection('language', options, cur_lang)
 
298
 
 
299
    def _theme_select(self):
 
300
        """ Create theme selection. """
 
301
        cur_theme = self.request.user.valid and self.request.user.theme_name or self.cfg.theme_default
 
302
        options = [("<default>", "<%s>" % self._("Default"))]
 
303
        for theme in wikiutil.getPlugins('theme', self.request.cfg):
 
304
            options.append((theme, theme))
 
305
 
 
306
        return util.web.makeSelection('theme_name', options, cur_theme)
 
307
 
 
308
    def _editor_default_select(self):
 
309
        """ Create editor selection. """
 
310
        editor_default = self.request.user.valid and self.request.user.editor_default or self.cfg.editor_default
 
311
        options = [("<default>", "<%s>" % self._("Default"))]
 
312
        for editor in ['text', 'gui', ]:
 
313
            options.append((editor, editor))
 
314
        return util.web.makeSelection('editor_default', options, editor_default)
 
315
 
 
316
    def _editor_ui_select(self):
 
317
        """ Create editor selection. """
 
318
        editor_ui = self.request.user.valid and self.request.user.editor_ui or self.cfg.editor_ui
 
319
        options = [("<default>", "<%s>" % self._("Default")),
 
320
                   ("theonepreferred", self._("the one preferred")),
 
321
                   ("freechoice", self._("free choice")),
 
322
                  ]
 
323
        return util.web.makeSelection('editor_ui', options, editor_ui)
 
324
 
 
325
 
 
326
    def create_form(self):
 
327
        """ Create the complete HTML form code. """
 
328
        _ = self._
 
329
        request = self.request
 
330
        self._form = self.make_form()
 
331
 
 
332
        if request.user.valid:
 
333
            buttons = [('save', _('Save')), ('cancel', _('Cancel')), ]
 
334
            uf_remove = self.cfg.user_form_remove
 
335
            uf_disable = self.cfg.user_form_disable
 
336
            for attr in request.user.auth_attribs:
 
337
                uf_disable.append(attr)
 
338
            for key, label, type, length, textafter in self.cfg.user_form_fields:
 
339
                default = self.cfg.user_form_defaults[key]
 
340
                if not key in uf_remove:
 
341
                    if key in uf_disable:
 
342
                        self.make_row(_(label),
 
343
                                  [html.INPUT(type=type, size=length, name=key, disabled="disabled",
 
344
                                   value=getattr(request.user, key)), ' ', _(textafter), ])
 
345
                    else:
 
346
                        self.make_row(_(label),
 
347
                                  [html.INPUT(type=type, size=length, name=key, value=getattr(request.user, key)), ' ', _(textafter), ])
 
348
 
 
349
            if not self.cfg.theme_force and not "theme_name" in self.cfg.user_form_remove:
 
350
                self.make_row(_('Preferred theme'), [self._theme_select()])
 
351
 
 
352
            if not self.cfg.editor_force:
 
353
                if not "editor_default" in self.cfg.user_form_remove:
 
354
                    self.make_row(_('Editor Preference'), [self._editor_default_select()])
 
355
                if not "editor_ui" in self.cfg.user_form_remove:
 
356
                    self.make_row(_('Editor shown on UI'), [self._editor_ui_select()])
 
357
 
 
358
            if not "tz_offset" in self.cfg.user_form_remove:
 
359
                self.make_row(_('Time zone'), [
 
360
                    _('Your time is'), ' ',
 
361
                    self._tz_select(),
 
362
                    html.BR(),
 
363
                    _('Server time is'), ' ',
 
364
                    time.strftime(self.cfg.datetime_fmt, util.timefuncs.tmtuple()),
 
365
                    ' (UTC)',
 
366
                ])
 
367
 
 
368
            if not "datetime_fmt" in self.cfg.user_form_remove:
 
369
                self.make_row(_('Date format'), [self._dtfmt_select()])
 
370
 
 
371
            if not "language" in self.cfg.user_form_remove:
 
372
                self.make_row(_('Preferred language'), [self._lang_select()])
 
373
 
 
374
            # boolean user options
 
375
            bool_options = []
 
376
            checkbox_fields = self.cfg.user_checkbox_fields
 
377
            checkbox_fields.sort(lambda a, b: cmp(a[1](_), b[1](_)))
 
378
            for key, label in checkbox_fields:
 
379
                if not key in self.cfg.user_checkbox_remove:
 
380
                    bool_options.extend([
 
381
                        html.INPUT(type="checkbox", name=key, value="1",
 
382
                            checked=getattr(request.user, key, 0),
 
383
                            disabled=key in self.cfg.user_checkbox_disable and True or None),
 
384
                        ' ', label(_), html.BR(),
 
385
                    ])
 
386
            self.make_row(_('General options'), bool_options, valign="top")
 
387
 
 
388
            self.make_row(_('Quick links'), [
 
389
                html.TEXTAREA(name="quicklinks", rows="6", cols="50")
 
390
                    .append('\n'.join(request.user.getQuickLinks())),
 
391
            ], valign="top")
 
392
 
 
393
            self._form.append(html.INPUT(type="hidden", name="action", value="userprefs"))
 
394
            self._form.append(html.INPUT(type="hidden", name="handler", value="prefs"))
 
395
 
 
396
        # Add buttons
 
397
        button_cell = []
 
398
        for name, label in buttons:
 
399
            if not name in self.cfg.user_form_remove:
 
400
                button_cell.extend([
 
401
                    html.INPUT(type="submit", name=name, value=label),
 
402
                    ' ',
 
403
                ])
 
404
        self.make_row('', button_cell)
 
405
 
 
406
        return unicode(self._form)