| | 1 | # -*- coding: iso8859-1 -*- |
|---|
| | 2 | # |
|---|
| | 3 | # Copyright (C) 2005 Edgewall Software |
|---|
| | 4 | # Copyright (C) 2005 Brad Anderson <brad@dsource.org> |
|---|
| | 5 | # |
|---|
| | 6 | # Trac is free software; you can redistribute it and/or |
|---|
| | 7 | # modify it under the terms of the GNU General Public License as |
|---|
| | 8 | # published by the Free Software Foundation; either version 2 of the |
|---|
| | 9 | # License, or (at your option) any later version. |
|---|
| | 10 | # |
|---|
| | 11 | # Trac is distributed in the hope that it will be useful, |
|---|
| | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| | 14 | # General Public License for more details. |
|---|
| | 15 | # |
|---|
| | 16 | # You should have received a copy of the GNU General Public License |
|---|
| | 17 | # along with this program; if not, write to the Free Software |
|---|
| | 18 | # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|---|
| | 19 | # |
|---|
| | 20 | # Author: Brad Anderson <brad@dsource.org> |
|---|
| | 21 | |
|---|
| | 22 | import time |
|---|
| | 23 | |
|---|
| | 24 | import perm |
|---|
| | 25 | import urllib |
|---|
| | 26 | import string |
|---|
| | 27 | from util import * |
|---|
| | 28 | from Module import Module |
|---|
| | 29 | |
|---|
| | 30 | |
|---|
| | 31 | |
|---|
| | 32 | class Admin(Module): |
|---|
| | 33 | template_name = 'admin.cs' |
|---|
| | 34 | |
|---|
| | 35 | def render(self, req): |
|---|
| | 36 | |
|---|
| | 37 | req.hdf.setValue('title', 'Admin') |
|---|
| | 38 | |
|---|
| | 39 | if self.perm.has_permission(perm.TRAC_ADMIN): |
|---|
| | 40 | req.hdf.setValue('admin.general_href', |
|---|
| | 41 | self.env.href.admin('general', None)) |
|---|
| | 42 | |
|---|
| | 43 | if self.perm.has_permission(perm.TICKET_ADMIN): |
|---|
| | 44 | req.hdf.setValue('admin.ticket_href', |
|---|
| | 45 | self.env.href.admin('ticket', 'component')) |
|---|
| | 46 | |
|---|
| | 47 | if self.perm.has_permission(perm.TRAC_ADMIN): |
|---|
| | 48 | req.hdf.setValue('admin.user_href', |
|---|
| | 49 | self.env.href.admin('user', 'users')) |
|---|
| | 50 | |
|---|
| | 51 | type = req.args.get('type', 'ticket').lower() |
|---|
| | 52 | req.hdf.setValue('admin.type', type) |
|---|
| | 53 | req.hdf.setValue('admin.path', '/admin/%s/' % type) |
|---|
| | 54 | |
|---|
| | 55 | if type == 'general': |
|---|
| | 56 | self.admin = AdminGeneral(self.perm, self.env, self.db) |
|---|
| | 57 | elif type == 'ticket': |
|---|
| | 58 | self.admin = AdminTicket(self.perm, self.env, self.db) |
|---|
| | 59 | elif type == 'user': |
|---|
| | 60 | self.admin = AdminUser(self.perm, self.env, self.db) |
|---|
| | 61 | |
|---|
| | 62 | self.admin.render(req) |
|---|
| | 63 | |
|---|
| | 64 | class AdminGeneral(Admin): |
|---|
| | 65 | def __init__(self, perm, env, db): |
|---|
| | 66 | self.perm = perm |
|---|
| | 67 | self.env = env |
|---|
| | 68 | self.db = db |
|---|
| | 69 | |
|---|
| | 70 | def render (self, req): |
|---|
| | 71 | self.perm.assert_permission(perm.TRAC_ADMIN) |
|---|
| | 72 | |
|---|
| | 73 | |
|---|
| | 74 | class AdminTicket(Admin): |
|---|
| | 75 | def __init__(self, perm, env, db): |
|---|
| | 76 | self.perm = perm |
|---|
| | 77 | self.env = env |
|---|
| | 78 | self.db = db |
|---|
| | 79 | |
|---|
| | 80 | def save_attrib(self, req, target, id): |
|---|
| | 81 | self.perm.assert_permission(perm.TRAC_ADMIN) |
|---|
| | 82 | if req.args.has_key('save'): |
|---|
| | 83 | name = req.args.get('name', '') |
|---|
| | 84 | if not name: |
|---|
| | 85 | raise TracError('You must provide a name for the %s.' % target, |
|---|
| | 86 | 'Required Field Missing') |
|---|
| | 87 | |
|---|
| | 88 | if id == '-1': |
|---|
| | 89 | self.create_attrib(req, target, name) |
|---|
| | 90 | else: |
|---|
| | 91 | self.update_attrib(req, target, id, name) |
|---|
| | 92 | |
|---|
| | 93 | def formatVal(self, val, fieldtype): |
|---|
| | 94 | """formats the val parameter to be better suited (escaped) for SQL |
|---|
| | 95 | strings to be executed by cursor.execute()""" |
|---|
| | 96 | |
|---|
| | 97 | # TODO: handle single and double quotes in val right here |
|---|
| | 98 | |
|---|
| | 99 | # check some things based on metadata, add quotes to val |
|---|
| | 100 | if fieldtype == 'date': |
|---|
| | 101 | if val == '': |
|---|
| | 102 | val = '0' |
|---|
| | 103 | else: |
|---|
| | 104 | val = parse_date(val) |
|---|
| | 105 | elif fieldtype in ('string','wiki') and val == '': |
|---|
| | 106 | val = 'null' |
|---|
| | 107 | else: |
|---|
| | 108 | val = "'%s'" % val |
|---|
| | 109 | return val |
|---|
| | 110 | |
|---|
| | 111 | def create_attrib(self, req, target, name): |
|---|
| | 112 | |
|---|
| | 113 | self.perm.assert_permission(perm.TRAC_ADMIN) |
|---|
| | 114 | cursor = self.db.cursor() |
|---|
| | 115 | self.env.log.debug("Creating new %s '%s'" % (target, name)) |
|---|
| | 116 | |
|---|
| | 117 | meta = self.get_attrib_metadata(target) |
|---|
| | 118 | fields = meta[0] |
|---|
| | 119 | types = meta[1] |
|---|
| | 120 | table = meta[2] |
|---|
| | 121 | where = meta[3] |
|---|
| | 122 | values = [] |
|---|
| | 123 | |
|---|
| | 124 | # loop thru fields, getting values list |
|---|
| | 125 | flds = zip(fields, types) |
|---|
| | 126 | for f in flds: |
|---|
| | 127 | # add to values |
|---|
| | 128 | val = req.args.get(f[0], '') |
|---|
| | 129 | values.append( self.formatVal(val, f[1]) ) |
|---|
| | 130 | |
|---|
| | 131 | # special case for enum |
|---|
| | 132 | if table == "enum": |
|---|
| | 133 | fields.insert(0, 'type') |
|---|
| | 134 | values.insert(0, self.formatVal(target, 'string') ) |
|---|
| | 135 | |
|---|
| | 136 | fieldlist = string.join(fields, ',') |
|---|
| | 137 | valueslist = string.join(values, ',') |
|---|
| | 138 | |
|---|
| | 139 | # self.env.log.debug("create_attrib() fieldlist : %s" % (fieldlist)) |
|---|
| | 140 | # self.env.log.debug("create_attrib() valueslist: %s" % (valueslist)) |
|---|
| | 141 | |
|---|
| | 142 | sql = "INSERT INTO %s (%s) VALUES (%s)" % (table, fieldlist, valueslist) |
|---|
| | 143 | self.env.log.debug("create_attrib() sql : %s" % (sql)) |
|---|
| | 144 | |
|---|
| | 145 | cursor.execute(sql) |
|---|
| | 146 | self.db.commit() |
|---|
| | 147 | req.redirect(self.env.href.admin('ticket', target)) |
|---|
| | 148 | |
|---|
| | 149 | def update_attrib(self, req, target, id, name): |
|---|
| | 150 | |
|---|
| | 151 | self.perm.assert_permission(perm.TRAC_ADMIN) |
|---|
| | 152 | cursor = self.db.cursor() |
|---|
| | 153 | self.env.log.debug( "Updating %s '%s'" % (target,id) ) |
|---|
| | 154 | |
|---|
| | 155 | if req.args.has_key('save'): |
|---|
| | 156 | self.env.log.info("Updating %s field of all tickets " |
|---|
| | 157 | "associated with %s '%s'" % (target, target, id) ) |
|---|
| | 158 | cursor.execute("UPDATE ticket SET %s = '%s' " |
|---|
| | 159 | "WHERE %s = '%s'" % (target, name, target, id) ) |
|---|
| | 160 | self.db.commit() |
|---|
| | 161 | |
|---|
| | 162 | self.env.log.info("Updating %s '%s' to '%s'" % (target, id, name) ) |
|---|
| | 163 | meta = self.get_attrib_metadata(target) |
|---|
| | 164 | fields = meta[0] |
|---|
| | 165 | types = meta[1] |
|---|
| | 166 | table = meta[2] |
|---|
| | 167 | where = meta[3] |
|---|
| | 168 | set = "" |
|---|
| | 169 | |
|---|
| | 170 | flds = zip(fields,types) |
|---|
| | 171 | for f in flds: |
|---|
| | 172 | val = req.args.get(f[0], "") |
|---|
| | 173 | val = self.formatVal(val, f[1]) |
|---|
| | 174 | |
|---|
| | 175 | self.env.log.debug( "update_attrib: %s %s" % (f[0],val) ) |
|---|
| | 176 | set += ", %s=%s" % (f[0], val) |
|---|
| | 177 | set = set[2:] |
|---|
| | 178 | |
|---|
| | 179 | if len(where) > 0: |
|---|
| | 180 | where += " and " |
|---|
| | 181 | where += "name='%s'" % id |
|---|
| | 182 | |
|---|
| | 183 | sql = "update %s set %s where %s" % (table, set, where) |
|---|
| | 184 | # raise TracError("save_attrib sql: '%s'" % sql) |
|---|
| | 185 | cursor.execute(sql) |
|---|
| | 186 | self.db.commit() |
|---|
| | 187 | |
|---|
| | 188 | cursor.close() |
|---|
| | 189 | |
|---|
| | 190 | req.redirect(self.env.href.admin('ticket', target)) |
|---|
| | 191 | |
|---|
| | 192 | def delete_attrib(self, req, target, id): |
|---|
| | 193 | self.perm.assert_permission(perm.TRAC_ADMIN) |
|---|
| | 194 | |
|---|
| | 195 | if req.args.has_key('delete'): |
|---|
| | 196 | cursor = self.db.cursor() |
|---|
| | 197 | if req.args.has_key('chk_retarget'): |
|---|
| | 198 | retarget = req.args.get('retarget') |
|---|
| | 199 | if retarget: |
|---|
| | 200 | self.env.log.info('Retargeting %s field of all ' |
|---|
| | 201 | 'tickets associated with %s=%s to %s=%s' % |
|---|
| | 202 | (target, target, id, target, retarget)) |
|---|
| | 203 | cursor.execute( 'UPDATE ticket SET %s = %s ' |
|---|
| | 204 | 'WHERE %s = %s', (target, retarget, target, id) ) |
|---|
| | 205 | |
|---|
| | 206 | else: |
|---|
| | 207 | self.env.log.info('Resetting %s field of all ' |
|---|
| | 208 | 'tickets associated with %s=%s' % (target, target, id)) |
|---|
| | 209 | cursor.execute( 'UPDATE ticket SET %s = NULL ' |
|---|
| | 210 | 'WHERE %s = %s', (target, target, id) ) |
|---|
| | 211 | |
|---|
| | 212 | |
|---|
| | 213 | self.env.log.debug('Deleting %s %s' % (target, id)) |
|---|
| | 214 | meta = self.get_attrib_metadata(target) |
|---|
| | 215 | table = meta[2] |
|---|
| | 216 | where = meta[3] |
|---|
| | 217 | |
|---|
| | 218 | sql = "DELETE FROM %s " % table |
|---|
| | 219 | sql += "WHERE name = %s " % self.formatVal(id, 'string') |
|---|
| | 220 | if where: |
|---|
| | 221 | sql += "AND %s " % where |
|---|
| | 222 | self.env.log.debug('delete_attrib - sql(delete from target ' |
|---|
| | 223 | 'table): %s' % (sql)) |
|---|
| | 224 | cursor.execute(sql) |
|---|
| | 225 | self.db.commit() |
|---|
| | 226 | |
|---|
| | 227 | req.redirect(self.env.href.admin('ticket', target)) |
|---|
| | 228 | |
|---|
| | 229 | # all of this info could be in the db as a metadata table |
|---|
| | 230 | def get_attrib_metadata(self, target): |
|---|
| | 231 | |
|---|
| | 232 | if target == 'component': |
|---|
| | 233 | fields = ['name', 'owner'] |
|---|
| | 234 | types = ['string', 'string'] |
|---|
| | 235 | table = 'component' |
|---|
| | 236 | where = '' |
|---|
| | 237 | elif target == 'version': |
|---|
| | 238 | fields = ['name', 'time'] |
|---|
| | 239 | types = ['string', 'date'] |
|---|
| | 240 | table = 'version' |
|---|
| | 241 | where = '' |
|---|
| | 242 | elif target == 'severity' or target == 'priority' or \ |
|---|
| | 243 | target == 'status' or target == 'resolution': |
|---|
| | 244 | fields = ['name', 'value'] |
|---|
| | 245 | types = ['string', 'string'] |
|---|
| | 246 | table='enum' |
|---|
| | 247 | where='type = \'%s\'' % target |
|---|
| | 248 | elif target == 'milestone': |
|---|
| | 249 | fields = ['name', 'due', 'completed', 'description'] |
|---|
| | 250 | types = ['string', 'date', 'date', 'wiki'] |
|---|
| | 251 | table = 'milestone' |
|---|
| | 252 | where = '' |
|---|
| | 253 | |
|---|
| | 254 | meta = (fields, types, table, where) |
|---|
| | 255 | return meta |
|---|
| | 256 | |
|---|
| | 257 | def get_attrib_data(self, target, id): |
|---|
| | 258 | |
|---|
| | 259 | meta = self.get_attrib_metadata(target) |
|---|
| | 260 | fields = meta[0] |
|---|
| | 261 | types = meta[1] |
|---|
| | 262 | table = meta[2] |
|---|
| | 263 | where = meta[3] |
|---|
| | 264 | |
|---|
| | 265 | if id != -1: |
|---|
| | 266 | sql_select = string.join(fields, ',') |
|---|
| | 267 | sql = 'SELECT %s ' \ |
|---|
| | 268 | 'FROM %s ' \ |
|---|
| | 269 | 'WHERE name = \'%s\'' % (sql_select, table, id) |
|---|
| | 270 | if where: |
|---|
| | 271 | sql += ' and %s' % where |
|---|
| | 272 | |
|---|
| | 273 | cursor = self.db.cursor() |
|---|
| | 274 | cursor.execute(sql) |
|---|
| | 275 | row = cursor.fetchone() |
|---|
| | 276 | cursor.close() |
|---|
| | 277 | if not row: |
|---|
| | 278 | raise TracError('Attribute \'%s\' does not exist.' % id, |
|---|
| | 279 | 'Invalid Attribute') |
|---|
| | 280 | |
|---|
| | 281 | values = row |
|---|
| | 282 | else: |
|---|
| | 283 | # new attrib value, so fill values with blanks |
|---|
| | 284 | values = [] |
|---|
| | 285 | for field in fields: |
|---|
| | 286 | values.append('') |
|---|
| | 287 | |
|---|
| | 288 | return [values, fields, types] |
|---|
| | 289 | |
|---|
| | 290 | def get_target_attributes(self, target='component'): |
|---|
| | 291 | |
|---|
| | 292 | sql_select = '' |
|---|
| | 293 | |
|---|
| | 294 | meta = self.get_attrib_metadata(target) |
|---|
| | 295 | fields = meta[0] |
|---|
| | 296 | types = meta[1] |
|---|
| | 297 | table = meta[2] |
|---|
| | 298 | where = meta[3] |
|---|
| | 299 | |
|---|
| | 300 | sql_select = string.join(fields, ',') |
|---|
| | 301 | sql = 'SELECT %s ' \ |
|---|
| | 302 | 'FROM %s ' % (sql_select, table) |
|---|
| | 303 | if where: |
|---|
| | 304 | sql += 'WHERE %s' % where |
|---|
| | 305 | |
|---|
| | 306 | cursor = self.db.cursor() |
|---|
| | 307 | cursor.execute(sql) |
|---|
| | 308 | |
|---|
| | 309 | # FIXME: fetchall should probably not be used. |
|---|
| | 310 | info = cursor.fetchall() |
|---|
| | 311 | cols = cursor.description |
|---|
| | 312 | |
|---|
| | 313 | return [cols, info, types] |
|---|
| | 314 | |
|---|
| | 315 | |
|---|
| | 316 | def render (self, req): |
|---|
| | 317 | self.perm.assert_permission(perm.TICKET_ADMIN) |
|---|
| | 318 | |
|---|
| | 319 | target = req.args.get('target', 'component') |
|---|
| | 320 | # to highlight the current target on page |
|---|
| | 321 | add_to_hdf(target, req.hdf, 'admin.curr_target') |
|---|
| | 322 | # self.env.log.debug("admin target '%s'" % target) |
|---|
| | 323 | |
|---|
| | 324 | action = req.args.get('action', 'view') |
|---|
| | 325 | |
|---|
| | 326 | if req.args.has_key('id'): |
|---|
| | 327 | id = urllib.unquote( req.args.get('id', '') ) |
|---|
| | 328 | |
|---|
| | 329 | # self.env.log.debug("admin id '%s'" % id) |
|---|
| | 330 | |
|---|
| | 331 | |
|---|
| | 332 | if action == 'new': |
|---|
| | 333 | self.render_editor(req, target, -1) |
|---|
| | 334 | elif action == 'edit': |
|---|
| | 335 | self.render_editor(req, target, id) |
|---|
| | 336 | elif action == 'delete': |
|---|
| | 337 | self.render_confirm(req, target, id) |
|---|
| | 338 | elif action == 'commit_changes': |
|---|
| | 339 | self.save_attrib(req, target, id) |
|---|
| | 340 | elif action == 'confirm_delete': |
|---|
| | 341 | self.delete_attrib(req, target, id) |
|---|
| | 342 | else: |
|---|
| | 343 | self.render_view(req, target) |
|---|
| | 344 | |
|---|
| | 345 | # menu on left |
|---|
| | 346 | targets = ['component', 'version', 'severity', 'priority', 'status', |
|---|
| | 347 | 'resolution', 'milestone'] |
|---|
| | 348 | |
|---|
| | 349 | # add custom fields to the list of targets for admin |
|---|
| | 350 | # # Until they're moved into a db table and out of ini, don't bother with admin |
|---|
| | 351 | # for f in get_custom_fields(self.env): |
|---|
| | 352 | # if f['type'] == 'select' or f['type'] == 'radio': |
|---|
| | 353 | # target = { 'name': f['name'] } |
|---|
| | 354 | # targets.append(target) |
|---|
| | 355 | |
|---|
| | 356 | add_to_hdf(targets, req.hdf, 'admin.targets') |
|---|
| | 357 | |
|---|
| | 358 | |
|---|
| | 359 | def render_confirm(self, req, target, id): |
|---|
| | 360 | |
|---|
| | 361 | req.hdf.setValue('title', 'Attribute: %s' % id) |
|---|
| | 362 | req.hdf.setValue('admin.mode', 'delete') |
|---|
| | 363 | |
|---|
| | 364 | add_to_hdf(id, req.hdf, 'admin.attrib.name') |
|---|
| | 365 | |
|---|
| | 366 | # get choices for retargeting |
|---|
| | 367 | meta = self.get_attrib_metadata(target) |
|---|
| | 368 | table = meta[2] |
|---|
| | 369 | where = meta[3] |
|---|
| | 370 | |
|---|
| | 371 | cursor = self.db.cursor() |
|---|
| | 372 | if where: |
|---|
| | 373 | where = "AND %s" % where |
|---|
| | 374 | else: |
|---|
| | 375 | where = "" |
|---|
| | 376 | sql = "SELECT name FROM %s WHERE name != '' %s " \ |
|---|
| | 377 | "ORDER BY name" % (table, where) |
|---|
| | 378 | cursor.execute(sql) |
|---|
| | 379 | retargets = [] |
|---|
| | 380 | retarget_no = 0 |
|---|
| | 381 | while 1: |
|---|
| | 382 | row = cursor.fetchone() |
|---|
| | 383 | if not row: |
|---|
| | 384 | break |
|---|
| | 385 | req.hdf.setValue('retargets.%d' % retarget_no, row['name']) |
|---|
| | 386 | retarget_no += 1 |
|---|
| | 387 | cursor.close() |
|---|
| | 388 | |
|---|
| | 389 | def render_editor(self, req, target, id=None): |
|---|
| | 390 | |
|---|
| | 391 | data = [] |
|---|
| | 392 | [values, fields, types] = \ |
|---|
| | 393 | self.get_attrib_data(target, id) |
|---|
| | 394 | |
|---|
| | 395 | for v, f, t in zip(values, fields, types): |
|---|
| | 396 | if t == 'date': |
|---|
| | 397 | if v == 0: |
|---|
| | 398 | v = '' |
|---|
| | 399 | else: |
|---|
| | 400 | tm = time.localtime(int(v)) |
|---|
| | 401 | v = time.strftime('%x', tm) |
|---|
| | 402 | data.append( dict([('fieldname',f),('fieldtype',t),('value',v)]) ) |
|---|
| | 403 | |
|---|
| | 404 | if id == -1: |
|---|
| | 405 | req.hdf.setValue('title', 'New Attribute') |
|---|
| | 406 | req.hdf.setValue('admin.mode', 'new') |
|---|
| | 407 | else: |
|---|
| | 408 | req.hdf.setValue('title', 'Attribute: %s' % id) |
|---|
| | 409 | req.hdf.setValue('admin.mode', 'edit') |
|---|
| | 410 | |
|---|
| | 411 | add_to_hdf(data, req.hdf, 'admin.data') |
|---|
| | 412 | add_to_hdf(id, req.hdf, 'admin.attrib.name') |
|---|
| | 413 | add_to_hdf(get_date_format_hint(), req.hdf, 'admin.attrib.date_hint') |
|---|
| | 414 | |
|---|
| | 415 | def render_view(self, req, target): |
|---|
| | 416 | |
|---|
| | 417 | req.hdf.setValue('admin.mode', 'view') |
|---|
| | 418 | |
|---|
| | 419 | try: |
|---|
| | 420 | [self.cols, self.rows, self.types] = \ |
|---|
| | 421 | self.get_target_attributes(target) |
|---|
| | 422 | except Exception, e: |
|---|
| | 423 | self.error = e |
|---|
| | 424 | req.hdf.setValue('attrib.message', |
|---|
| | 425 | 'Attribute data error: %s' % e) |
|---|
| | 426 | return None |
|---|
| | 427 | |
|---|
| | 428 | # Convert the header info to HDF-format |
|---|
| | 429 | idx = 0 |
|---|
| | 430 | for col in self.cols: |
|---|
| | 431 | title=col[0].capitalize() |
|---|
| | 432 | prefix = 'attrib.headers.%d' % idx |
|---|
| | 433 | req.hdf.setValue('%s.real' % prefix, col[0]) |
|---|
| | 434 | req.hdf.setValue(prefix, title) |
|---|
| | 435 | |
|---|
| | 436 | idx = idx + 1 |
|---|
| | 437 | |
|---|
| | 438 | # Convert the rows and cells to HDF-format |
|---|
| | 439 | row_idx = 0 |
|---|
| | 440 | for row in self.rows: |
|---|
| | 441 | col_idx = 0 |
|---|
| | 442 | numrows = len(row) |
|---|
| | 443 | for cell in row: |
|---|
| | 444 | cell = str(cell) |
|---|
| | 445 | column = self.cols[col_idx][0] |
|---|
| | 446 | type = self.types[col_idx] |
|---|
| | 447 | value = {} |
|---|
| | 448 | value['type'] = type |
|---|
| | 449 | |
|---|
| | 450 | if type == 'date' and cell != 'None' and cell != '0': |
|---|
| | 451 | t = time.localtime(int(cell)) |
|---|
| | 452 | value['date'] = time.strftime('%x', t) |
|---|
| | 453 | value['time'] = time.strftime('%X', t) |
|---|
| | 454 | value['datetime'] = time.strftime('%c', t) |
|---|
| | 455 | value['gmt'] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', |
|---|
| | 456 | time.gmtime(int(cell))) |
|---|
| | 457 | |
|---|
| | 458 | prefix = 'attrib.items.%d.%s' % (row_idx, str(column)) |
|---|
| | 459 | req.hdf.setValue(prefix, escape(str(cell))) |
|---|
| | 460 | for key in value.keys(): |
|---|
| | 461 | req.hdf.setValue(prefix + '.' + key, str(value[key])) |
|---|
| | 462 | |
|---|
| | 463 | col_idx += 1 |
|---|
| | 464 | row_idx += 1 |
|---|
| | 465 | req.hdf.setValue('attrib.numrows', str(row_idx)) |
|---|
| | 466 | |
|---|
| | 467 | class AdminUser(Admin): |
|---|
| | 468 | def __init__(self, perm, env, db): |
|---|
| | 469 | self.perm = perm |
|---|
| | 470 | self.env = env |
|---|
| | 471 | self.db = db |
|---|
| | 472 | |
|---|
| | 473 | def render (self, req): |
|---|
| | 474 | self.perm.assert_permission(perm.TRAC_ADMIN) |