Package trac :: Package prefs :: Module web_ui

Source Code for Module trac.prefs.web_ui

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright (C) 2004-2023 Edgewall Software 
  4  # Copyright (C) 2004-2005 Daniel Lundin <[email protected]> 
  5  # All rights reserved. 
  6  # 
  7  # This software is licensed as described in the file COPYING, which 
  8  # you should have received as part of this distribution. The terms 
  9  # are also available at https://trac.edgewall.org/wiki/TracLicense. 
 10  # 
 11  # This software consists of voluntary contributions made by many 
 12  # individuals. For the exact contribution history, see the revision 
 13  # history and logs, available at https://trac.edgewall.org/log/. 
 14  # 
 15  # Author: Daniel Lundin <[email protected]> 
 16   
 17  import math 
 18  import pkg_resources 
 19  import re 
 20   
 21  from trac.core import * 
 22  from trac.prefs.api import IPreferencePanelProvider 
 23  from trac.util import as_float, lazy 
 24  from trac.util.datefmt import all_timezones, get_timezone, localtz 
 25  from trac.util.html import tag 
 26  from trac.util.translation import _, Locale, deactivate,\ 
 27                                    get_available_locales, make_activable 
 28  from trac.web.api import HTTPNotFound, IRequestHandler, \ 
 29                           is_valid_default_handler 
 30  from trac.web.chrome import Chrome, INavigationContributor, \ 
 31                              ITemplateProvider, add_notice, add_stylesheet, \ 
 32                              add_warning 
