PKe…68úÀýPPEGG-INFO/SOURCES.txtsetup.cfg setup.py TracWebAdmin.egg-info/PKG-INFO TracWebAdmin.egg-info/SOURCES.txt TracWebAdmin.egg-info/dependency_links.txt TracWebAdmin.egg-info/entry_points.txt TracWebAdmin.egg-info/top_level.txt webadmin/__init__.py webadmin/basics.py webadmin/logging.py webadmin/perm.py webadmin/plugin.py webadmin/ticket.py webadmin/web_ui.py PKd…68º 8¦ÚÚEGG-INFO/entry_points.txt[trac.plugins] webadmin.logging = webadmin.logging webadmin.ticket = webadmin.ticket webadmin.basics = webadmin.basics webadmin.perm = webadmin.perm webadmin.web_ui = webadmin.web_ui webadmin.plugin = webadmin.plugin PKd…68“×2EGG-INFO/dependency_links.txt PKd…68‘OúðEGG-INFO/PKG-INFOMetadata-Version: 1.0 Name: TracWebAdmin Version: 0.1.2dev Summary: Web interface for administration of Trac Home-page: http://projects.edgewall.com/trac/wiki/WebAdmin Author: Edgewall Software Author-email: info@edgewall.com License: BSD Description: UNKNOWN Platform: UNKNOWN PKd…68•4Ì EGG-INFO/top_level.txtwebadmin PKp…68“×2EGG-INFO/not-zip-safe PKn…68¥¥_!webadmin/basics.pyc;ò ÂÃìEc@s.dkTdklZdefd„ƒYZdS((s*(sIAdminPageProvidersProjectAdminPagecBs$tZeeƒd„Zd„ZRS(Nccs+|iidƒoddddfVndS(Ns TRAC_ADMINsgeneralsGeneralsbasicsBasic Settings(sreqspermshas_permission(sselfsreq((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/basics.pysget_admin_pagesscCs |idjo–|iidd|iidƒƒ|iidd|iidƒƒ|iidd|iidƒƒ|iiƒ|i|i i i ||ƒƒnhd|iiddƒ<d|iiddƒ<d|iiddƒ<|id # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://projects.edgewall.com/trac/. # # Author: Christopher Lenz import os from trac.core import * from webadmin.web_ui import IAdminPageProvider class LoggingAdminPage(Component): implements(IAdminPageProvider) # IAdminPageProvider methods def get_admin_pages(self, req): if req.perm.has_permission('TRAC_ADMIN'): yield ('general', 'General', 'logging', 'Logging') def process_admin_request(self, req, cat, page, path_info): log_type = self.config.get('logging', 'log_type') log_level = self.config.get('logging', 'log_level').upper() log_file = self.config.get('logging', 'log_file', 'trac.log') log_dir = os.path.join(self.env.path, 'log') log_types = [ dict(name='', label=''), dict(name='stderr', label='Console', selected=log_type == 'stderr'), dict(name='file', label='File', selected=log_type == 'file'), dict(name='syslog', label='Syslog', disabled=os.name != 'posix', selected=log_type in ('unix', 'syslog')), dict(name='eventlog', label='Windows event log', disabled=os.name != 'nt', selected=log_type in ('winlog', 'eventlog', 'nteventlog')), ] log_levels = ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'] if req.method == 'POST': changed = False new_type = req.args.get('log_type') if new_type and new_type not in ('stderr', 'file', 'syslog', 'eventlog'): raise TracError('Unknown log type %s' % new_type, 'Invalid log type') if new_type != log_type: self.config.set('logging', 'log_type', new_type or 'none') changed = True log_type = new_type if log_type: new_level = req.args.get('log_level') if new_level and new_level not in log_levels: raise TracError('Unknown log level %s' % new_level, 'Invalid log level') if new_level and new_level != log_level: self.config.set('logging', 'log_level', new_level) changed = True log_evel = new_level else: self.config.remove('logging', 'log_level') changed = True if log_type == 'file': new_file = req.args.get('log_file', 'trac.log') if new_file != log_file: self.config.set('logging', 'log_file', new_file or '') changed = True log_file = new_file if log_type == 'file' and not log_file: raise TracError('You must specify a log file', 'Missing field') else: self.config.remove('logging', 'log_file') changed = True if changed: self.config.save() req.redirect(self.env.href.admin(cat, page)) req.hdf['admin.log'] = {'type': log_type, 'types': log_types, 'level': log_level, 'levels': log_levels, 'file': log_file, 'dir': log_dir} return 'admin_log.cs', None PK‘£e6&Œ=Ø$$webadmin/plugin.py# -*- coding: utf-8 -*- # # Copyright (C) 2005-2006 Edgewall Software # Copyright (C) 2005-2006 Christopher Lenz # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://projects.edgewall.com/trac/. # # Author: Christopher Lenz import email import inspect import os import shutil import sys from trac import ticket, __version__ as TRAC_VERSION from trac.core import * from webadmin.web_ui import IAdminPageProvider try: import pkg_resources except ImportError: pkg_resources = None def _find_base_path(path, module_name): base_path = os.path.splitext(path)[0] while base_path.replace(os.sep, '.').endswith(module_name): base_path = os.path.dirname(base_path) module_name = '.'.join(module_name.split('.')[:-1]) if not module_name: break return base_path TRAC_PATH = _find_base_path(sys.modules['trac.core'].__file__, 'trac.core') # Ideally, this wouldn't be hard-coded like this required_components = ('AboutModule', 'DefaultPermissionGroupProvider', 'Environment', 'EnvironmentSetup', 'PermissionSystem', 'RequestDispatcher', 'Mimeview', 'Chrome') class PluginAdminPage(Component): implements(IAdminPageProvider) # IAdminPageProvider methods def get_admin_pages(self, req): if req.perm.has_permission('TRAC_ADMIN'): yield ('general', 'General', 'plugin', 'Plugins') def process_admin_request(self, req, cat, page, _): req.perm.assert_permission('TRAC_ADMIN') if req.method == 'POST': if req.args.has_key('update'): self._do_update(req) elif req.args.has_key('install'): self._do_install(req) elif req.args.has_key('uninstall'): self._do_uninstall(req) else: self.log.warning('Unknown POST request: %s', req.args) anchor = '' if req.args.has_key('plugin'): anchor = '#no' + req.args.get('plugin') req.redirect(self.env.href.admin(cat, page) + anchor) self._render_view(req) return 'admin_plugin.cs', None # Internal methods def _do_install(self, req): """Install a plugin.""" if not req.args.has_key('plugin_file'): raise TracError, 'No file uploaded' upload = req.args['plugin_file'] if not upload.filename: raise TracError, 'No file uploaded' plugin_filename = upload.filename.replace('\\', '/').replace(':', '/') plugin_filename = os.path.basename(plugin_filename) if not plugin_filename: raise TracError, 'No file uploaded' if not plugin_filename.endswith('.egg') and \ not plugin_filename.endswith('.py'): raise TracError, 'Uploaded file is not a Python source file or egg' target_path = os.path.join(self.env.path, 'plugins', plugin_filename) if os.path.isfile(target_path): raise TracError, 'Plugin %s already installed' % plugin_filename self.log.info('Installing plugin %s', plugin_filename) flags = os.O_CREAT + os.O_WRONLY + os.O_EXCL try: flags += os.O_BINARY except AttributeError: # OS_BINARY not available on every platform pass target_file = os.fdopen(os.open(target_path, flags), 'w') try: shutil.copyfileobj(upload.file, target_file) self.log.info('Plugin %s installed to %s', plugin_filename, target_path) finally: target_file.close() # TODO: Validate that the uploaded file is actually a valid Trac plugin def _do_uninstall(self, req): """Uninstall a plugin.""" plugin_filename = req.args.get('plugin_filename') if not plugin_filename: return plugin_path = os.path.join(self.env.path, 'plugins', plugin_filename) if not os.path.isfile(plugin_path): return self.log.info('Uninstalling plugin %s', plugin_filename) os.remove(plugin_path) def _do_update(self, req): """Update component enablement.""" components = req.args.getlist('component') enabled = req.args.getlist('enable') changes = False # FIXME: this needs to be more intelligent and minimize multiple # component names to prefix rules for component in components: is_enabled = self.env.is_component_enabled(component) if is_enabled != (component in enabled): self.config.set('components', component, is_enabled and 'disabled' or 'enabled') self.log.info('%sabling component %s', is_enabled and 'Dis' or 'En', component) changes = True if changes: self.config.save() def _render_view(self, req): plugins = {} plugins_dir = os.path.realpath(os.path.join(self.env.path, 'plugins')) from trac.core import ComponentMeta for component in ComponentMeta._components: module = sys.modules[component.__module__] dist = self._find_distribution(module) plugin_filename = None if os.path.realpath(os.path.dirname(dist.location)) == plugins_dir: plugin_filename = os.path.basename(dist.location) description = inspect.getdoc(component) if description: description = description.split('.', 1)[0] + '.' if dist.project_name not in plugins: readonly = True if plugin_filename and os.access(dist.location, os.F_OK + os.W_OK): readonly = False plugins[dist.project_name] = { 'name': dist.project_name, 'version': dist.version, 'path': dist.location, 'description': description, 'plugin_filename': plugin_filename, 'readonly': readonly, 'info': self._get_pkginfo(dist), 'components': [] } plugins[dist.project_name]['components'].append({ 'name': component.__name__, 'module': module.__name__, 'description': description, 'enabled': self.env.is_component_enabled(component), 'required': component.__name__ in required_components, }) def component_order(a, b): c = cmp(len(a['module'].split('.')), len(b['module'].split('.'))) if c == 0: c = cmp(a['module'].lower(), b['module'].lower()) if c == 0: c = cmp(a['name'].lower(), b['name'].lower()) return c for category in plugins: plugins[category]['components'].sort(component_order) req.hdf['title'] = 'Manage Plugins' req.hdf['admin.plugins.0'] = plugins['Trac'] addons = [key for key in plugins.keys() if key != 'Trac'] addons.sort() for idx, category in enumerate(addons): req.hdf['admin.plugins.%s' % (idx + 1)] = plugins[category] if not os.access(plugins_dir, os.F_OK + os.W_OK): req.hdf['admin.readonly'] = True def _find_distribution(self, module): # Determine the plugin that this component belongs to path = module.__file__ if path.endswith('.pyc') or path.endswith('.pyo'): path = path[:-1] if os.path.basename(path) == '__init__.py': path = os.path.dirname(path) path = _find_base_path(path, module.__name__) if path == TRAC_PATH: return pkg_resources.Distribution(project_name='Trac', version=TRAC_VERSION, location=path) for dist in pkg_resources.find_distributions(path, only=True): return dist else: # This is a plain Python source file, not an egg return pkg_resources.Distribution(project_name=module.__name__, version='', location=module.__file__) def _get_pkginfo(self, dist): attrs = ('author', 'author-email', 'license', 'home-page', 'summary', 'description') info = {} try: pkginfo = email.message_from_string(dist.get_metadata('PKG-INFO')) for attr in [key for key in attrs if key in pkginfo]: info[attr.lower().replace('-', '_')] = pkginfo[attr] except email.Errors.MessageError, e: self.log.warning('Failed to parse PKG-INFO file for %s: %s', dist, e, exc_info=True) return info PKn…68Äš¢É < <webadmin/ticket.pyc;ò ÂÃìEc@sèdkZdklZdklZdkTdklZlZdkl Z de fd„ƒYZ de fd „ƒYZ d e fd „ƒYZ d e fd „ƒYZdefd„ƒYZdefd„ƒYZdefd„ƒYZdS(N(sticket(sutil(s*(sIPermissionRequestorsPermissionSystem(sIAdminPageProvidersComponentAdminPagecBs$tZeeƒd„Zd„ZRS(Nccs+|iidƒoddddfVndS(Ns TICKET_ADMINstickets Ticket Systems componentss Components(sreqspermshas_permission(sselfsreq((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/ticket.pysget_admin_pages sc s|iidƒ|oti|i|ƒ} |i djo¹|i i dƒol|i i dƒ| _ |i i dƒ| _ |i i dƒ| _| iƒ|i|iii||ƒƒqõ|i i dƒo#|i|iii||ƒƒqõnhd| i <d| i <d| i<|idAdminN( sselfs _get_pagessreqspagess providerssMarkupsenvshrefsadmin(sselfsreqspagess providers((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/web_ui.pysget_navigation_items?s  cCsjtid|iƒ}|oJ|idƒ|id<|idƒ|id<|idƒ|idesisUnknown Admin Pages cat_labelis page_labelishrefs admin.pagessadmin.active_catsadmin.active_pagesadmin.page_templatesadmin.page_contentsadmin/css/admin.csssadmin.cs(sselfs _get_pagessreqspagess providerss TracErrorsargssgetscat_idspage_ids path_infosfiltersNonesprovidersprocess_admin_requeststemplates content_typesappends_[1]spagesenvshrefsadminshdfs isinstances basestringsMarkupsrendersadd_stylesheet( sselfsreqs_[1]s content_types providersspages path_infostemplatesproviderscat_idspage_idspages((scat_ids3build/bdist.darwin-8.0.1-x86/egg/webadmin/web_ui.pysprocess_request]s,!! …   cCs'dkl}d|tdƒfgSdS(s„Return the absolute path of a directory containing additional static resources (such as images, style sheets, etc). (sresource_filenamesadminshtdocsN(s pkg_resourcessresource_filenames__name__(sselfsresource_filename((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/web_ui.pysget_htdocs_dirs€s cCs!dkl}|tdƒgSdS(siReturn the absolute path of the directory containing the provided ClearSilver templates. (sresource_filenames templatesN(s pkg_resourcessresource_filenames__name__(sselfsresource_filename((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/web_ui.pysget_templates_dirs‡s (s__name__s __module__s implementssINavigationContributorsIRequestHandlersITemplateProvidersExtensionPointsIAdminPageProviderspage_providerssget_active_navigation_itemsget_navigation_itemss match_requests _get_pagessprocess_requestsget_htdocs_dirssget_templates_dirs(((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/web_ui.pys AdminModule5s    # (sres trac.cores trac.permsIPermissionRequestors trac.utilsMarkupstrac.websIRequestHandlerstrac.web.chromesadd_stylesheetsINavigationContributorsITemplateProviders trac.web.hrefsHrefs__all__s InterfacesIAdminPageProviders Components AdminModule( sINavigationContributorsITemplateProviders__all__sIRequestHandlersIPermissionRequestorsMarkupsIAdminPageProvidersresHrefs AdminModulesadd_stylesheet((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/web_ui.pys?s      PK‘£e6Î8P¸?¸?webadmin/ticket.py# -*- coding: utf-8 -*- # # Copyright (C) 2005-2006 Edgewall Software # Copyright (C) 2005 Jonas Borgström # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://projects.edgewall.com/trac/. # # Author: Jonas Borgström import time from trac import ticket from trac import util from trac.core import * from trac.perm import IPermissionRequestor, PermissionSystem from webadmin.web_ui import IAdminPageProvider class ComponentAdminPage(Component): implements(IAdminPageProvider) # IAdminPageProvider methods def get_admin_pages(self, req): if req.perm.has_permission('TICKET_ADMIN'): yield ('ticket', 'Ticket System', 'components', 'Components') def process_admin_request(self, req, cat, page, component): req.perm.assert_permission('TICKET_ADMIN') # Detail view? if component: comp = ticket.Component(self.env, component) if req.method == 'POST': if req.args.get('save'): comp.name = req.args.get('name') comp.owner = req.args.get('owner') comp.description = req.args.get('description') comp.update() req.redirect(self.env.href.admin(cat, page)) elif req.args.get('cancel'): req.redirect(self.env.href.admin(cat, page)) req.hdf['admin.component'] = { 'name': comp.name, 'owner': comp.owner, 'description': comp.description } else: if req.method == 'POST': # Add Component if req.args.get('add') and req.args.get('name'): comp = ticket.Component(self.env) comp.name = req.args.get('name') if req.args.get('owner'): comp.owner = req.args.get('owner') comp.insert() req.redirect(self.env.href.admin(cat, page)) # Remove components elif req.args.get('remove') and req.args.get('sel'): sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] if not sel: raise TracError, 'No component selected' db = self.env.get_db_cnx() for name in sel: comp = ticket.Component(self.env, name, db=db) comp.delete(db=db) db.commit() req.redirect(self.env.href.admin(cat, page)) # Set default component elif req.args.get('apply'): if req.args.get('default'): name = req.args.get('default') self.log.info('Setting default component to %s', name) self.config.set('ticket', 'default_component', name) self.config.save() req.redirect(self.env.href.admin(cat, page)) default = self.config.get('ticket', 'default_component') req.hdf['admin.components'] = \ [{'name': c.name, 'owner': c.owner, 'is_default': c.name == default, 'href': self.env.href.admin(cat, page, c.name) } for c in ticket.Component.select(self.env)] if self.config.getbool('ticket', 'restrict_owner'): perm = PermissionSystem(self.env) def valid_owner(username): return perm.get_user_permissions(username).get('TICKET_MODIFY') req.hdf['admin.owners'] = [username for username, name, email in self.env.get_known_users() if valid_owner(username)] return 'admin_component.cs', None class MilestoneAdminPage(Component): implements(IAdminPageProvider) # IAdminPageProvider methods def get_admin_pages(self, req): if req.perm.has_permission('TICKET_ADMIN'): yield ('ticket', 'Ticket System', 'milestones', 'Milestones') def process_admin_request(self, req, cat, page, milestone): req.perm.assert_permission('TICKET_ADMIN') # Detail view? if milestone: mil = ticket.Milestone(self.env, milestone) if req.method == 'POST': if req.args.get('save'): mil.name = req.args.get('name') due = req.args.get('duedate', '') try: mil.due = due and util.parse_date(due) or 0 except ValueError, e: raise TracError(e, 'Invalid Date Format') if 'completed' in req.args: completed = req.args.get('completeddate', '') try: mil.completed = completed and \ util.parse_date(completed) or 0 except ValueError, e: raise TracError(e, 'Invalid Date Format') if mil.completed > time.time(): raise TracError('Completion date may not be in the ' 'future', 'Invalid Completion Date') else: mil.completed = 0 mil.description = req.args.get('description', '') mil.update() req.redirect(self.env.href.admin(cat, page)) elif req.args.get('cancel'): req.redirect(self.env.href.admin(cat, page)) req.hdf['admin.milestone'] = { 'name': mil.name, 'duedate': mil.due and util.format_datetime(mil.due) or '', 'description': mil.description } else: if req.method == 'POST': # Add Milestone if req.args.get('add') and req.args.get('name'): mil = ticket.Milestone(self.env) mil.name = req.args.get('name') if req.args.get('duedate'): mil.due = util.parse_date(req.args.get('duedate')) mil.insert() req.redirect(self.env.href.admin(cat, page)) # Remove milestone elif req.args.get('remove') and req.args.get('sel'): sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] if not sel: raise TracError, 'No milestone selected' db = self.env.get_db_cnx() for name in sel: mil = ticket.Milestone(self.env, name, db=db) mil.delete(db=db) db.commit() req.redirect(self.env.href.admin(cat, page)) # Set default milestone elif req.args.get('apply'): if req.args.get('default'): name = req.args.get('default') self.log.info('Setting default milestone to %s', name) self.config.set('ticket', 'default_milestone', name) self.config.save() req.redirect(self.env.href.admin(cat, page)) default = self.config.get('ticket', 'default_milestone') req.hdf['admin.milestones'] = \ [{'name': m.name, 'duedate': m.due and util.format_datetime(m.due) or '', 'is_default': m.name == default, 'href': self.env.href.admin(cat, page, m.name) } for m in ticket.Milestone.select(self.env)] req.hdf['admin.date_hint'] = util.get_date_format_hint() req.hdf['admin.datetime_hint'] = util.get_datetime_format_hint() req.hdf['admin.datetime_now'] = util.format_datetime() return 'admin_milestone.cs', None class VersionAdminPage(Component): implements(IAdminPageProvider) # IAdminPageProvider methods def get_admin_pages(self, req): if req.perm.has_permission('TICKET_ADMIN'): yield ('ticket', 'Ticket System', 'versions', 'Versions') def process_admin_request(self, req, cat, page, version): req.perm.assert_permission('TICKET_ADMIN') # Detail view? if version: ver = ticket.Version(self.env, version) if req.method == 'POST': if req.args.get('save'): ver.name = req.args.get('name') if req.args.get('time'): ver.time = util.parse_date(req.args.get('time')) ver.description = req.args.get('description') ver.update() req.redirect(self.env.href.admin(cat, page)) elif req.args.get('cancel'): req.redirect(self.env.href.admin(cat, page)) req.hdf['admin.version'] = { 'name': ver.name, 'time': ver.time and util.format_datetime(ver.time) or '', 'description': ver.description } else: if req.method == 'POST': # Add Version if req.args.get('add') and req.args.get('name'): ver = ticket.Version(self.env) ver.name = req.args.get('name') if req.args.get('time'): ver.time = util.parse_date(req.args.get('time')) ver.insert() req.redirect(self.env.href.admin(cat, page)) # Remove versions elif req.args.get('remove') and req.args.get('sel'): sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] if not sel: raise TracError, 'No version selected' db = self.env.get_db_cnx() for name in sel: ver = ticket.Version(self.env, name, db=db) ver.delete(db=db) db.commit() req.redirect(self.env.href.admin(cat, page)) # Set default version elif req.args.get('apply'): if req.args.get('default'): name = req.args.get('default') self.log.info('Setting default version to %s', name) self.config.set('ticket', 'default_version', name) self.config.save() req.redirect(self.env.href.admin(cat, page)) default = self.config.get('ticket', 'default_version') req.hdf['admin.versions'] = \ [{'name': v.name, 'time': v.time and util.format_datetime(v.time) or '', 'is_default': v.name == default, 'href': self.env.href.admin(cat, page, v.name) } for v in ticket.Version.select(self.env)] req.hdf['admin.date_hint'] = util.get_date_format_hint() req.hdf['admin.datetime_hint'] = util.get_datetime_format_hint() req.hdf['admin.datetime_now'] = util.format_datetime() return 'admin_version.cs', None class AbstractEnumAdminPage(Component): implements(IAdminPageProvider) abstract = True _type = 'unknown' _enum_cls = None _label = ('(Undefined)', '(Undefined)') # IAdminPageProvider methods def get_admin_pages(self, req): if req.perm.has_permission('TICKET_ADMIN'): yield ('ticket', 'Ticket System', self._type, self._label[1]) def process_admin_request(self, req, cat, page, path_info): req.perm.assert_permission('TICKET_ADMIN') req.hdf['admin.enum'] = { 'label_singular': self._label[0], 'label_plural': self._label[1] } # Detail view? if path_info: enum = self._enum_cls(self.env, path_info) if req.method == 'POST': if req.args.get('save'): enum.name = req.args.get('name') enum.update() req.redirect(self.env.href.admin(cat, page)) elif req.args.get('cancel'): req.redirect(self.env.href.admin(cat, page)) req.hdf['admin.enum'] = { 'name': enum.name, 'value': enum.value } else: default = self.config.get('ticket', 'default_%s' % self._type) if req.method == 'POST': # Add enum if req.args.get('add') and req.args.get('name'): enum = self._enum_cls(self.env) enum.name = req.args.get('name') enum.insert() req.redirect(self.env.href.admin(cat, page)) # Remove enums elif req.args.get('remove') and req.args.get('sel'): sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] if not sel: raise TracError, 'No enum selected' db = self.env.get_db_cnx() for name in sel: enum = self._enum_cls(self.env, name, db=db) enum.delete(db=db) db.commit() req.redirect(self.env.href.admin(cat, page)) # Appy changes elif req.args.get('apply'): # Set default value if req.args.get('default'): name = req.args.get('default') if name != default: self.log.info('Setting default %s to %s', self._type, name) self.config.set('ticket', 'default_%s' % self._type, name) self.config.save() # Change enum values order = dict([(key[6:], req.args.get(key)) for key in req.args.keys() if key.startswith('value_')]) values = dict([(val, True) for val in order.values()]) if len(order) != len(values): raise TracError, 'Order numbers must be unique' db = self.env.get_db_cnx() for enum in self._enum_cls.select(self.env, db=db): new_value = order[enum.value] if new_value != enum.value: enum.value = new_value enum.update(db=db) db.commit() req.redirect(self.env.href.admin(cat, page)) req.hdf['admin.enums'] = [ {'name': e.name, 'value': e.value, 'is_default': e.name == default, 'href': self.env.href.admin(cat, page, e.name) } for e in self._enum_cls.select(self.env)] return 'admin_enum.cs', None class PriorityAdminPage(AbstractEnumAdminPage): _type = 'priority' _enum_cls = ticket.Priority _label = ('Priority', 'Priorities') class SeverityAdminPage(AbstractEnumAdminPage): _type = 'severity' _enum_cls = ticket.Severity _label = ('Severity', 'Severities') class TicketTypeAdminPage(AbstractEnumAdminPage): _type = 'type' _enum_cls = ticket.Type _label = ('Ticket Type', 'Ticket Types') PKn…68^CËÛ Û webadmin/perm.pyc;ò ÂÃìEc@sDdkZdkTdklZdklZdefd„ƒYZdS(N(s*(sPermissionSystem(sIAdminPageProvidersPermissionAdminPagecBs$tZeeƒd„Zd„ZRS(Nccs+|iidƒoddddfVndS(Ns TRAC_ADMINsgeneralsGeneralsperms Permissions(sreqspermshas_permission(sselfsreq((s1build/bdist.darwin-8.0.1-x86/egg/webadmin/perm.pysget_admin_pagesscCst|iƒ}|iƒ}|iidƒ} |iidƒ} |iidƒ}|i djoš|iidƒo | o| oV| |i ƒjotdƒ‚n|i| | ƒ|i|iii||ƒƒqû|iidƒo | o|o3|i| |ƒ|i|iii||ƒƒqû|iidƒo|iidƒo¥|iidƒ} t| tƒo| p| g} xM| D]E} | id d ƒ\} } | | f|jo|i| | ƒq‹q‹W|i|iii||ƒƒqûn|id „ƒ|i ƒ|id ?ss admin.actionsiskeys%s:%ss admin.permss admin_perm.cs(!sPermissionSystemsselfsenvspermsget_all_permissionsspermssreqsargssgetssubjectsactionsgroupsmethods get_actionss TracErrorsgrant_permissionsredirectshrefsadminscatspagessels isinstanceslistskeyssplitsrevoke_permissionssortshdfsappends_[1]spsNone(sselfsreqscatspages path_infosgroupspermssperms_[1]spskeysactionsselssubject((s1build/bdist.darwin-8.0.1-x86/egg/webadmin/perm.pysprocess_admin_request!s6 !#!#& 'U(s__name__s __module__s implementssIAdminPageProvidersget_admin_pagessprocess_admin_request(((s1build/bdist.darwin-8.0.1-x86/egg/webadmin/perm.pysPermissionAdminPages  (sres trac.cores trac.permsPermissionSystemswebadmin.web_uisIAdminPageProviders ComponentsPermissionAdminPage(sresPermissionSystemsPermissionAdminPagesIAdminPageProvider((s1build/bdist.darwin-8.0.1-x86/egg/webadmin/perm.pys?s   PKm…68£ƒ=í-'-'webadmin/plugin.pyc;ò ÂÃìEc@s×dkZdkZdkZdkZdkZdklZlZdk Tdk l Z y dk Z Wne j o eZ nXd„ZeeididƒZdddd d d d d fZdefd„ƒYZdS(N(stickets __version__(s*(sIAdminPageProvidercCs€tii|ƒd}x_|itidƒi|ƒo?tii|ƒ}di |i dƒd ƒ}| oPqqW|SdS(Nis.iÿÿÿÿ( sosspathssplitexts base_pathsreplacessepsendswiths module_namesdirnamesjoinssplit(spaths module_names base_path((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/plugin.pys_find_base_path s s trac.cores AboutModulesDefaultPermissionGroupProviders EnvironmentsEnvironmentSetupsPermissionSystemsRequestDispatchersMimeviewsChromesPluginAdminPagecBsZtZeeƒd„Zd„Zd„Zd„Zd„Zd„Z d„Z d„Z RS( Nccs+|iidƒoddddfVndS(Ns TRAC_ADMINsgeneralsGeneralspluginsPlugins(sreqspermshas_permission(sselfsreq((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/plugin.pysget_admin_pages7scCs|iidƒ|idjoÜ|iidƒo|i|ƒn_|iidƒo|i|ƒn;|iidƒo|i |ƒn|i i d|iƒd}|iidƒod |ii dƒ}n|i|iii||ƒ|ƒn|i|ƒd tfSdS( Ns TRAC_ADMINsPOSTsupdatesinstalls uninstallsUnknown POST request: %sssplugins#nosadmin_plugin.cs(sreqspermsassert_permissionsmethodsargsshas_keysselfs _do_updates _do_installs _do_uninstallslogswarningsanchorsgetsredirectsenvshrefsadminscatspages _render_viewsNone(sselfsreqscatspages_sanchor((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/plugin.pysprocess_admin_request;s' cCs­|iidƒ o td‚n|id}|i o td‚n|iiddƒiddƒ}ti i |ƒ}| o td‚n|i dƒ o|i dƒ o td‚nti i |ii d |ƒ}ti i|ƒotd |‚n|iid |ƒtititi}y|ti7}Wntj onXtiti||ƒd ƒ}z-ti|i|ƒ|iid ||ƒWd|iƒXdS(sInstall a plugin.s plugin_filesNo file uploadeds\s/s:s.eggs.pys0Uploaded file is not a Python source file or eggspluginssPlugin %s already installedsInstalling plugin %sswsPlugin %s installed to %sN( sreqsargsshas_keys TracErrorsuploadsfilenamesreplacesplugin_filenamesosspathsbasenamesendswithsjoinsselfsenvs target_pathsisfileslogsinfosO_CREATsO_WRONLYsO_EXCLsflagssO_BINARYsAttributeErrorsfdopensopens target_filesshutils copyfileobjsfilesclose(sselfsreqs target_filesplugin_filenamesuploads target_pathsflags((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/plugin.pys _do_installQs8    ! "  cCs€|iidƒ}| odSntii|iid|ƒ}tii |ƒ odSn|i i d|ƒti |ƒdS(sUninstall a plugin.splugin_filenameNspluginssUninstalling plugin %s(sreqsargssgetsplugin_filenamesosspathsjoinsselfsenvs plugin_pathsisfileslogsinfosremove(sselfsreqsplugin_filenames plugin_path((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/plugin.pys _do_uninstalluscCsÎ|iidƒ}|iidƒ}t}x…|D]}}|i i |ƒ}|||jjoR|i i d||odpdƒ|iid|odpd|ƒt}q1q1W|o|i iƒnd S( sUpdate component enablement.s componentsenables componentssdisabledsenableds%sabling component %ssDissEnN(sreqsargssgetlists componentssenabledsFalseschangess componentsselfsenvsis_component_enableds is_enabledsconfigssetslogsinfosTruessave(sselfsreqs is_enableds componentsenableds componentsschanges((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/plugin.pys _do_update€s cCsøh}tiitii|iidƒƒ} dkl } x½| i D]²} t i | i}|i|ƒ}t} tiitii|iƒƒ| jotii|iƒ} nti| ƒ}|o|iddƒdd}n|i|jo¢t}| oti|ititi ƒo t!}nhd|i<d|i"<d|i<d |<d | <d |<d |i#|ƒ<d g<||i  cCsï|i}|idƒp |idƒo|d }ntii|ƒdjotii|ƒ}nt||iƒ}|t jo t i dddt d|ƒSnxCt i |d tƒD] }|Sq»Wt i d|idd d|iƒSdS( Ns.pycs.pyoiÿÿÿÿs __init__.pys project_namesTracsversionslocationsonlys(smodules__file__spathsendswithsossbasenamesdirnames_find_base_paths__name__s TRAC_PATHs pkg_resourcess Distributions TRAC_VERSIONsfind_distributionssTruesdist(sselfsmodulesdistspath((s3build/bdist.darwin-8.0.1-x86/egg/webadmin/plugin.pys_find_distributionÍs     c Csßddddddf}h}yti|idƒƒ}x`gi}|D]!}||jo||ƒqJqJ~D]&}||||i ƒi dd ƒ # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://projects.edgewall.com/trac/. # # Author: Christopher Lenz from trac.core import * from webadmin.web_ui import IAdminPageProvider class ProjectAdminPage(Component): implements(IAdminPageProvider) # IAdminPageProvider methods def get_admin_pages(self, req): if req.perm.has_permission('TRAC_ADMIN'): yield ('general', 'General', 'basic', 'Basic Settings') def process_admin_request(self, req, cat, page, path_info): if req.method == "POST": self.config.set('project', 'name', req.args.get('name')) self.config.set('project', 'url', req.args.get('url')) self.config.set('project', 'descr', req.args.get('description')) self.config.save() req.redirect(self.env.href.admin(cat, page)) req.hdf['admin.project'] = { 'name': self.config.get('project', 'name'), 'description': self.config.get('project', 'descr'), 'url': self.config.get('project', 'url') } return 'admin_basics.cs', None PKm…682«««webadmin/__init__.pyc;ò ÂÃìEc@s dkTdS((s*N(swebadmin.web_ui(((s5build/bdist.darwin-8.0.1-x86/egg/webadmin/__init__.pys?sPK‘£e6Ù“ eYYwebadmin/web_ui.py# -*- coding: utf-8 -*- # # Copyright (C) 2005-2006 Edgewall Software # Copyright (C) 2005 Jonas Borgström # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://projects.edgewall.com/trac/. # # Author: Jonas Borgström import re from trac.core import * from trac.perm import IPermissionRequestor from trac.util import Markup from trac.web import IRequestHandler from trac.web.chrome import add_stylesheet, INavigationContributor, \ ITemplateProvider from trac.web.href import Href __all__ = ['IAdminPageProvider'] class IAdminPageProvider(Interface): """ Extension point interface for adding pages to the admin module. """ def get_admin_pages(self, req): """ Return a list of available admin pages. The pages returned by this function must be a tuple of the form (category, category_label, page, page_label). """ def process_admin_request(self, req, category, page, path_info): """ Process the request for the admin `page`. This function should return a tuple of the form (template, content_type) where `template` is the ClearSilver template to use (either a `neo_cs.CS` object, or the file name of the template) and `content_type` is the MIME type of the content. If `content_type` is `None`, "text/html" is assumed. """ class AdminModule(Component): implements(INavigationContributor, IRequestHandler, ITemplateProvider) page_providers = ExtensionPoint(IAdminPageProvider) # INavigationContributor methods def get_active_navigation_item(self, req): return 'admin' def get_navigation_items(self, req): """The 'Admin' navigation item is only visible if at least one admin page is available.""" pages, providers = self._get_pages(req) if pages: yield 'mainnav', 'admin', Markup('Admin', self.env.href.admin()) # IRequestHandler methods def match_request(self, req): match = re.match('/admin(?:/([^/]+))?(?:/([^/]+))?(?:/(.*)$)?', req.path_info) if match: req.args['cat_id'] = match.group(1) req.args['page_id'] = match.group(2) req.args['path_info'] = match.group(3) return True def _get_pages(self, req): """Return a list of available admin pages.""" pages = [] providers = {} for provider in self.page_providers: p = list(provider.get_admin_pages(req)) for page in p: providers[(page[0], page[2])] = provider pages += p pages.sort() return pages, providers def process_request(self, req): pages, providers = self._get_pages(req) if not pages: raise TracError('No admin pages available') cat_id = req.args.get('cat_id') or pages[0][0] page_id = req.args.get('page_id') path_info = req.args.get('path_info') if not page_id: page_id = filter(lambda page: page[0] == cat_id, pages)[0][2] provider = providers.get((cat_id, page_id), None) if not provider: raise TracError('Unknown Admin Page') template, content_type = provider.process_admin_request(req, cat_id, page_id, path_info) req.hdf['admin.pages'] = [{'cat_id': page[0], 'cat_label': page[1], 'page_id': page[2], 'page_label': page[3], 'href': self.env.href.admin(page[0], page[2]) } for page in pages] req.hdf['admin.active_cat'] = cat_id req.hdf['admin.active_page'] = page_id if isinstance(template, basestring): req.hdf['admin.page_template'] = template else: req.hdf['admin.page_content'] = Markup(template.render()) add_stylesheet(req, 'admin/css/admin.css') return 'admin.cs', content_type # ITemplateProvider def get_htdocs_dirs(self): """Return the absolute path of a directory containing additional static resources (such as images, style sheets, etc). """ from pkg_resources import resource_filename return [('admin', resource_filename(__name__, 'htdocs'))] def get_templates_dirs(self): """Return the absolute path of the directory containing the provided ClearSilver templates. """ from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] PK‘£e6__.Hc c webadmin/perm.py# -*- coding: utf-8 -*- # # Copyright (C) 2005-2006 Edgewall Software # Copyright (C) 2005 Jonas Borgström # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://projects.edgewall.com/trac/. # # Author: Jonas Borgström import re from trac.core import * from trac.perm import PermissionSystem from webadmin.web_ui import IAdminPageProvider class PermissionAdminPage(Component): implements(IAdminPageProvider) # IAdminPageProvider def get_admin_pages(self, req): if req.perm.has_permission('TRAC_ADMIN'): yield ('general', 'General', 'perm', 'Permissions') def process_admin_request(self, req, cat, page, path_info): perm = PermissionSystem(self.env) perms = perm.get_all_permissions() subject = req.args.get('subject') action = req.args.get('action') group = req.args.get('group') if req.method == 'POST': # Grant permission to subject if req.args.get('add') and subject and action: if action not in perm.get_actions(): raise TracError('Unknown action') perm.grant_permission(subject, action) req.redirect(self.env.href.admin(cat, page)) # Add subject to group elif req.args.get('add') and subject and group: perm.grant_permission(subject, group) req.redirect(self.env.href.admin(cat, page)) # Remove permissions action elif req.args.get('remove') and req.args.get('sel'): sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] for key in sel: subject, action = key.split(':', 1) if (subject, action) in perms: perm.revoke_permission(subject, action) req.redirect(self.env.href.admin(cat, page)) perms.sort(lambda a, b: cmp(a[0], b[0])) req.hdf['admin.actions'] = perm.get_actions() req.hdf['admin.perms'] = [{'subject': p[0], 'action': p[1], 'key': '%s:%s' % p } for p in perms] return 'admin_perm.cs', None PK£e6zae!webadmin/htdocs/js/admin.jsfunction toggleClass(element, class1, class2) { function indexOf(array, obj) { if (obj) { for (var i = 0; i < array.length; i++) { if (array[i] == obj) return i; } } return array.length; } var classNames = element.className.split(/\s+/) || []; var classIndex = indexOf(classNames, class1); if (classIndex >= classNames.length) { classIndex = indexOf(classNames, class2); var tmp = class1; class1 = class2; class2 = tmp; } classNames.splice(classIndex, class1 ? 1 : 0, class2); element.className = classNames.join(' '); } var fragmentId = document.location.hash; if (fragmentId) { fragmentId = fragmentId.substr(1); } function enableFolding(triggerId) { var trigger = document.getElementById(triggerId); if (!trigger) return; toggleClass(trigger.parentNode, triggerId != fragmentId ? "collapsed" : "expanded"); var link = document.createElement("a"); link.href = "#" + triggerId; trigger.parentNode.replaceChild(link, trigger); link.appendChild(trigger); trigger.style.cursor = "pointer"; addEvent(link, "click", function() { toggleClass(link.parentNode, "expanded", "collapsed"); }); } PK£e6¡1ãã webadmin/htdocs/img/expanded.png‰PNG  IHDR žr‡gAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<PLTEsss–––øøøÐÐÐ………®®®æææÿÿÿÃæOtRNSÿÿÿÿÿÿÿÞƒ½Y=IDATxÚDÉI0Aþÿã¨Ù8u1ÐhØ3BÏ‚f4c¦5ÚCe¯ÞÓÔv†ºÛ¨k7>[ rŠÀÒÿÞIEND®B`‚PK£e6ÇQ·¬ÚÚ!webadmin/htdocs/img/collapsed.png‰PNG  IHDR žr‡gAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<PLTEsss–––øøøÐÐÐ………®®®æææÿÿÿÃæOtRNSÿÿÿÿÿÿÿÞƒ½Y4IDATxÚb`gf‡vFF68›…• Îò˜™àlÆf``Ä"ŽP0Ù|„½ƒº˜²]sIEND®B`‚PK£e6àÍã­llwebadmin/htdocs/css/admin.css.tabs { border: 1px solid black; border-color: gray black black gray; margin: 2em 0; padding: .5em 0 0; float: left; width: 12em; } .tabs ul { list-style: none; margin: 0 0 .5em; padding: 0 } .tabs li { margin: 0; padding: 0.1em .5em } .tabs li li { margin: 0 -0.5em; padding-left: 1.5em } .tabs li li.active { background: #ccb; } .tabs :link, .tabs :visited { border: none; display: block } .tabs :link:hover, .tabs :visited:hover { background: transparent } .tabcontents { padding: 0.4em 2em; margin-left: 12em; min-height: 300px; } p.help { color: #666; font-size: 90%; margin: 1em .5em .5em } form.addnew { clear: right; float: right; margin: -2em 0 4em; width: 33% } form.mod { margin-top: 1em; } form.mod .field { margin: .5em 0; } form .field em { color: #888; font-size: smaller } form .field .disabled em { color: #d7d7d7 } table.listing { clear: none; width: 64% } table.listing .sel, table.listing .default { text-align: center; width: 1% } /* Plugins panel */ form#addplug { width: 35% } .plugin { background: #f7f7f7; border: 1px solid #d7d7d7; margin: 0 0 2em; padding: 2px .5em; text-align: left; width: 60%; } .plugin h3 { background: url(../img/expanded.png) 0 50% no-repeat; margin: .5em 0 0; padding-left: 16px; } .collapsed h3 { background-image: url(../img/collapsed.png); } .plugin .buttons { margin-top: 0; text-align: right } .plugin .uninstall { margin-top: -2em; padding: 0 } .plugin .summary, .plugin .info { color: #999; font-size: 80%; padding-left: 16px; } .plugin .summary { margin: -.5em 0 .5em } .plugin .info { margin: 1em 0 .5em; } .plugin .info dt { float: left; width: 7em; } .plugin .info dd { padding: 0; margin: 0; } .plugin .listing { width: 100% } .collapsed .info, .collapsed .listing, .collapsed .update { display: none } .plugin .listing td { background: #fff } .plugin .listing .name p { color: #999; font-size: 80%; margin: 0 } PK‘£e6à­)‘ ‘ %webadmin/templates/admin_component.cs

