annotate bitten/admin.py @ 431:11658e4af0cc

Require WebAdmin plugin to ensure the plugin is loaded before Bitten.
author cmlenz
date Tue, 14 Aug 2007 22:45:09 +0000
parents d6e1a05f32f7
children 74c51f648466
rev   line source
429
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
2 #
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
3 # Copyright (C) 2007 Edgewall Software
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
4 # All rights reserved.
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
5 #
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
6 # This software is licensed as described in the file COPYING, which
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
7 # you should have received as part of this distribution. The terms
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
8 # are also available at http://bitten.edgewall.org/wiki/License.
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
9
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
10 """Implementation of the web administration interface."""
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
11
431
11658e4af0cc Require WebAdmin plugin to ensure the plugin is loaded before Bitten.
cmlenz
parents: 429
diff changeset
12 from pkg_resources import require
429
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
13 import re
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
14
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
15 from trac.core import *
431
11658e4af0cc Require WebAdmin plugin to ensure the plugin is loaded before Bitten.
cmlenz
parents: 429
diff changeset
16 try:
11658e4af0cc Require WebAdmin plugin to ensure the plugin is loaded before Bitten.
cmlenz
parents: 429
diff changeset
17 require("TracWebAdmin")
11658e4af0cc Require WebAdmin plugin to ensure the plugin is loaded before Bitten.
cmlenz
parents: 429
diff changeset
18 from webadmin.web_ui import IAdminPageProvider
11658e4af0cc Require WebAdmin plugin to ensure the plugin is loaded before Bitten.
cmlenz
parents: 429
diff changeset
19 except ImportError:
11658e4af0cc Require WebAdmin plugin to ensure the plugin is loaded before Bitten.
cmlenz
parents: 429
diff changeset
20 IAdminPageProvider = None
429
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
21
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
22 from bitten.model import BuildConfig, TargetPlatform
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
23 from bitten.recipe import Recipe
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
24 from bitten.util import xmlio
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
25
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
26
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
27 class BuildMasterAdminPageProvider(Component):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
28 """Web administration panel for configuring the build master."""
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
29
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
30 implements(IAdminPageProvider)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
31
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
32 # IAdminPageProvider methods
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
33
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
34 def get_admin_pages(self, req):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
35 if req.perm.has_permission('BUILD_ADMIN'):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
36 yield ('bitten', 'Bitten', 'master', 'Build Master')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
37
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
38 def process_admin_request(self, req, cat, page, path_info):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
39 from bitten.master import BuildMaster
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
40 master = BuildMaster(self.env)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
41
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
42 if req.method == 'POST':
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
43 changed = False
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
44 build_all = 'build_all' in req.args
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
45 if build_all != master.build_all:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
46 self.config['bitten'].set('build_all',
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
47 build_all and 'yes' or 'no')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
48 changed = True
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
49 adjust_timestamps = 'adjust_timestamps' in req.args
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
50 if adjust_timestamps != master.adjust_timestamps:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
51 self.config['bitten'].set('adjust_timestamps',
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
52 adjust_timestamps and 'yes' or 'no')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
53 changed = True
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
54 slave_timeout = int(req.args.get('slave_timeout', 0))
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
55 if slave_timeout != master.slave_timeout:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
56 self.config['bitten'].set('slave_timeout', str(slave_timeout))
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
57 changed = True
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
58 if changed:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
59 self.config.save()
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
60
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
61 req.hdf['admin.master'] = {
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
62 'build_all': master.build_all,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
63 'adjust_timestamps': master.adjust_timestamps,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
64 'slave_timeout': master.slave_timeout,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
65 }
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
66 return 'bitten_admin_master.cs', None
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
67
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
68
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
69 class BuildConfigurationsAdminPageProvider(Component):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
70 """Web administration panel for configuring the build master."""
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
71
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
72 implements(IAdminPageProvider)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
73
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
74 # IAdminPageProvider methods
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
75
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
76 def get_admin_pages(self, req):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
77 if req.perm.has_permission('BUILD_MODIFY'):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
78 yield ('bitten', 'Bitten', 'configs', 'Configurations')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
79
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
80 def process_admin_request(self, req, cat, page, config_name):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
81 data = {}
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
82
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
83 if config_name:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
84 config = BuildConfig.fetch(self.env, config_name)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
85 platforms = list(TargetPlatform.select(self.env, config=config.name))
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
86
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
87 if req.method == 'POST':
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
88 if 'save' in req.args:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
89 name = req.args.get('name')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
90 if not name:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
91 raise TracError('Missing required field "name"',
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
92 'Missing field')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
93 if not re.match(r'^[\w.-]+$', name):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
94 raise TracError('The field "name" may only contain '
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
95 'letters, digits, periods, or dashes.',
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
96 'Invalid field')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
97
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
98 path = req.args.get('path', '')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
99 repos = self.env.get_repository(req.authname)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
100 max_rev = req.args.get('max_rev') or None
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
101 try:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
102 node = repos.get_node(path, max_rev)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
103 assert node.isdir, '%s is not a directory' % node.path
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
104 except (AssertionError, TracError), e:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
105 raise TracError(e, 'Invalid repository path')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
106 if req.args.get('min_rev'):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
107 try:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
108 repos.get_node(path, req.args.get('min_rev'))
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
109 except TracError, e:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
110 raise TracError(e,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
111 'Invalid value for oldest revision')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
112
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
113 recipe_xml = req.args.get('recipe', '')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
114 if recipe_xml:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
115 try:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
116 Recipe(xmlio.parse(recipe_xml)).validate()
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
117 except xmlio.ParseError, e:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
118 raise TracError('Failure parsing recipe: %s' % e,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
119 'Invalid recipe')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
120 except InvalidRecipeError, e:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
121 raise TracError(e, 'Invalid recipe')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
122
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
123 config.name = name
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
124 config.path = repos.normalize_path(path)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
125 config.recipe = recipe_xml
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
126 config.min_rev = req.args.get('min_rev')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
127 config.max_rev = req.args.get('max_rev')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
128 config.label = req.args.get('label', '')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
129 config.description = req.args.get('description', '')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
130 config.update()
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
131 req.redirect(self.env.href.admin(cat, page))
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
132
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
133 elif 'cancel' in req.args:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
134 req.redirect(self.env.href.admin(cat, page))
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
135
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
136 data['config'] = {
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
137 'name': config.name, 'label': config.label or config.name,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
138 'active': config.active, 'path': config.path,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
139 'min_rev': config.min_rev, 'max_rev': config.max_rev,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
140 'description': config.description,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
141 'recipe': config.recipe,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
142 'platforms': [{
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
143 'name': platform.name,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
144 'id': platform.id,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
145 'href': req.href.admin('bitten', 'configs', config.name,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
146 platform.id)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
147 } for platform in platforms]
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
148 }
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
149
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
150 else:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
151 if req.method == 'POST':
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
152 # Add configuration
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
153 if 'add' in req.args:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
154 config = BuildConfig(self.env)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
155 config.name = req.args.get('name')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
156 config.label = req.args.get('label', config.name)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
157 config.path = req.args.get('path')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
158 config.insert()
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
159 req.redirect(self.env.href.admin(cat, page, config.name))
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
160
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
161 # Remove configurations
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
162 elif 'remove' in req.args:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
163 sel = req.args.get('sel')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
164 sel = isinstance(sel, list) and sel or [sel]
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
165 if not sel:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
166 raise TracError('No configuration selected')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
167 db = self.env.get_db_cnx()
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
168 for name in sel:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
169 config = BuildConfig.fetch(self.env, name, db=db)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
170 config.delete(db=db)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
171 db.commit()
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
172 req.redirect(self.env.href.admin(cat, page))
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
173
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
174 # Set active state
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
175 elif 'apply' in req.args:
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
176 active = req.args.get('active')
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
177 active = isinstance(active, list) and active or [active]
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
178 db = self.env.get_db_cnx()
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
179 for config in BuildConfig.select(self.env, db=db,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
180 include_inactive=True):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
181 config.active = config.name in active
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
182 config.update(db=db)
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
183 db.commit()
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
184 req.redirect(self.env.href.admin(cat, page))
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
185
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
186 configs = []
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
187 for config in BuildConfig.select(self.env, include_inactive=True):
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
188 configs.append({
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
189 'name': config.name, 'label': config.label or config.name,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
190 'active': config.active, 'path': config.path,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
191 'min_rev': config.min_rev, 'max_rev': config.max_rev,
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
192 'href': req.href.admin('bitten', 'configs', config.name),
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
193 })
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
194 data['configs'] = configs
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
195
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
196 req.hdf['admin'] = data
d6e1a05f32f7 Start webadmin integration.
cmlenz
parents:
diff changeset
197 return 'bitten_admin_configs.cs', None
Copyright (C) 2012-2017 Edgewall Software