33 34 35 -class PreferencesModule(Component):
36 """Displays the preference panels and dispatch control to the 37 individual panels""" 38 39 implements(INavigationContributor, IRequestHandler, ITemplateProvider) 40 41 panel_providers = ExtensionPoint(IPreferencePanelProvider) 42 43 # INavigationContributor methods 44
45 - def get_active_navigation_item(self, req):
46 return 'prefs'
47
48 - def get_navigation_items(self, req):
49 panels = self._get_panels(req)[0] 50 if panels: 51 yield 'metanav', 'prefs', tag.a(_("Preferences"), 52 href=req.href.prefs())
53 54 # IRequestHandler methods 55
56 - def match_request(self, req):
57 match = re.match('/prefs(?:/([^/]+))?$', req.path_info) 58 if match: 59 req.args['panel_id'] = match.group(1) 60 return True
61
62 - def process_request(self, req):
63 if req.is_xhr and req.method == 'POST' and 'save_prefs' in req.args: 64 self._do_save_xhr(req) 65 66 panels, providers = self._get_panels(req) 67 if not panels: 68 raise HTTPNotFound(_("No preference panels available")) 69 70 panels = [] 71 child_panels = {} 72 providers = {} 73 for provider in self.panel_providers: 74 for panel in provider.get_preference_panels(req) or []: 75 if len(panel) == 3: 76 name, label, parent = panel 77 child_panels.setdefault(parent, []).append((name, label)) 78 else: 79 name = panel[0] 80 panels.append(panel) 81 providers[name] = provider 82 panels = sorted(panels) 83 84 panel_id = req.args.get('panel_id') 85 if panel_id is None: 86 panel_id = panels[1][0] \ 87 if len(panels) > 1 and panels[0][0] == 'advanced' \ 88 else panels[0][0] 89 chosen_provider = providers.get(panel_id) 90 if not chosen_provider: 91 raise HTTPNotFound(_("Unknown preference panel '%(panel)s'", 92 panel=panel_id)) 93 94 session_data = {'session': req.session} 95 96 # Render child preference panels. 97 chrome = Chrome(self.env) 98 children = [] 99 if child_panels.get(panel_id): 100 for name, label in child_panels[panel_id]: 101 resp = providers[name].render_preference_panel(req, name) 102 ctemplate, cdata = resp[:2] 103 cdata.update(session_data) 104 if len(resp) == 2: 105 rendered = chrome.render_fragment(req, ctemplate, cdata) 106 else: 107 # Backward compatibility with Genshi preference panels 108 # TODO (1.5.1) remove 109 rendered = chrome.render_template(req, ctemplate, cdata, 110 None, fragment=True) 111 children.append((name, label, rendered)) 112 113 resp = chosen_provider.render_preference_panel(req, panel_id) 114 data = resp[1] 115 116 data.update(session_data) 117 data.update({ 118 'active_panel': panel_id, 119 'panels': panels, 120 'children': children, 121 }) 122 123 add_stylesheet(req, 'common/css/prefs.css') 124 return resp
125 126 # ITemplateProvider methods 127
128 - def get_htdocs_dirs(self):
129 return []
130
131 - def get_templates_dirs(self):
132 return [pkg_resources.resource_filename('trac.prefs', 'templates')]
133 134 # Internal methods 135
136 - def _get_panels(self, req):
137 """Return a list of available preference panels.""" 138 panels = [] 139 providers = {} 140 for provider in self.panel_providers: 141 p = list(provider.get_preference_panels(req) or []) 142 for panel in p: 143 providers[panel[0]] = provider 144 panels += p 145 146 return panels, providers
147
148 - def _do_save_xhr(self, req):
149 for key in req.args: 150 if key not in ('save_prefs', 'panel_id', '__FORM_TOKEN'): 151 req.session[key] = req.args[key] 152 req.session.save() 153 req.send_no_content()
154
155 156 -class AdvancedPreferencePanel(Component):
157 158 implements(IPreferencePanelProvider) 159 160 _form_fields = ('newsid',) 161 162 # IPreferencePanelProvider methods 163
164 - def get_preference_panels(self, req):
165 if not req.is_authenticated: 166 yield 'advanced', _("Advanced")
167
168 - def render_preference_panel(self, req, panel):
169 if req.method == 'POST': 170 if 'restore' in req.args: 171 self._do_load(req) 172 else: 173 _do_save(req, panel, self._form_fields) 174 return 'prefs_advanced.html', {'session_id': req.session.sid}
175
176 - def _do_load(self, req):
177 if not req.is_authenticated: 178 oldsid = req.args.get('loadsid') 179 if oldsid: 180 req.session.get_session(oldsid) 181 add_notice(req, _("The session has been loaded."))
182
183 184 -class GeneralPreferencePanel(Component):
185 186 implements(IPreferencePanelProvider) 187 188 _form_fields = ('name', 'email') 189 190 # IPreferencePanelProvider methods 191
192 - def get_preference_panels(self, req):
193 yield None, _("General")
194
195 - def render_preference_panel(self, req, panel):
196 if req.method == 'POST': 197 _do_save(req, panel, self._form_fields) 198 return 'prefs_general.html', {}
199
200 201 -class LocalizationPreferencePanel(Component):
202 203 implements(IPreferencePanelProvider) 204 205 _form_fields = ('tz', 'lc_time', 'dateinfo', 'language') 206 207 # IPreferencePanelProvider methods 208
209 - def get_preference_panels(self, req):
210 yield 'localization', _("Localization")
211
212 - def render_preference_panel(self, req, panel):
213 if req.method == 'POST': 214 if Locale and \ 215 req.args.get('language') != req.session.get('language'): 216 # reactivate translations with new language setting 217 # when changed 218 del req.locale # for re-negotiating locale 219 deactivate() 220 make_activable(lambda: req.locale, self.env.path) 221 _do_save(req, panel, self._form_fields) 222 223 data = { 224 'timezones': all_timezones, 225 'timezone': get_timezone, 226 'localtz': localtz, 227 'has_babel': False, 228 } 229 if Locale: 230 locale_ids = get_available_locales() 231 locales = [Locale.parse(locale) for locale in locale_ids] 232 # use locale identifiers from get_available_locales() instead 233 # of str(locale) to prevent storing expanded locale identifier 234 # to session, e.g. zh_Hans_CN and zh_Hant_TW, since Babel 1.0. 235 # see #11258. 236 languages = sorted((id_, locale.display_name) 237 for id_, locale in zip(locale_ids, locales)) 238 data['locales'] = locales 239 data['languages'] = languages 240 data['has_babel'] = True 241 return 'prefs_localization.html', data
242
243 244 -class UserInterfacePreferencePanel(Component):
245 246 implements(IPreferencePanelProvider) 247 248 _request_handlers = ExtensionPoint(IRequestHandler) 249 250 _form_fields = ('accesskeys', 'default_handler','ui.auto_preview_timeout', 251 'ui.hide_help', 'ui.use_symbols', 'wiki_fullwidth') 252 253 # IPreferencePanelProvider methods 254
255 - def get_preference_panels(self, req):
256 yield 'userinterface', _("User Interface")
257
258 - def render_preference_panel(self, req, panel):
259 if req.method == 'POST': 260 _do_save(req, panel, self._form_fields) 261 262 auto_preview_timeout = self.config.get('trac', 'auto_preview_timeout') 263 data = { 264 'project_default_handler': self._project_default_handler, 265 'valid_default_handlers': self._valid_default_handlers, 266 'default_auto_preview_timeout': auto_preview_timeout, 267 } 268 return 'prefs_userinterface.html', data
269 270 # Internal methods 271 272 @property
273 - def _project_default_handler(self):
274 return self.config.get('trac', 'default_handler')
275 276 @lazy
277 - def _valid_default_handlers(self):
278 return sorted(handler.__class__.__name__ 279 for handler in self._request_handlers 280 if is_valid_default_handler(handler))
281
282 283 -def _do_save(req, panel, form_fields):
284 for field in form_fields: 285 val = req.args.get(field, '').strip() 286 if val: 287 if field == 'ui.auto_preview_timeout': 288 fval = as_float(val, default=None) 289 if fval is None or math.isinf(fval) or math.isnan(fval) \ 290 or fval < 0: 291 add_warning(req, _("Discarded invalid value \"%(val)s\" " 292 "for auto preview timeout.", val=val)) 293 continue 294 if field == 'tz' and 'tz' in req.session and \ 295 val not in all_timezones: 296 del req.session[field] 297 elif field == 'newsid': 298 req.session.change_sid(val) 299 else: 300 req.session[field] = val 301 elif (field in req.args or field + '_cb' in req.args) and \ 302 field in req.session: 303 del req.session[field] 304 add_notice(req, _("Your preferences have been saved.")) 305 req.redirect(req.href.prefs(panel))
306