source: themeengineplugin/tags/themeengine-2.3.0/themeengine/admin.py

Last change on this file was 18328, checked in by Cinc-th, 2 years ago

ThemeEnginePlugin: compatibility fixes for Trac 1.4 and Trac 1.6.

  • created Jinja2 templates.
  • Fix for exception when trying to render error page using Trac 1.4 and Trac 1.6
  • New version of jquery.rule.js fixes problem where colors weren't applied to the input fields after color scheme selection in custom panel.
  • Python 3 fixes.
  • Fixed test cases where custom theme components weren't loaded into the environment.

Closes #14020

File size: 8.1 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (c) 2006-2010 Noah Kantrowitz <noah@coderanger.net>
4# Copyright (c) 2013      Olemis Lang <olemis+trac@gmail.com>
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.
9#
10
11import os.path
12
13from pkg_resources import resource_filename, resource_string
14
15from trac.admin.api import IAdminPanelProvider
16from trac.core import *
17from trac.web.chrome import add_script, add_stylesheet, Chrome, ITemplateProvider
18from trac.web.api import IRequestHandler, HTTPNotFound
19from trac.perm import IPermissionRequestor
20
21from themeengine.api import ThemeEngineSystem, ThemeNotFound
22from themeengine.translation import _, dgettext
23
24
25try:
26    dict.iteritems
27except AttributeError:
28    # Python 3
29    def iteritems(d):
30        return iter(d.items())
31else:
32    # Python 2
33    def iteritems(d):
34        return d.iteritems()
35
36
37class SimpleThemeAdminModule(Component):
38    """An admin panel for ThemeEngine."""
39
40    implements(IAdminPanelProvider, IPermissionRequestor, ITemplateProvider, IRequestHandler)
41
42    def __init__(self):
43        self.system = ThemeEngineSystem(self.env)
44
45    # IAdminPanelProvider methods
46    def get_admin_panels(self, req):
47        if req.perm.has_permission('THEME_ADMIN'):
48            yield 'theme', _('Theme'), 'theme', _('Theme')
49
50    def render_admin_panel(self, req, cat, page, path_info):
51        if req.method == 'POST':
52            self.config.set('theme', 'theme', req.args['theme'].lower())
53            self.config.save()
54
55            req.redirect(req.href.admin(cat, page))
56
57        data = {
58            'themeengine': {
59                'info': self.system.info.items(),
60            },
61            '_dgettext' : dgettext,
62        }
63
64        try:
65            theme_name = self.system.theme and self.system.theme['name'] \
66                                           or 'default'
67        except ThemeNotFound:
68            theme_name = 'default'
69        theme_name = theme_name.islower() and theme_name.title() or theme_name
70        data['themeengine']['current'] = theme_name
71        index = 0
72        curtheme = None
73        for i, (k, v) in enumerate(data['themeengine']['info']):
74            if k.lower() == theme_name.lower():
75                index = i
76                curtheme = v
77                break
78        data['themeengine']['current_index'] = index
79        data['themeengine']['current_theme'] = curtheme
80
81        #add_stylesheet(req, 'themeengine/jquery.jcarousel.css')
82        #add_stylesheet(req, 'themeengine/skins/tango/skin.css')
83        #add_script(req, 'themeengine/jquery.jcarousel.pack.js')
84        add_script(req, 'themeengine/jquery.cycle2.min.js')
85        add_stylesheet(req, 'themeengine/admin.css')
86        add_stylesheet(req, 'themeengine/cycle2.css')
87        #add_script(req, 'themeengine/jcarousellite_1.0.1.js')
88        if hasattr(Chrome, 'jenv'):
89            return 'admin_theme_jinja.html', data
90        else:
91            return 'admin_theme.html', data
92
93    # IPermissionRequestor methods
94    def get_permission_actions(self):
95        yield 'THEME_ADMIN'
96
97    # ITemplateProvider methods
98    def get_htdocs_dirs(self):
99        yield 'themeengine', resource_filename(__name__, 'htdocs')
100
101    def get_templates_dirs(self):
102        yield resource_filename(__name__, 'templates')
103
104    # IRequestHandler methods
105    def match_request(self, req):
106        return req.path_info.startswith('/themeengine')
107
108    def process_request(self, req):
109        data = {}
110
111        path_info = req.path_info[13:]
112        if path_info.startswith('screenshot/'):
113            return self._send_screenshot(req, data, path_info[11:])
114
115        raise HTTPNotFound("The requested URL was not found on this server")
116
117    def _send_screenshot(self, req, data, name):
118        if name not in self.system.info:
119            raise HTTPNotFound("Invalid theme name '%s'", name)
120        theme = self.system.info[name]
121
122        if 'screenshot' in theme:
123            img = resource_string(theme['module'], theme['screenshot'])
124        else:
125            img = resource_string(__name__, 'htdocs/no_screenshot.png')
126        req.send(img, 'image/png')
127
128
129class CustomThemeAdminModule(Component):
130    """An admin panel for ThemeEngine."""
131
132    implements(IAdminPanelProvider)
133
134    def __init__(self):
135        self.system = ThemeEngineSystem(self.env)
136
137    # IAdminPanelProvider methods
138    def get_admin_panels(self, req):
139        if req.perm.has_permission('THEME_ADMIN'):
140            yield 'theme', _('Theme'), 'custom', _('Customize')
141            yield 'theme', _('Theme'), 'advanced', _('Customize: Advanced')
142
143    def render_admin_panel(self, req, cat, page, path_info):
144        data = {
145            'themes': self.system.info.items(),
146            '_dgettext' : dgettext,
147            'iteritems': iteritems
148        }
149
150        theme_name = self.system.theme_name or 'Default'
151        data['current'] = theme_name
152        index = 0
153        curtheme = {}
154        for i, (k, v) in enumerate(data['themes']):
155            if k == theme_name:
156                index = i
157                curtheme = v
158                break
159        data['current_index'] = index
160        data['current_theme'] = curtheme
161
162        colors = {}
163        if curtheme:
164            for name, prop, selector in curtheme.get('colors', ()):
165                # Check the config first
166                val = self.config.get('theme', 'color.'+name)
167                if not val and curtheme.get('schemes'):
168                    # Then look for a scheme
169                    val = curtheme['schemes'][0][1].get(name)
170                if not val:
171                    # Otherwise use black for foreground and white for background
172                    if prop == 'color':
173                        val = '#000000'
174                    else:
175                        val = '#ffffff'
176                colors[name] = val
177        data['colors'] = colors
178        data['enable'] = self.config.getbool('theme', 'enable_css', False)
179
180        if hasattr(self.env, 'get_htdocs_dir'):
181            theme_css_file = os.path.join(self.env.get_htdocs_dir(), 'theme.css')
182        else:
183            theme_css_file = os.path.join(self.env.htdocs_dir, 'theme.css')
184        if page == 'advanced' and os.path.exists(theme_css_file):
185            data['css'] = open(theme_css_file).read()
186
187        if req.method == 'POST':
188            if 'enable_css' in req.args:
189                self.config.set('theme', 'enable_css', 'enabled')
190            else:
191                self.config.set('theme', 'enable_css', 'disabled')
192
193            for key, value in self.config.options('theme'):
194                if key.startswith('color.'):
195                    self.config.remove('theme', key)
196
197            for name, color in iteritems(colors):
198                color = req.args.get('color_'+name, color)
199                self.config.set('theme', 'color.'+name, color)
200
201            f = open(theme_css_file, 'w')
202            if page == 'advanced':
203                f.write(req.args.get('css', ''))
204            else:
205                f.write('/* Warning: this file is auto-generated. If you edit it, changes will be lost the next time you use the simple customizer. */\n')
206                for name, prop, selector in curtheme.get('colors', ()):
207                    color = req.args.get('color_'+name, colors.get(name, '#ffffff'))
208                    f.write('%s {\n'%selector)
209                    f.write(%s: %s;\n'%(prop, color))
210                    f.write('}\n\n')
211            f.close()
212
213            self.config.save()
214            req.redirect(req.href.admin(cat, page))
215
216        add_stylesheet(req, 'themeengine/farbtastic/farbtastic.css')
217        add_stylesheet(req, 'themeengine/admin.css')
218        add_script(req, 'themeengine/farbtastic/farbtastic.js')
219        if page == 'advanced':
220            if hasattr(Chrome, 'jenv'):
221                return 'admin_theme_advanced_jinja.html', data
222            else:
223                return 'admin_theme_advanced.html', data
224        else:
225            add_script(req, 'themeengine/jquery.rule.js')
226            if hasattr(Chrome, 'jenv'):
227                return 'admin_theme_custom_jinja.html', data
228            else:
229                return 'admin_theme_custom.html', data
Note: See TracBrowser for help on using the repository browser.