source: dynamicfieldsplugin/0.12/dynfields/web_ui.py

Last change on this file was 14003, checked in by Ryan J Ollos, 10 years ago

Changed license to 3-Clause BSD with permission of author. Refs #11832.

  • Property svn:executable set to *
File size: 5.2 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2010-2014 Rob Guttman <guttman@alum.mit.edu>
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution.
8#
9
10import re
11from pkg_resources import resource_filename
12
13from trac.core import *
14from trac.web.api import IRequestFilter, IRequestHandler
15from trac.web.chrome import ITemplateProvider, add_script, add_stylesheet
16from trac.config import ListOption
17from trac.prefs.api import IPreferencePanelProvider
18from trac.util.translation import domain_functions
19
20from dynfields.options import Options
21from dynfields.rules import add_domain, _, IRule
22
23
24class DynamicFieldsModule(Component):
25    """A module that dynamically alters ticket fields based an extensible
26    set of rules.  Uses jQuery for full implementation."""
27
28    implements(IRequestFilter, IRequestHandler, ITemplateProvider,
29               IPreferencePanelProvider)
30
31    rules = ExtensionPoint(IRule)
32
33    def __init__(self):
34    # bind the 'dynfields' catalog to the specified locale directory
35        locale_dir = resource_filename(__name__, 'locale')
36        add_domain(self.env.path, locale_dir)
37
38    # ITemplateProvider methods
39    def get_htdocs_dirs(self):
40        return [('dynfields', resource_filename(__name__, 'htdocs'))]
41
42    def get_templates_dirs(self):
43        return [resource_filename(__name__, 'templates')]
44
45    # IRequestFilter methods
46    def pre_process_request(self, req, handler):
47        return handler
48
49    def post_process_request(self, req, template, data, content_type):
50        if ((req.path_info.startswith('/ticket') or
51            req.path_info.startswith('/newticket')) and \
52                req.perm.has_permission('TICKET_MODIFY') \
53            ) or ( \
54            req.path_info.startswith('/query') and \
55                req.perm.has_permission('REPORT_VIEW')):
56            # don't remove leading '/' character
57            add_script(req, '/dynfields/dynfields.js')
58            add_script(req, 'dynfields/layout.js')
59            add_script(req, 'dynfields/rules.js')
60        return template, data, content_type
61
62    # IRequestHandler methods
63    def match_request(self, req):
64        return req.path_info.startswith('/dynfields')
65
66    def process_request(self, req):
67        data = {'triggers':self._get_triggers(req)}
68        return 'dynfields.html', {'data': data},'text/javascript'
69
70    def _get_triggers(self, req):
71        """Converts trac.ini config to dict of triggers with rule specs."""
72        triggers = {}
73        opts = Options(self.env)
74        for key in opts:
75            # extract the target field
76            target_re = re.compile(r"(?P<target>[^.]+).*")
77            target = target_re.match(key).groupdict()['target']
78
79            # extract rule specifications from configs
80            for rule in self.rules:
81                trigger = rule.get_trigger(target, key, opts)
82                if not trigger:
83                    continue
84                if not opts.is_enabled(req, key):
85                    continue
86                value,_ = opts.get_value_and_options(req, target, key)
87                spec = {'rule_name':rule.name, 'trigger':trigger,
88                        'target':target, 'value':value}
89                rule.update_spec(req, key, opts, spec)
90                if trigger not in triggers:
91                    triggers[trigger] = [] # rule specs
92                triggers[trigger].append(spec)
93
94        return triggers
95
96    # IPreferencePanelProvider methods
97    def get_preference_panels(self, req):
98        if self._get_prefs_data(req): # only show if there are preferences
99            # TRANSLATOR: the preferences tab label
100            yield 'dynfields', _("Dynamic Fields")
101
102    def render_preference_panel(self, req, panel):
103        opts = Options(self.env)
104        if req.method == 'POST':
105            opts.set_prefs(req)
106        data = self._get_prefs_data(req, opts)
107        return 'prefs_panel.html', {'data':data, 'saved':req.method == 'POST'}
108
109    def _get_prefs_data(self, req, opts=None):
110        """Returns the pref data, a dict of rule class titles whose values
111        include lists of rule spec preference dicts each with these keys:
112
113          id (based on unique key)
114          label (of checkbox)
115          enabled ('1' or '0')
116          type ('none' or 'select'; TODO: 'text')
117          options (list of options if type is 'select')
118          value (saved preference or default value)
119        """
120        if opts is None:
121            opts = Options(self.env)
122        data = {}
123        for rule in self.rules:
124            for key in opts:
125                if not opts.has_pref(key):
126                    continue
127                target_re = re.compile(r"(?P<target>[^.]+).*")
128                target = target_re.match(key).groupdict()['target']
129                trigger = rule.get_trigger(target, key, opts)
130                if not trigger:
131                    continue
132
133                # this rule spec has a pref - so get it!
134                pref = opts.get_pref(req, target, key)
135                rule.update_pref(req, trigger, target, key, opts, pref)
136                if rule.title not in data:
137                    data[rule.title] = {'desc':rule.desc, 'prefs':[]}
138                data[rule.title]['prefs'].append(pref)
139        return data
Note: See TracBrowser for help on using the repository browser.