Manage Components

Modify Component:

Add Component:
 Name OwnerDefault
checked="checked" >

You can remove all items from this list to completely hide this field from the user interface.

As long as you don't add any items to the list, this field will remain completely hidden from the user interface.

PK‘£e684YÖ::%webadmin/templates/admin_milestone.cs

Manage Milestones

Modify Milestone:

Add Milestone:
 Name TimeDefault
checked="checked" >

You can remove all items from this list to completely hide this field from the user interface.

As long as you don't add any items to the list, this field will remain completely hidden from the user interface.

PK‘£e6X ÷6ººwebadmin/templates/admin.cs

Administration

class="active">

  • PK‘£e6%З­­"webadmin/templates/admin_basics.cs

    Basic Settings

    Project
    PK‘£e69_€ññ webadmin/templates/admin_perm.cs

    Manage Permissions

    Grant Permission:

    Grant permission for an action to a subject, which can be either a user or a group.

    Add Subject to Group:

    Add a user or group to an existing permission group.

     SubjectAction
    PK‘£e6—é8“……webadmin/templates/admin_log.cs

    Logging Configuration

    If you specify a relative path, the log file will be stored inside the log directory of the project environment ().

    You may need to restart the server for these changes to take effect.

    PK‘£e61N;U U #webadmin/templates/admin_version.cs

    Manage Versions

    Modify Version:

    Add Version:
     Name TimeDefault
    checked="checked" >

    You can remove all items from this list to completely hide this field from the user interface.

    As long as you don't add any items to the list, this field will remain completely hidden from the user interface.

    PK‘£e6·êÜvšš webadmin/templates/admin_enum.cs

    Manage

    Modify
    Add
     Name DefaultOrder
    checked="checked" />

    You can remove all items from this list to completely hide this field from the user interface.

    As long as you don't add any items to the list, this field will remain completely hidden from the user interface.

    PK‘£e6~F”±] ] "webadmin/templates/admin_plugin.cs

    Manage Plugins

    Install Plugin:

    The web server does not have sufficient permissions to store files in the environment plugins directory. Upload a plugin packaged as Python egg.

    disabled="disabled" />

    disabled="disabled" />

    Author:
    Home page:
    License:
    ComponentEnabled

    checked="checked" disabled="disabled" />
    PKe…68úÀýPP¤EGG-INFO/SOURCES.txtPKd…68º 8¦ÚÚ¤‚EGG-INFO/entry_points.txtPKd…68“×2¤“EGG-INFO/dependency_links.txtPKd…68‘Oúð¤ÏEGG-INFO/PKG-INFOPKd…68•4Ì ¤EGG-INFO/top_level.txtPKp…68“×2¤QEGG-INFO/not-zip-safePKn…68¥¥_!¤…webadmin/basics.pycPK‘£e6Åo'P  ¤E webadmin/__init__.pyPK‘£e6¿\ÝfZZ¤‚ webadmin/logging.pyPK‘£e6&Œ=Ø$$¤ webadmin/plugin.pyPKn…68Äš¢É < <¤\Awebadmin/ticket.pycPKn…68¶÷ÁèQQ¤—}webadmin/web_ui.pycPK‘£e6Î8P¸?¸?¤™webadmin/ticket.pyPKn…68^CËÛ Û ¤Ùwebadmin/perm.pycPKm…68£ƒ=í-'-'¤ äwebadmin/plugin.pycPKm…68Uç€íÕ Õ ¤i webadmin/logging.pycPK‘£e6©ê@::¤pwebadmin/basics.pyPKm…682«««¤Úwebadmin/__init__.pycPK‘£e6Ù“ eYY¤¸webadmin/web_ui.pyPK‘£e6__.Hc c ¤A4webadmin/perm.pyPK£e6zae!´Ò>webadmin/htdocs/js/admin.jsPK£e6¡1ãã ´Dwebadmin/htdocs/img/expanded.pngPK£e6ÇQ·¬ÚÚ!´2Ewebadmin/htdocs/img/collapsed.pngPK£e6àÍã­ll´KFwebadmin/htdocs/css/admin.cssPK‘£e6à­)‘ ‘ %´òMwebadmin/templates/admin_component.csPK‘£e684YÖ::%´ÆYwebadmin/templates/admin_milestone.csPK‘£e6X ÷6ºº´Ciwebadmin/templates/admin.csPK‘£e6%З­­"´6mwebadmin/templates/admin_basics.csPK‘£e69_€ññ ´#pwebadmin/templates/admin_perm.csPK‘£e6—é8“……´Rvwebadmin/templates/admin_log.csPK‘£e61N;U U #´}webadmin/templates/admin_version.csPK‘£e6·êÜvšš ´ªˆwebadmin/templates/admin_enum.csPK‘£e6~F”±] ] "´‚‘webadmin/templates/admin_plugin.csPK!! ž