PKmh92EGG-INFO/dependency_links.txt PKmh9EGG-INFO/entry_points.txt [console_scripts] trac-admin = trac.admin.console:run tracd = trac.web.standalone:main [trac.plugins] trac.about = trac.about trac.admin.console = trac.admin.console trac.admin.web_ui = trac.admin.web_ui trac.attachment = trac.attachment trac.db.mysql = trac.db.mysql_backend trac.db.postgres = trac.db.postgres_backend trac.db.sqlite = trac.db.sqlite_backend trac.mimeview.enscript = trac.mimeview.enscript trac.mimeview.patch = trac.mimeview.patch trac.mimeview.php = trac.mimeview.php trac.mimeview.pygments = trac.mimeview.pygments[Pygments] trac.mimeview.rst = trac.mimeview.rst[reST] trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity] trac.mimeview.txtl = trac.mimeview.txtl[Textile] trac.prefs = trac.prefs.web_ui trac.search = trac.search.web_ui trac.ticket.admin = trac.ticket.admin trac.ticket.query = trac.ticket.query trac.ticket.report = trac.ticket.report trac.ticket.roadmap = trac.ticket.roadmap trac.ticket.web_ui = trac.ticket.web_ui trac.timeline = trac.timeline.web_ui trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs trac.versioncontrol.web_ui = trac.versioncontrol.web_ui trac.web.auth = trac.web.auth trac.wiki.macros = trac.wiki.macros trac.wiki.web_ui = trac.wiki.web_ui PKkh92EGG-INFO/not-zip-safe PKmh9tGEGG-INFO/PKG-INFOMetadata-Version: 1.0 Name: Trac Version: 0.11.2 Summary: Integrated SCM, wiki, issue tracker and project environment Home-page: http://trac.edgewall.org/ Author: Edgewall Software Author-email: info@edgewall.com License: BSD Download-URL: http://trac.edgewall.org/wiki/TracDownload Description: Trac is a minimalistic web-based software project management and bug/issue tracking system. It provides an interface to the Subversion revision control systems, an integrated wiki, flexible issue tracking and convenient report facilities. Platform: UNKNOWN Classifier: Environment :: Web Environment Classifier: Framework :: Trac Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Bug Tracking Classifier: Topic :: Software Development :: Version Control PKmh99{EGG-INFO/requires.txtsetuptools>=0.6b1 Genshi>=0.5 [SilverCity] SilverCity>=0.9.4 [Textile] textile>=2.0 [Pygments] Pygments>=0.6 [reST] docutils>=0.3PKmh9]O11EGG-INFO/SOURCES.txtAUTHORS COPYING ChangeLog INSTALL README RELEASE TESTING-README THANKS UPGRADE setup.cfg setup.py setup_wininst.bmp Trac.egg-info/PKG-INFO Trac.egg-info/SOURCES.txt Trac.egg-info/dependency_links.txt Trac.egg-info/entry_points.txt Trac.egg-info/not-zip-safe Trac.egg-info/requires.txt Trac.egg-info/top_level.txt cgi-bin/trac.cgi cgi-bin/trac.fcgi contrib/README contrib/bugzilla2trac.py contrib/checkwiki.py contrib/emailfilter.py contrib/htdigest.py contrib/htpasswd.py contrib/migrateticketmodel.py contrib/sourceforge2trac.py contrib/trac-post-commit-hook contrib/trac-post-commit-hook.cmd contrib/trac-pre-commit-hook contrib/rpm/installscript contrib/rpm/makerpm contrib/workflow/README contrib/workflow/enterprise-workflow.ini contrib/workflow/migrate_original_to_basic.py contrib/workflow/opensource-workflow.ini contrib/workflow/showworkflow contrib/workflow/simple-workflow.ini contrib/workflow/trivial-workflow.ini contrib/workflow/workflow_parser.py doc/README doc/trac_icon_16x16.png doc/trac_icon_32x32.png doc/trac_logo.png doc/trac_logo.svg sample-plugins/HelloWorld.py sample-plugins/Timestamp.py sample-plugins/revision_links.py sample-plugins/ticket_clone.py sample-plugins/permissions/authz_policy.py sample-plugins/permissions/public_wiki_policy.py sample-plugins/permissions/vulnerability_tickets.py sample-plugins/workflow/CodeReview.py sample-plugins/workflow/DeleteTicket.py sample-plugins/workflow/StatusFixer.py sample-plugins/workflow/VoteOperation.py sample-plugins/workflow/enterprise-review-workflow.ini trac/__init__.py trac/about.py trac/attachment.py trac/config.py trac/core.py trac/db_default.py trac/env.py trac/loader.py trac/log.py trac/notification.py trac/perm.py trac/resource.py trac/test.py trac/admin/__init__.py trac/admin/api.py trac/admin/console.py trac/admin/web_ui.py trac/admin/templates/admin.html trac/admin/templates/admin_basics.html trac/admin/templates/admin_components.html trac/admin/templates/admin_enums.html trac/admin/templates/admin_legacy.html trac/admin/templates/admin_logging.html trac/admin/templates/admin_milestones.html trac/admin/templates/admin_perms.html trac/admin/templates/admin_plugins.html trac/admin/templates/admin_versions.html trac/admin/templates/deploy_trac.cgi trac/admin/templates/deploy_trac.fcgi trac/admin/templates/deploy_trac.wsgi trac/admin/tests/__init__.py trac/admin/tests/console-tests.txt trac/admin/tests/console.py trac/db/__init__.py trac/db/api.py trac/db/mysql_backend.py trac/db/pool.py trac/db/postgres_backend.py trac/db/schema.py trac/db/sqlite_backend.py trac/db/util.py trac/db/tests/__init__.py trac/db/tests/api.py trac/htdocs/README trac/htdocs/asc.png trac/htdocs/attachment.png trac/htdocs/changeset.png trac/htdocs/closedticket.png trac/htdocs/collapsed.png trac/htdocs/desc.png trac/htdocs/dots.gif trac/htdocs/draft.png trac/htdocs/edit_toolbar.png trac/htdocs/editedticket.png trac/htdocs/envelope.png trac/htdocs/expanded.png trac/htdocs/expander_normal.png trac/htdocs/expander_normal_hover.png trac/htdocs/expander_open.png trac/htdocs/expander_open_hover.png trac/htdocs/extlink.gif trac/htdocs/feed.png trac/htdocs/file.png trac/htdocs/folder.png trac/htdocs/ics.png trac/htdocs/imggrid.png trac/htdocs/loading.gif trac/htdocs/lock-locked.png trac/htdocs/milestone.png trac/htdocs/newticket.png trac/htdocs/parent.png trac/htdocs/python.png trac/htdocs/topbar_gradient.png trac/htdocs/topbar_gradient2.png trac/htdocs/trac.ico trac/htdocs/trac_banner.png trac/htdocs/trac_logo_mini.png trac/htdocs/vgradient.png trac/htdocs/wiki.png trac/htdocs/css/about.css trac/htdocs/css/admin.css trac/htdocs/css/browser.css trac/htdocs/css/changeset.css trac/htdocs/css/code.css trac/htdocs/css/diff.css trac/htdocs/css/prefs.css trac/htdocs/css/report.css trac/htdocs/css/roadmap.css trac/htdocs/css/search.css trac/htdocs/css/ticket.css trac/htdocs/css/timeline.css trac/htdocs/css/trac.css trac/htdocs/css/wiki.css trac/htdocs/guide/basic-workflow.png trac/htdocs/guide/original-workflow.png trac/htdocs/js/blame.js trac/htdocs/js/diff.js trac/htdocs/js/expand_dir.js trac/htdocs/js/folding.js trac/htdocs/js/ie_pre7_hacks.js trac/htdocs/js/jquery.js trac/htdocs/js/keyboard_nav.js trac/htdocs/js/noconflict.js trac/htdocs/js/query.js trac/htdocs/js/search.js trac/htdocs/js/suggest.js trac/htdocs/js/trac.js trac/htdocs/js/wikitoolbar.js trac/mimeview/__init__.py trac/mimeview/api.py trac/mimeview/enscript.py trac/mimeview/patch.py trac/mimeview/php.py trac/mimeview/pygments.py trac/mimeview/rst.py trac/mimeview/silvercity.py trac/mimeview/txtl.py trac/mimeview/tests/__init__.py trac/mimeview/tests/api.py trac/mimeview/tests/patch.html trac/mimeview/tests/patch.py trac/mimeview/tests/php.py trac/mimeview/tests/pygments.html trac/mimeview/tests/pygments.py trac/prefs/__init__.py trac/prefs/api.py trac/prefs/web_ui.py trac/prefs/templates/prefs.html trac/prefs/templates/prefs_advanced.html trac/prefs/templates/prefs_datetime.html trac/prefs/templates/prefs_general.html trac/prefs/templates/prefs_keybindings.html trac/prefs/templates/prefs_pygments.html trac/prefs/tests/__init__.py trac/prefs/tests/functional.py trac/search/__init__.py trac/search/api.py trac/search/web_ui.py trac/search/templates/opensearch.xml trac/search/templates/search.html trac/templates/README trac/templates/about.html trac/templates/attachment.html trac/templates/diff_div.html trac/templates/diff_view.html trac/templates/error.html trac/templates/footer.cs trac/templates/header.cs trac/templates/history_view.html trac/templates/index.html trac/templates/layout.html trac/templates/macros.cs trac/templates/macros.html trac/templates/macros.rss trac/templates/page_index.html trac/templates/theme.html trac/tests/__init__.py trac/tests/allwiki.py trac/tests/attachment.py trac/tests/config.py trac/tests/contentgen.py trac/tests/core.py trac/tests/env.py trac/tests/notification.py trac/tests/perm.py trac/tests/resource.py trac/tests/wikisyntax.py trac/tests/functional/__init__.py trac/tests/functional/better_twill.py trac/tests/functional/compat.py trac/tests/functional/testcases.py trac/tests/functional/testenv.py trac/tests/functional/tester.py trac/tests/functional/xhtml-lat1.ent trac/tests/functional/xhtml-special.ent trac/tests/functional/xhtml-symbol.ent trac/tests/functional/xhtml1-strict.dtd trac/ticket/__init__.py trac/ticket/admin.py trac/ticket/api.py trac/ticket/default_workflow.py trac/ticket/model.py trac/ticket/notification.py trac/ticket/query.py trac/ticket/report.py trac/ticket/roadmap.py trac/ticket/web_ui.py trac/ticket/templates/milestone_delete.html trac/ticket/templates/milestone_edit.html trac/ticket/templates/milestone_view.html trac/ticket/templates/query.html trac/ticket/templates/query.rss trac/ticket/templates/query_results.html trac/ticket/templates/report.rss trac/ticket/templates/report_delete.html trac/ticket/templates/report_edit.html trac/ticket/templates/report_view.html trac/ticket/templates/roadmap.html trac/ticket/templates/ticket.html trac/ticket/templates/ticket.rss trac/ticket/templates/ticket_notify_email.txt trac/ticket/tests/__init__.py trac/ticket/tests/api.py trac/ticket/tests/conversion.py trac/ticket/tests/functional.py trac/ticket/tests/model.py trac/ticket/tests/notification.py trac/ticket/tests/query.py trac/ticket/tests/report.py trac/ticket/tests/roadmap.py trac/ticket/tests/wikisyntax.py trac/ticket/workflows/basic-workflow.ini trac/ticket/workflows/original-workflow.ini trac/timeline/__init__.py trac/timeline/api.py trac/timeline/web_ui.py trac/timeline/templates/timeline.html trac/timeline/templates/timeline.rss trac/timeline/tests/__init__.py trac/timeline/tests/functional.py trac/upgrades/__init__.py trac/upgrades/db10.py trac/upgrades/db11.py trac/upgrades/db12.py trac/upgrades/db13.py trac/upgrades/db14.py trac/upgrades/db15.py trac/upgrades/db16.py trac/upgrades/db17.py trac/upgrades/db18.py trac/upgrades/db19.py trac/upgrades/db20.py trac/upgrades/db21.py trac/upgrades/db3.py trac/upgrades/db4.py trac/upgrades/db5.py trac/upgrades/db6.py trac/upgrades/db7.py trac/upgrades/db8.py trac/upgrades/db9.py trac/util/__init__.py trac/util/autoreload.py trac/util/compat.py trac/util/daemon.py trac/util/datefmt.py trac/util/html.py trac/util/presentation.py trac/util/text.py trac/util/translation.py trac/util/tests/__init__.py trac/util/tests/datefmt.py trac/util/tests/presentation.py trac/util/tests/text.py trac/versioncontrol/__init__.py trac/versioncontrol/api.py trac/versioncontrol/cache.py trac/versioncontrol/diff.py trac/versioncontrol/svn_authz.py trac/versioncontrol/svn_fs.py trac/versioncontrol/templates/browser.html trac/versioncontrol/templates/changeset.html trac/versioncontrol/templates/diff_form.html trac/versioncontrol/templates/dir_entries.html trac/versioncontrol/templates/revisionlog.html trac/versioncontrol/templates/revisionlog.rss trac/versioncontrol/templates/revisionlog.txt trac/versioncontrol/tests/__init__.py trac/versioncontrol/tests/api.py trac/versioncontrol/tests/cache.py trac/versioncontrol/tests/diff.py trac/versioncontrol/tests/functional.py trac/versioncontrol/tests/svn_authz.py trac/versioncontrol/tests/svn_fs.py trac/versioncontrol/tests/svnrepos.dump trac/versioncontrol/web_ui/__init__.py trac/versioncontrol/web_ui/browser.py trac/versioncontrol/web_ui/changeset.py trac/versioncontrol/web_ui/log.py trac/versioncontrol/web_ui/main.py trac/versioncontrol/web_ui/util.py trac/versioncontrol/web_ui/tests/__init__.py trac/versioncontrol/web_ui/tests/wikisyntax.py trac/web/__init__.py trac/web/_fcgi.py trac/web/api.py trac/web/auth.py trac/web/cgi_frontend.py trac/web/chrome.py trac/web/clearsilver.py trac/web/fcgi_frontend.py trac/web/href.py trac/web/main.py trac/web/modpython_frontend.py trac/web/session.py trac/web/standalone.py trac/web/wsgi.py trac/web/tests/__init__.py trac/web/tests/api.py trac/web/tests/auth.py trac/web/tests/cgi_frontend.py trac/web/tests/chrome.py trac/web/tests/clearsilver.py trac/web/tests/href.py trac/web/tests/session.py trac/web/tests/wikisyntax.py trac/wiki/__init__.py trac/wiki/api.py trac/wiki/formatter.py trac/wiki/intertrac.py trac/wiki/interwiki.py trac/wiki/macros.py trac/wiki/model.py trac/wiki/parser.py trac/wiki/web_ui.py trac/wiki/default-pages/CamelCase trac/wiki/default-pages/InterMapTxt trac/wiki/default-pages/InterTrac trac/wiki/default-pages/InterWiki trac/wiki/default-pages/PageTemplates trac/wiki/default-pages/RecentChanges trac/wiki/default-pages/SandBox trac/wiki/default-pages/TitleIndex trac/wiki/default-pages/TracAccessibility trac/wiki/default-pages/TracAdmin trac/wiki/default-pages/TracBackup trac/wiki/default-pages/TracBrowser trac/wiki/default-pages/TracCgi trac/wiki/default-pages/TracChangeset trac/wiki/default-pages/TracEnvironment trac/wiki/default-pages/TracFastCgi trac/wiki/default-pages/TracFineGrainedPermissions trac/wiki/default-pages/TracGuide trac/wiki/default-pages/TracImport trac/wiki/default-pages/TracIni trac/wiki/default-pages/TracInstall trac/wiki/default-pages/TracInterfaceCustomization trac/wiki/default-pages/TracLinks trac/wiki/default-pages/TracLogging trac/wiki/default-pages/TracModPython trac/wiki/default-pages/TracModWSGI trac/wiki/default-pages/TracNavigation trac/wiki/default-pages/TracNotification trac/wiki/default-pages/TracPermissions trac/wiki/default-pages/TracPlugins trac/wiki/default-pages/TracQuery trac/wiki/default-pages/TracReports trac/wiki/default-pages/TracRevisionLog trac/wiki/default-pages/TracRoadmap trac/wiki/default-pages/TracRss trac/wiki/default-pages/TracSearch trac/wiki/default-pages/TracStandalone trac/wiki/default-pages/TracSupport trac/wiki/default-pages/TracSyntaxColoring trac/wiki/default-pages/TracTickets trac/wiki/default-pages/TracTicketsCustomFields trac/wiki/default-pages/TracTimeline trac/wiki/default-pages/TracUnicode trac/wiki/default-pages/TracUpgrade trac/wiki/default-pages/TracWiki trac/wiki/default-pages/TracWorkflow trac/wiki/default-pages/WikiDeletePage trac/wiki/default-pages/WikiFormatting trac/wiki/default-pages/WikiHtml trac/wiki/default-pages/WikiMacros trac/wiki/default-pages/WikiNewPage trac/wiki/default-pages/WikiPageNames trac/wiki/default-pages/WikiProcessors trac/wiki/default-pages/WikiRestructuredText trac/wiki/default-pages/WikiRestructuredTextLinks trac/wiki/default-pages/WikiStart trac/wiki/templates/wiki_delete.html trac/wiki/templates/wiki_diff.html trac/wiki/templates/wiki_edit.html trac/wiki/templates/wiki_view.html trac/wiki/tests/__init__.py trac/wiki/tests/formatter.py trac/wiki/tests/functional.py trac/wiki/tests/macros.py trac/wiki/tests/model.py trac/wiki/tests/wiki-tests.txt trac/wiki/tests/wikisyntax.py wiki-macros/READMEPKmh9hVEGG-INFO/top_level.txttrac PKkh9Oe/<<trac/__init__.py# -*- coding: utf-8 -*- # # Copyright (C) 2003-2008 Edgewall Software # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. __version__ = __import__('pkg_resources').get_distribution('Trac').version PKmh9>(>trac/__init__.pyc; Ic@sedidiZdS(s pkg_resourcessTracN(s __import__sget_distributionsversions __version__(s __version__((s2build/bdist.darwin-9.5.0-i386/egg/trac/__init__.pys?sPKkh9!O trac/about.py# -*- coding: utf-8 -*- # # Copyright (C) 2004-2008 Edgewall Software # Copyright (C) 2004-2005 Jonas Borgström # Copyright (C) 2004-2005 Daniel Lundin # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Jonas Borgström # Christopher Lenz import re from genshi.builder import tag from trac.core import * from trac.perm import IPermissionRequestor from trac.util.translation import _ from trac.web import IRequestHandler from trac.web.chrome import INavigationContributor class AboutModule(Component): """Provides various about pages.""" implements(INavigationContributor, IPermissionRequestor, IRequestHandler) # INavigationContributor methods def get_active_navigation_item(self, req): return 'about' def get_navigation_items(self, req): yield ('metanav', 'about', tag.a(_('About Trac'), href=req.href.about())) # IPermissionRequestor methods def get_permission_actions(self): return ['CONFIG_VIEW'] # IRequestHandler methods def match_request(self, req): return re.match(r'/about(?:_trac)?(?:/.+)?$', req.path_info) def process_request(self, req): data = {} if 'CONFIG_VIEW' in req.perm('config', 'systeminfo'): # Collect system information data['systeminfo'] = self.env.systeminfo if 'CONFIG_VIEW' in req.perm('config', 'ini'): # Collect config information sections = [] for section in self.config.sections(): options = [] default_options = self.config.defaults().get(section) for name,value in self.config.options(section): default = default_options and default_options.get(name) or '' options.append({ 'name': name, 'value': value, 'modified': unicode(value) != unicode(default) }) options.sort(lambda x,y: cmp(x['name'], y['name'])) sections.append({'name': section, 'options': options}) sections.sort(lambda x,y: cmp(x['name'], y['name'])) data['config'] = sections return 'about.html', data, None PKmh9Ɗ د trac/about.pyc; Ic@skdkZdklZdkTdklZdklZdkl Z dk l Z de fdYZ dS( N(stag(s*(sIPermissionRequestor(s_(sIRequestHandler(sINavigationContributors AboutModulecBsKtZdZeeeedZdZdZ dZ dZ RS(sProvides various about pages.cCsdSdS(Nsabout((sselfsreq((s/build/bdist.darwin-9.5.0-i386/egg/trac/about.pysget_active_navigation_item&sccs/ddtitdd|iifVdS(Nsmetanavsabouts About Tracshref(stagsas_sreqshrefsabout(sselfsreq((s/build/bdist.darwin-9.5.0-i386/egg/trac/about.pysget_navigation_items)scCs dgSdS(Ns CONFIG_VIEW((sself((s/build/bdist.darwin-9.5.0-i386/egg/trac/about.pysget_permission_actions/scCstid|iSdS(Ns/about(?:_trac)?(?:/.+)?$(sresmatchsreqs path_info(sselfsreq((s/build/bdist.darwin-9.5.0-i386/egg/trac/about.pys match_request4sc Csbh}d|iddjo|ii|dJssoptionss about.html(sdatasreqspermsselfsenvs systeminfossectionssconfigssectionsoptionssdefaultssgetsdefault_optionssnamesvaluesdefaultsappendsunicodessortsNone( sselfsreqsnamesdefaultsdatasvaluesoptionssdefault_optionsssectionsssection((s/build/bdist.darwin-9.5.0-i386/egg/trac/about.pysprocess_request7s$ >#( s__name__s __module__s__doc__s implementssINavigationContributorsIPermissionRequestorsIRequestHandlersget_active_navigation_itemsget_navigation_itemssget_permission_actionss match_requestsprocess_request(((s/build/bdist.darwin-9.5.0-i386/egg/trac/about.pys AboutModules     (sresgenshi.builderstags trac.cores trac.permsIPermissionRequestorstrac.util.translations_strac.websIRequestHandlerstrac.web.chromesINavigationContributors Components AboutModule(sINavigationContributorsIRequestHandlersIPermissionRequestorsrestags AboutModules_((s/build/bdist.darwin-9.5.0-i386/egg/trac/about.pys?s      PKkh9}}trac/attachment.py# -*- coding: utf-8 -*- # # Copyright (C) 2003-2008 Edgewall Software # Copyright (C) 2003-2005 Jonas Borgström # Copyright (C) 2005 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Jonas Borgström # Christopher Lenz from datetime import datetime import os import re import shutil import time import unicodedata from genshi.builder import tag from trac import perm, util from trac.config import BoolOption, IntOption from trac.core import * from trac.env import IEnvironmentSetupParticipant from trac.mimeview import * from trac.perm import PermissionError, PermissionSystem, IPermissionPolicy from trac.resource import * from trac.search import search_to_sql, shorten_result from trac.util import get_reporter_id, create_unique_file, content_disposition from trac.util.datefmt import to_timestamp, utc from trac.util.text import unicode_quote, unicode_unquote, pretty_size from trac.util.translation import _ from trac.web import HTTPBadRequest, IRequestHandler from trac.web.chrome import add_link, add_stylesheet, add_ctxtnav, \ INavigationContributor from trac.web.href import Href from trac.wiki.api import IWikiSyntaxProvider from trac.wiki.formatter import format_to class InvalidAttachment(TracError): """Exception raised when attachment validation fails.""" class IAttachmentChangeListener(Interface): """Extension point interface for components that require notification when attachments are created or deleted.""" def attachment_added(attachment): """Called when an attachment is added.""" def attachment_deleted(attachment): """Called when an attachment is deleted.""" class IAttachmentManipulator(Interface): """Extension point interface for components that need to manipulate attachments. Unlike change listeners, a manipulator can reject changes being committed to the database.""" def prepare_attachment(req, attachment, fields): """Not currently called, but should be provided for future compatibility.""" def validate_attachment(req, attachment): """Validate an attachment after upload but before being stored in Trac environment. Must return a list of `(field, message)` tuples, one for each problem detected. `field` can be any of `description`, `username`, `filename`, `content`, or `None` to indicate an overall problem with the attachment. Therefore, a return value of `[]` means everything is OK.""" class ILegacyAttachmentPolicyDelegate(Interface): """Interface that can be used by plugins to seemlessly participate to the legacy way of checking for attachment permissions. This should no longer be necessary once it becomes easier to setup fine-grained permissions in the default permission store. """ def check_attachment_permission(action, username, resource, perm): """Return the usual True/False/None security policy decision appropriate for the requested action on an attachment. :param action: one of ATTACHMENT_VIEW, ATTACHMENT_CREATE, ATTACHMENT_DELETE :param username: the user string :param resource: the `Resource` for the attachment. Note that when ATTACHMENT_CREATE is checked, the resource `.id` will be `None`. :param perm: the permission cache for that username and resource """ class Attachment(object): def __init__(self, env, parent_realm_or_attachment_resource, parent_id=None, filename=None, db=None): if isinstance(parent_realm_or_attachment_resource, Resource): self.resource = parent_realm_or_attachment_resource else: self.resource = Resource(parent_realm_or_attachment_resource, parent_id).child('attachment', filename) self.env = env self.parent_realm = self.resource.parent.realm self.parent_id = unicode(self.resource.parent.id) if self.resource.id: self._fetch(self.resource.id, db) else: self.filename = None self.description = None self.size = None self.date = None self.author = None self.ipnr = None def _set_filename(self, val): self.resource.id = val filename = property(lambda self: self.resource.id, _set_filename) def _fetch(self, filename, db=None): if not db: db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT filename,description,size,time,author,ipnr " "FROM attachment WHERE type=%s AND id=%s " "AND filename=%s ORDER BY time", (self.parent_realm, unicode(self.parent_id), filename)) row = cursor.fetchone() cursor.close() if not row: self.filename = filename raise ResourceNotFound(_("Attachment '%(title)s' does not exist.", title=self.title), _('Invalid Attachment')) self.filename = row[0] self.description = row[1] self.size = row[2] and int(row[2]) or 0 time = row[3] and int(row[3]) or 0 self.date = datetime.fromtimestamp(time, utc) self.author = row[4] self.ipnr = row[5] def _get_path(self): path = os.path.join(self.env.path, 'attachments', self.parent_realm, unicode_quote(self.parent_id)) if self.filename: path = os.path.join(path, unicode_quote(self.filename)) return os.path.normpath(path) path = property(_get_path) def _get_title(self): return '%s:%s: %s' % (self.parent_realm, self.parent_id, self.filename) title = property(_get_title) def delete(self, db=None): assert self.filename, 'Cannot delete non-existent attachment' if not db: db = self.env.get_db_cnx() handle_ta = True else: handle_ta = False cursor = db.cursor() cursor.execute("DELETE FROM attachment WHERE type=%s AND id=%s " "AND filename=%s", (self.parent_realm, self.parent_id, self.filename)) if os.path.isfile(self.path): try: os.unlink(self.path) except OSError: self.env.log.error('Failed to delete attachment file %s', self.path, exc_info=True) if handle_ta: db.rollback() raise TracError(_('Could not delete attachment')) self.env.log.info('Attachment removed: %s' % self.title) if handle_ta: db.commit() for listener in AttachmentModule(self.env).change_listeners: listener.attachment_deleted(self) def insert(self, filename, fileobj, size, t=None, db=None): # FIXME: `t` should probably be switched to `datetime` too if not db: db = self.env.get_db_cnx() handle_ta = True else: handle_ta = False self.size = size and int(size) or 0 timestamp = int(t or time.time()) self.date = datetime.fromtimestamp(timestamp, utc) # Make sure the path to the attachment is inside the environment # attachments directory attachments_dir = os.path.join(os.path.normpath(self.env.path), 'attachments') commonprefix = os.path.commonprefix([attachments_dir, self.path]) assert commonprefix == attachments_dir if not os.access(self.path, os.F_OK): os.makedirs(self.path) filename = unicode_quote(filename) path, targetfile = create_unique_file(os.path.join(self.path, filename)) try: # Note: `path` is an unicode string because `self.path` was one. # As it contains only quoted chars and numbers, we can use `ascii` basename = os.path.basename(path).encode('ascii') filename = unicode_unquote(basename) cursor = db.cursor() cursor.execute("INSERT INTO attachment " "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)", (self.parent_realm, self.parent_id, filename, self.size, timestamp, self.description, self.author, self.ipnr)) shutil.copyfileobj(fileobj, targetfile) self.resource.id = self.filename = filename self.env.log.info('New attachment: %s by %s', self.title, self.author) if handle_ta: db.commit() for listener in AttachmentModule(self.env).change_listeners: listener.attachment_added(self) finally: targetfile.close() def select(cls, env, parent_realm, parent_id, db=None): if not db: db = env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT filename,description,size,time,author,ipnr " "FROM attachment WHERE type=%s AND id=%s ORDER BY time", (parent_realm, unicode(parent_id))) for filename,description,size,time,author,ipnr in cursor: attachment = Attachment(env, parent_realm, parent_id) attachment.filename = filename attachment.description = description attachment.size = size and int(size) or 0 time = time and int(time) or 0 attachment.date = datetime.fromtimestamp(time, utc) attachment.author = author attachment.ipnr = ipnr yield attachment def delete_all(cls, env, parent_realm, parent_id, db): """Delete all attachments of a given resource. As this is usually done while deleting the parent resource, the `db` argument is ''not'' optional here. """ attachment_dir = None for attachment in list(cls.select(env, parent_realm, parent_id, db)): attachment_dir = os.path.dirname(attachment.path) attachment.delete(db) if attachment_dir: try: os.rmdir(attachment_dir) except OSError: env.log.error("Can't delete attachment directory %s", attachment_dir, exc_info=True) select = classmethod(select) delete_all = classmethod(delete_all) def open(self): self.env.log.debug('Trying to open attachment at %s', self.path) try: fd = open(self.path, 'rb') except IOError: raise ResourceNotFound(_("Attachment '%(filename)s' not found", filename=self.filename)) return fd class AttachmentModule(Component): implements(IEnvironmentSetupParticipant, IRequestHandler, INavigationContributor, IWikiSyntaxProvider, IResourceManager) change_listeners = ExtensionPoint(IAttachmentChangeListener) manipulators = ExtensionPoint(IAttachmentManipulator) CHUNK_SIZE = 4096 max_size = IntOption('attachment', 'max_size', 262144, """Maximum allowed file size (in bytes) for ticket and wiki attachments.""") render_unsafe_content = BoolOption('attachment', 'render_unsafe_content', 'false', """Whether attachments should be rendered in the browser, or only made downloadable. Pretty much any file may be interpreted as HTML by the browser, which allows a malicious user to attach a file containing cross-site scripting attacks. For public sites where anonymous users can create attachments it is recommended to leave this option disabled (which is the default).""") # IEnvironmentSetupParticipant methods def environment_created(self): """Create the attachments directory.""" if self.env.path: os.mkdir(os.path.join(self.env.path, 'attachments')) def environment_needs_upgrade(self, db): return False def upgrade_environment(self, db): pass # INavigationContributor methods def get_active_navigation_item(self, req): return req.args.get('realm') def get_navigation_items(self, req): return [] # IRequestHandler methods def match_request(self, req): match = re.match(r'/(raw-)?attachment/([^/]+)(?:/(.*))?$', req.path_info) if match: raw, realm, path = match.groups() if raw: req.args['format'] = 'raw' req.args['realm'] = realm if path: req.args['path'] = path return True def process_request(self, req): parent_id = None parent_realm = req.args.get('realm') path = req.args.get('path') filename = None if not parent_realm or not path: raise HTTPBadRequest(_('Bad request')) parent_realm = Resource(parent_realm) action = req.args.get('action', 'view') if action == 'new': parent_id = path.rstrip('/') else: segments = path.split('/') parent_id = '/'.join(segments[:-1]) filename = len(segments) > 1 and segments[-1] parent = parent_realm(id=parent_id) # Link the attachment page to parent resource parent_name = get_resource_name(self.env, parent) parent_url = get_resource_url(self.env, parent, req.href) add_link(req, 'up', parent_url, parent_name) add_ctxtnav(req, _('Back to %(parent)s', parent=parent_name), parent_url) if action != 'new' and not filename: # there's a trailing '/', show the list return self._render_list(req, parent) attachment = Attachment(self.env, parent.child('attachment', filename)) if req.method == 'POST': if action == 'new': self._do_save(req, attachment) elif action == 'delete': self._do_delete(req, attachment) elif action == 'delete': data = self._render_confirm_delete(req, attachment) elif action == 'new': data = self._render_form(req, attachment) else: data = self._render_view(req, attachment) add_stylesheet(req, 'common/css/code.css') return 'attachment.html', data, None # IWikiSyntaxProvider methods def get_wiki_syntax(self): return [] def get_link_resolvers(self): yield ('raw-attachment', self._format_link) yield ('attachment', self._format_link) # Public methods def attachment_data(self, context): """Return the list of viewable attachments. :param context: the rendering context corresponding to the parent `Resource` of the attachments """ parent = context.resource attachments = [] for attachment in Attachment.select(self.env, parent.realm, parent.id): if 'ATTACHMENT_VIEW' in context.perm(attachment.resource): attachments.append(attachment) new_att = parent.child('attachment') return {'attach_href': get_resource_url(self.env, new_att, context.href, action='new'), 'can_create': 'ATTACHMENT_CREATE' in context.perm(new_att), 'attachments': attachments, 'parent': context.resource} def get_history(self, start, stop, realm): """Return an iterable of tuples describing changes to attachments on a particular object realm. The tuples are in the form (change, realm, id, filename, time, description, author). `change` can currently only be `created`. """ # Traverse attachment directory db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT type, id, filename, time, description, author " " FROM attachment " " WHERE time > %s AND time < %s " " AND type = %s", (to_timestamp(start), to_timestamp(stop), realm)) for realm, id, filename, ts, description, author in cursor: time = datetime.fromtimestamp(ts, utc) yield ('created', realm, id, filename, time, description, author) def get_timeline_events(self, req, resource_realm, start, stop): """Return an event generator suitable for ITimelineEventProvider. Events are changes to attachments on resources of the given `resource_realm.realm`. """ for change, realm, id, filename, time, descr, author in \ self.get_history(start, stop, resource_realm.realm): attachment = resource_realm(id=id).child('attachment', filename) if 'ATTACHMENT_VIEW' in req.perm(attachment): yield ('attachment', time, author, (attachment, descr), self) def render_timeline_event(self, context, field, event): attachment, descr = event[3] if field == 'url': return self.get_resource_url(attachment, context.href) elif field == 'title': name = get_resource_name(self.env, attachment.parent) title = get_resource_summary(self.env, attachment.parent) return tag(tag.em(os.path.basename(attachment.id)), _(" attached to "), tag.em(name, title=title)) elif field == 'description': return format_to(self.env, None, context(attachment.parent), descr) def get_search_results(self, req, resource_realm, terms): """Return a search result generator suitable for ISearchSource. Search results are attachments on resources of the given `resource_realm.realm` whose filename, description or author match the given terms. """ db = self.env.get_db_cnx() sql_query, args = search_to_sql(db, ['filename', 'description', 'author'], terms) cursor = db.cursor() cursor.execute("SELECT id,time,filename,description,author " "FROM attachment " "WHERE type = %s " "AND " + sql_query, (resource_realm.realm, ) + args) for id, time, filename, desc, author in cursor: attachment = resource_realm(id=id).child('attachment', filename) if 'ATTACHMENT_VIEW' in req.perm(attachment): yield (get_resource_url(self.env, attachment, req.href), get_resource_shortname(self.env, attachment), datetime.fromtimestamp(time, utc), author, shorten_result(desc, terms)) # IResourceManager methods def get_resource_realms(self): yield 'attachment' def get_resource_url(self, resource, href, **kwargs): """Return an URL to the attachment itself. A `format` keyword argument equal to `'raw'` will be converted to the raw-attachment prefix. """ format = kwargs.get('format') prefix = 'attachment' if format == 'raw': kwargs.pop('format') prefix = 'raw-attachment' parent_href = unicode_unquote(get_resource_url(self.env, resource.parent(version=None), Href(''))) if not resource.id: # link to list of attachments, which must end with a trailing '/' # (see process_request) return href(prefix, parent_href) + '/' else: return href(prefix, parent_href, resource.id, **kwargs) def get_resource_description(self, resource, format=None, **kwargs): if format == 'compact': return '%s (%s)' % (resource.id, get_resource_name(self.env, resource.parent)) elif format == 'summary': return Attachment(self.env, resource).description if resource.id: return _("Attachment '%(id)s' in %(parent)s", id=resource.id, parent=get_resource_name(self.env, resource.parent)) else: return _("Attachments of %(parent)s", parent=get_resource_name(self.env, resource.parent)) # Internal methods def _do_save(self, req, attachment): req.perm(attachment.resource).require('ATTACHMENT_CREATE') if 'cancel' in req.args: req.redirect(get_resource_url(self.env, attachment.resource.parent, req.href)) upload = req.args['attachment'] if not hasattr(upload, 'filename') or not upload.filename: raise TracError(_('No file uploaded')) if hasattr(upload.file, 'fileno'): size = os.fstat(upload.file.fileno())[6] else: upload.file.seek(0, 2) # seek to end of file size = upload.file.tell() upload.file.seek(0) if size == 0: raise TracError(_("Can't upload empty file")) # Maximum attachment size (in bytes) max_size = self.max_size if max_size >= 0 and size > max_size: raise TracError(_('Maximum attachment size: %(num)s bytes', num=max_size), _('Upload failed')) # We try to normalize the filename to unicode NFC if we can. # Files uploaded from OS X might be in NFD. filename = unicodedata.normalize('NFC', unicode(upload.filename, 'utf-8')) filename = filename.replace('\\', '/').replace(':', '/') filename = os.path.basename(filename) if not filename: raise TracError(_('No file uploaded')) # Now the filename is known, update the attachment resource # attachment.filename = filename attachment.description = req.args.get('description', '') attachment.author = get_reporter_id(req, 'author') attachment.ipnr = req.remote_addr # Validate attachment for manipulator in self.manipulators: for field, message in manipulator.validate_attachment(req, attachment): if field: raise InvalidAttachment(_('Attachment field %(field)s is ' 'invalid: %(message)s', field=field, message=message)) else: raise InvalidAttachment(_('Invalid attachment: %(message)s', message=message)) if req.args.get('replace'): try: old_attachment = Attachment(self.env, attachment.resource(id=filename)) if not (old_attachment.author and req.authname \ and old_attachment.author == req.authname): req.perm(attachment.resource).require('ATTACHMENT_DELETE') old_attachment.delete() except TracError: pass # don't worry if there's nothing to replace attachment.filename = None attachment.insert(filename, upload.file, size) req.redirect(get_resource_url(self.env, attachment.resource(id=None), req.href)) def _do_delete(self, req, attachment): req.perm(attachment.resource).require('ATTACHMENT_DELETE') parent_href = get_resource_url(self.env, attachment.resource.parent, req.href) if 'cancel' in req.args: req.redirect(parent_href) attachment.delete() req.redirect(parent_href) def _render_confirm_delete(self, req, attachment): req.perm(attachment.resource).require('ATTACHMENT_DELETE') return {'mode': 'delete', 'title': _('%(attachment)s (delete)', attachment=get_resource_name(self.env, attachment.resource)), 'attachment': attachment} def _render_form(self, req, attachment): req.perm(attachment.resource).require('ATTACHMENT_CREATE') return {'mode': 'new', 'author': get_reporter_id(req), 'attachment': attachment, 'max_size': self.max_size} def _render_list(self, req, parent): attachment = parent.child('attachment') data = { 'mode': 'list', 'attachment': None, # no specific attachment 'attachments': self.attachment_data(Context.from_request(req, parent)) } return 'attachment.html', data, None def _render_view(self, req, attachment): req.perm(attachment.resource).require('ATTACHMENT_VIEW') can_delete = 'ATTACHMENT_DELETE' in req.perm(attachment.resource) req.check_modified(attachment.date, str(can_delete)) data = {'mode': 'view', 'title': get_resource_name(self.env, attachment.resource), 'attachment': attachment} fd = attachment.open() try: mimeview = Mimeview(self.env) # MIME type detection str_data = fd.read(1000) fd.seek(0) mime_type = mimeview.get_mimetype(attachment.filename, str_data) # Eventually send the file directly format = req.args.get('format') if format in ('raw', 'txt'): if not self.render_unsafe_content: # Force browser to download files instead of rendering # them, since they might contain malicious code enabling # XSS attacks req.send_header('Content-Disposition', 'attachment') if format == 'txt': mime_type = 'text/plain' elif not mime_type: mime_type = 'application/octet-stream' if 'charset=' not in mime_type: charset = mimeview.get_charset(str_data, mime_type) mime_type = mime_type + '; charset=' + charset req.send_file(attachment.path, mime_type) # add ''Plain Text'' alternate link if needed if (self.render_unsafe_content and mime_type and not mime_type.startswith('text/plain')): plaintext_href = get_resource_url(self.env, attachment.resource, req.href, format='txt') add_link(req, 'alternate', plaintext_href, _('Plain Text'), mime_type) # add ''Original Format'' alternate link (always) raw_href = get_resource_url(self.env, attachment.resource, req.href, format='raw') add_link(req, 'alternate', raw_href, _('Original Format'), mime_type) self.log.debug("Rendering preview of file %s with mime-type %s" % (attachment.filename, mime_type)) data['preview'] = mimeview.preview_data( Context.from_request(req, attachment.resource), fd, os.fstat(fd.fileno()).st_size, mime_type, attachment.filename, raw_href, annotations=['lineno']) return data finally: fd.close() def _format_link(self, formatter, ns, target, label): link, params, fragment = formatter.split_link(target) ids = link.split(':', 2) attachment = None if len(ids) == 3: known_realms = ResourceSystem(self.env).get_known_realms() # new-style attachment: TracLinks (filename:realm:id) if ids[1] in known_realms: attachment = Resource(ids[1], ids[2]).child('attachment', ids[0]) else: # try old-style attachment: TracLinks (realm:id:filename) if ids[0] in known_realms: attachment = Resource(ids[0], ids[1]).child('attachment', ids[2]) else: # local attachment: TracLinks (filename) attachment = formatter.resource.child('attachment', link) if attachment and 'ATTACHMENT_VIEW' in formatter.perm(attachment): try: model = Attachment(self.env, attachment) format = None if ns.startswith('raw'): format = 'raw' href = get_resource_url(self.env, attachment, formatter.href, format=format) return tag.a(label, class_='attachment', href=href + params, title=get_resource_name(self.env, attachment)) except ResourceNotFound, e: pass # FIXME: should be either: # # model = Attachment(self.env, attachment) # if model.exists: # ... # # or directly: # # if attachment.exists: # # (related to #4130) return tag.a(label, class_='missing attachment') class LegacyAttachmentPolicy(Component): implements(IPermissionPolicy) delegates = ExtensionPoint(ILegacyAttachmentPolicyDelegate) # IPermissionPolicy methods _perm_maps = { 'ATTACHMENT_CREATE': {'ticket': 'TICKET_APPEND', 'wiki': 'WIKI_MODIFY', 'milestone': 'MILESTONE_MODIFY'}, 'ATTACHMENT_VIEW': {'ticket': 'TICKET_VIEW', 'wiki': 'WIKI_VIEW', 'milestone': 'MILESTONE_VIEW'}, 'ATTACHMENT_DELETE': {'ticket': 'TICKET_ADMIN', 'wiki': 'WIKI_DELETE', 'milestone': 'MILESTONE_DELETE'}, } def check_permission(self, action, username, resource, perm): perm_map = self._perm_maps.get(action) if not perm_map or not resource or resource.realm != 'attachment': return legacy_action = perm_map.get(resource.parent.realm) if legacy_action: decision = legacy_action in perm if not decision: self.env.log.debug('LegacyAttachmentPolicy denied %s ' 'access to %s. User needs %s' % (username, resource, legacy_action)) return decision else: for d in self.delegates: decision = d.check_attachment_permission(action, username, resource, perm) if decision is not None: return decision PKmh9trac/attachment.pyc; Ic@sdklZdkZdkZdkZdkZdkZdklZdkl Z l Z dk l Z l Z dkTdklZdkTdklZlZlZdkTdklZlZd klZlZlZd klZl Z d k!l"Z"l#Z#l$Z$d k%l&Z&d k'l(Z(l)Z)dk*l+Z+l,Z,l-Z-l.Z.dk/l0Z0dk1l2Z2dk3l4Z4de5fdYZ6de7fdYZ8de7fdYZ9de7fdYZ:de;fdYZ<de=fdYZ>de=fdYZ?dS( (sdatetimeN(stag(spermsutil(s BoolOptions IntOption(s*(sIEnvironmentSetupParticipant(sPermissionErrorsPermissionSystemsIPermissionPolicy(s search_to_sqlsshorten_result(sget_reporter_idscreate_unique_filescontent_disposition(s to_timestampsutc(s unicode_quotesunicode_unquotes pretty_size(s_(sHTTPBadRequestsIRequestHandler(sadd_linksadd_stylesheets add_ctxtnavsINavigationContributor(sHref(sIWikiSyntaxProvider(s format_tosInvalidAttachmentcBstZdZRS(s2Exception raised when attachment validation fails.(s__name__s __module__s__doc__(((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysInvalidAttachment0s sIAttachmentChangeListenercBs tZdZdZdZRS(soExtension point interface for components that require notification when attachments are created or deleted.cCsdS(s#Called when an attachment is added.N((s attachment((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysattachment_added8scCsdS(s%Called when an attachment is deleted.N((s attachment((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysattachment_deleted;s(s__name__s __module__s__doc__sattachment_addedsattachment_deleted(((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysIAttachmentChangeListener4s  sIAttachmentManipulatorcBs tZdZdZdZRS(sExtension point interface for components that need to manipulate attachments. Unlike change listeners, a manipulator can reject changes being committed to the database.cCsdS(sNNot currently called, but should be provided for future compatibility.N((sreqs attachmentsfields((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysprepare_attachmentFscCsdS(sValidate an attachment after upload but before being stored in Trac environment. Must return a list of `(field, message)` tuples, one for each problem detected. `field` can be any of `description`, `username`, `filename`, `content`, or `None` to indicate an overall problem with the attachment. Therefore, a return value of `[]` means everything is OK.N((sreqs attachment((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysvalidate_attachmentJs(s__name__s __module__s__doc__sprepare_attachmentsvalidate_attachment(((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysIAttachmentManipulator?s  sILegacyAttachmentPolicyDelegatecBstZdZdZRS(sInterface that can be used by plugins to seemlessly participate to the legacy way of checking for attachment permissions. This should no longer be necessary once it becomes easier to setup fine-grained permissions in the default permission store. cCsdS(sHReturn the usual True/False/None security policy decision appropriate for the requested action on an attachment. :param action: one of ATTACHMENT_VIEW, ATTACHMENT_CREATE, ATTACHMENT_DELETE :param username: the user string :param resource: the `Resource` for the attachment. Note that when ATTACHMENT_CREATE is checked, the resource `.id` will be `None`. :param perm: the permission cache for that username and resource N((sactionsusernamesresourcesperm((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pyscheck_attachment_permission\s (s__name__s __module__s__doc__scheck_attachment_permission(((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysILegacyAttachmentPolicyDelegateTs s AttachmentcBstZeeedZdZedeZedZdZeeZ dZ ee Z edZ eedZ ed Zd ZeeZeeZd ZRS( NcCst|to ||_nt||id||_||_|ii i |_ t |ii i |_|ii o|i|ii |n7t|_t|_t|_t|_t|_t|_dS(Ns attachment(s isinstances#parent_realm_or_attachment_resourcesResourcesselfsresources parent_idschildsfilenamesenvsparentsrealms parent_realmsunicodesids_fetchsdbsNones descriptionssizesdatesauthorsipnr(sselfsenvs#parent_realm_or_attachment_resources parent_idsfilenamesdb((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pys__init__ls        cCs||i_dS(N(svalsselfsresourcesid(sselfsval((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pys _set_filenamescCs |iiS(N(sselfsresourcesid(sself((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysscCs/| o|ii}n|i}|id|it|i|f|i }|i | o1||_ t tdd|itdn|d|_ |d|_|dot|dpd|_|dot|dpd}ti|t|_|d |_|d |_dS( NswSELECT filename,description,size,time,author,ipnr FROM attachment WHERE type=%s AND id=%s AND filename=%s ORDER BY times&Attachment '%(title)s' does not exist.stitlesInvalid Attachmentiiiiii(sdbsselfsenvs get_db_cnxscursorsexecutes parent_realmsunicodes parent_idsfilenamesfetchonesrowsclosesResourceNotFounds_stitles descriptionsintssizestimesdatetimes fromtimestampsutcsdatesauthorsipnr(sselfsfilenamesdbscursorstimesrow((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pys_fetchs&         %" cCsmtii|iid|it|i}|io"tii|t|i}ntii |SdS(Ns attachments( sosspathsjoinsselfsenvs parent_realms unicode_quotes parent_idsfilenamesnormpath(sselfspath((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pys _get_paths  "cCsd|i|i|ifSdS(Ns %s:%s: %s(sselfs parent_realms parent_idsfilename(sself((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pys _get_titlescCsP|ip td| o|ii}t}nt}|i }|i d|i |i |ift ii|iotyt i|iWqtj oM|iiid|idt|o|inttdqXn|iiid|i|o|inx't|iiD]}|i|q5WdS(Ns%Cannot delete non-existent attachments>DELETE FROM attachment WHERE type=%s AND id=%s AND filename=%ss#Failed to delete attachment file %ssexc_infosCould not delete attachmentsAttachment removed: %s(sselfsfilenamesAssertionErrorsdbsenvs get_db_cnxsTrues handle_tasFalsescursorsexecutes parent_realms parent_idsosspathsisfilesunlinksOSErrorslogserrorsrollbacks TracErrors_sinfostitlescommitsAttachmentModuleschange_listenersslistenersattachment_deleted(sselfsdbslistenerscursors handle_ta((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysdeletes.   c Cs*| o|ii}t} nt} |o t|pd|_t|p t i }t i |t|_tiitii|iid}tii||ig}||jptti|iti oti|int|}ttii|i|\} } ztii| id}t |}|i!} | i"d|i#|i$||i||i%|i&|i'ft(i)|| ||i+_,|_|ii-i.d|i/|i&| o|i0nx't1|ii2D]} | i4|qWWd| i5XdS(Nis attachmentssasciis7INSERT INTO attachment VALUES (%s,%s,%s,%s,%s,%s,%s,%s)sNew attachment: %s by %s(6sdbsselfsenvs get_db_cnxsTrues handle_tasFalsessizesintststimes timestampsdatetimes fromtimestampsutcsdatesosspathsjoinsnormpathsattachments_dirs commonprefixsAssertionErrorsaccesssF_OKsmakedirss unicode_quotesfilenamescreate_unique_files targetfilesbasenamesencodesunicode_unquotescursorsexecutes parent_realms parent_ids descriptionsauthorsipnrsshutils copyfileobjsfileobjsresourcesidslogsinfostitlescommitsAttachmentModuleschange_listenersslistenersattachment_addedsclose(sselfsfilenamesfileobjssizestsdbsbasenames commonprefixs timestamps targetfileslisteners handle_taspathscursorsattachments_dir((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysinsertsB      1 c cs| o|i}n|i} | id|t|fx| D]\}}} } }}t|||} || _|| _ | o t| pd| _ | o t| pd} ti| t| _|| _ || _ | VqGWdS(NsgSELECT filename,description,size,time,author,ipnr FROM attachment WHERE type=%s AND id=%s ORDER BY timei(sdbsenvs get_db_cnxscursorsexecutes parent_realmsunicodes parent_idsfilenames descriptionssizestimesauthorsipnrs Attachments attachmentsintsdatetimes fromtimestampsutcsdate( sclssenvs parent_realms parent_idsdbsipnrsauthors descriptionsfilenamescursors attachmentstimessize((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysselects       cCst}xHt|i||||D](}t i i |i }|i |q%W|oDyt i|Wqtj o |iid|dtqXndS(sDelete all attachments of a given resource. As this is usually done while deleting the parent resource, the `db` argument is ''not'' optional here. s$Can't delete attachment directory %ssexc_infoN(sNonesattachment_dirslistsclssselectsenvs parent_realms parent_idsdbs attachmentsosspathsdirnamesdeletesrmdirsOSErrorslogserrorsTrue(sclssenvs parent_realms parent_idsdbsattachment_dirs attachment((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pys delete_all s cCsh|iiid|iyt|id}Wn.tj o"tt dd|i nX|SdS(NsTrying to open attachment at %ssrbs#Attachment '%(filename)s' not foundsfilename( sselfsenvslogsdebugspathsopensfdsIOErrorsResourceNotFounds_sfilename(sselfsfd((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysopens (s__name__s __module__sNones__init__s _set_filenamespropertysfilenames_fetchs _get_pathspaths _get_titlestitlesdeletesinsertsselects delete_alls classmethodsopen(((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pys Attachmentjs       3    sAttachmentModulecBsAtZeeeeeeee Z ee Z dZ eddddZeddddZd Zd Zd Zd Zd ZdZdZdZdZdZdZdZdZdZdZ dZ!e"dZ#dZ$dZ%dZ&dZ'dZ(dZ)d Z*RS(!Nis attachmentsmax_sizeisNMaximum allowed file size (in bytes) for ticket and wiki attachments.srender_unsafe_contentsfalsesWhether attachments should be rendered in the browser, or only made downloadable. Pretty much any file may be interpreted as HTML by the browser, which allows a malicious user to attach a file containing cross-site scripting attacks. For public sites where anonymous users can create attachments it is recommended to leave this option disabled (which is the default).cCs7|iio&titii|iidndS(s!Create the attachments directory.s attachmentsN(sselfsenvspathsossmkdirsjoin(sself((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysenvironment_createdFs cCstSdS(N(sFalse(sselfsdb((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysenvironment_needs_upgradeKscCsdS(N((sselfsdb((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysupgrade_environmentNscCs|iidSdS(Nsrealm(sreqsargssget(sselfsreq((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysget_active_navigation_itemSscCsgSdS(N((sselfsreq((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysget_navigation_itemsVscCsztid|i}|oZ|i\}}}|od|id %s AND time < %s AND type = %sscreatedN(sselfsenvs get_db_cnxsdbscursorsexecutes to_timestampsstartsstopsrealmsidsfilenamestss descriptionsauthorsdatetimes fromtimestampsutcstime( sselfsstartsstopsrealms descriptionsauthorsdbstssfilenamescursorstimesid((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pys get_historys  c csx|i|||iD]i\} }}} } }}|d|i d| } d|i| jod| || |f|fVqqWdS(sReturn an event generator suitable for ITimelineEventProvider. Events are changes to attachments on resources of the given `resource_realm.realm`. sids attachmentsATTACHMENT_VIEWN(sselfs get_historysstartsstopsresource_realmsrealmschangesidsfilenamestimesdescrsauthorschilds attachmentsreqsperm( sselfsreqsresource_realmsstartsstopsrealmsdescrsauthorsids attachmentstimesfilenameschange((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysget_timeline_eventss cCs|d\}}|djo|i||iSn|djokt|i |i }t |i |i }ttitii|itdti|d|Sn1|djo#t|i t||i |SndS(Nisurlstitles attached to s description(sevents attachmentsdescrsfieldsselfsget_resource_urlscontextshrefsget_resource_namesenvsparentsnamesget_resource_summarystitlestagsemsosspathsbasenamesids_s format_tosNone(sselfscontextsfieldseventsnamesdescrstitles attachment((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysrender_timeline_events  # ccs|ii}t|dddg|\} }|i}|i d| |i f|x|D]\}} } } }|d|id| } d|i| joJt|i| |it|i| ti| t|t| |fVqaqaWdS( sReturn a search result generator suitable for ISearchSource. Search results are attachments on resources of the given `resource_realm.realm` whose filename, description or author match the given terms. sfilenames descriptionsauthorsOSELECT id,time,filename,description,author FROM attachment WHERE type = %s AND sids attachmentsATTACHMENT_VIEWN(sselfsenvs get_db_cnxsdbs search_to_sqlstermss sql_querysargsscursorsexecutesresource_realmsrealmsidstimesfilenamesdescsauthorschilds attachmentsreqspermsget_resource_urlshrefsget_resource_shortnamesdatetimes fromtimestampsutcsshorten_result(sselfsreqsresource_realmstermssargssauthorsdbsidscursors attachmentstimesfilenames sql_querysdesc((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysget_search_resultss  ccsdVdS(Ns attachment((sself((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysget_resource_realmsscKs|id}d}|djo|idd}ntt|i|i dt t d}|i o|||dSn||||i|SdS( sReturn an URL to the attachment itself. A `format` keyword argument equal to `'raw'` will be converted to the raw-attachment prefix. sformats attachmentsrawsraw-attachmentsversionss/N(skwargssgetsformatsprefixspopsunicode_unquotesget_resource_urlsselfsenvsresourcesparentsNonesHrefs parent_hrefsidshref(sselfsresourceshrefskwargssformats parent_hrefsprefix((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysget_resource_urls    ! cKs|djo$d|it|i|ifSn%|djot|i|iSn|io,t dd|idt|i|iSn t ddt|i|iSdS(Nscompacts%s (%s)ssummarys!Attachment '%(id)s' in %(parent)ssidsparentsAttachments of %(parent)s( sformatsresourcesidsget_resource_namesselfsenvsparents Attachments descriptions_(sselfsresourcesformatskwargs((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysget_resource_descriptions $  c Csg|i|iidd|ijo)|it|i |ii |i n|id} t | d p| i ottdnt | ido ti| iid}n3| iidd | ii}| iid|djottd n|i} | djo || jo%ttd d | td ntidt| id}|iddidd}tii|}| ottdn|iidd|_ t!|d|_"|i#|_$xu|i%D]j}xa|i'||D]M\}}|o"t*tdd|d|q&t*tdd|q&Wq W|iidoyqt+|i |id|}|i"o|i-o|i"|i-j o|i|iidn|i.Wntj onXt/|_n|i0|| i||it|i |idt/|i dS(NsATTACHMENT_CREATEscancels attachmentsfilenamesNo file uploadedsfilenoiiisCan't upload empty files&Maximum attachment size: %(num)s bytessnums Upload failedsNFCsutf-8s\s/s:s descriptionssauthors2Attachment field %(field)s is invalid: %(message)ssfieldsmessagesInvalid attachment: %(message)ssreplacesidsATTACHMENT_DELETE(1sreqsperms attachmentsresourcesrequiresargssredirectsget_resource_urlsselfsenvsparentshrefsuploadshasattrsfilenames TracErrors_sfilesossfstatsfilenossizesseekstellsmax_sizes unicodedatas normalizesunicodesreplacespathsbasenamesgets descriptionsget_reporter_idsauthors remote_addrsipnrs manipulatorss manipulatorsvalidate_attachmentsfieldsmessagesInvalidAttachments Attachmentsold_attachmentsauthnamesdeletesNonesinsert( sselfsreqs attachmentsmessagessizes manipulatorsfilenamesfieldsold_attachmentsuploadsmax_size((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pys_do_savesf              ( cCss|i|iidt|i|ii|i }d|i jo|i |n|i |i |dS(NsATTACHMENT_DELETEscancel(sreqsperms attachmentsresourcesrequiresget_resource_urlsselfsenvsparentshrefs parent_hrefsargssredirectsdelete(sselfsreqs attachments parent_href((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pys _do_deletebs  c CsW|i|iidhdd<dtddt|i|i<d|sLegacyAttachmentPolicycBstZeeeeZhdhdd<dd<dd<<dhdd <dd <dd <<d hdd <dd<dd<|iD]3}|i||||}|tj o|SqqWdS(Ns attachments<LegacyAttachmentPolicy denied %s access to %s. User needs %s(sselfs _perm_mapssgetsactionsperm_mapsresourcesrealmsparents legacy_actionspermsdecisionsenvslogsdebugsusernames delegatessdscheck_attachment_permissionsNone( sselfsactionsusernamesresourcespermsds legacy_actionsperm_mapsdecision((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pyscheck_permissions  $    ( s__name__s __module__s implementssIPermissionPolicysExtensionPointsILegacyAttachmentPolicyDelegates delegatess _perm_mapsscheck_permission(((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pysLegacyAttachmentPolicys  r (@sdatetimesossresshutilstimes unicodedatasgenshi.builderstagstracspermsutils trac.configs BoolOptions IntOptions trac.corestrac.envsIEnvironmentSetupParticipants trac.mimeviews trac.permsPermissionErrorsPermissionSystemsIPermissionPolicys trac.resources trac.searchs search_to_sqlsshorten_results trac.utilsget_reporter_idscreate_unique_filescontent_dispositionstrac.util.datefmts to_timestampsutcstrac.util.texts unicode_quotesunicode_unquotes pretty_sizestrac.util.translations_strac.websHTTPBadRequestsIRequestHandlerstrac.web.chromesadd_linksadd_stylesheets add_ctxtnavsINavigationContributors trac.web.hrefsHrefs trac.wiki.apisIWikiSyntaxProviderstrac.wiki.formatters format_tos TracErrorsInvalidAttachments InterfacesIAttachmentChangeListenersIAttachmentManipulatorsILegacyAttachmentPolicyDelegatesobjects Attachments ComponentsAttachmentModulesLegacyAttachmentPolicy(*sIPermissionPolicysunicode_unquotes pretty_sizes search_to_sqls IntOptionsdatetimesIWikiSyntaxProvidersPermissionErrorstagsadd_links AttachmentsAttachmentModulesHTTPBadRequestsshutils add_ctxtnavsadd_stylesheetsIRequestHandlersLegacyAttachmentPolicyspermsrescontent_dispositionsINavigationContributorsInvalidAttachments format_tosutcsshorten_resultsutilsIEnvironmentSetupParticipantsILegacyAttachmentPolicyDelegatesIAttachmentManipulators_screate_unique_files unicodedatasIAttachmentChangeListenersPermissionSystems to_timestampsHrefstimes BoolOptionsget_reporter_idsoss unicode_quote((s4build/bdist.darwin-9.5.0-i386/egg/trac/attachment.pys?s>             PKkh95ɝOEOEtrac/config.py# -*- coding: utf-8 -*- # # Copyright (C) 2005-2008 Edgewall Software # Copyright (C) 2005-2007 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. from ConfigParser import ConfigParser import os from trac.core import ExtensionPoint, TracError from trac.util.compat import set, sorted from trac.util.text import to_unicode, CRLF __all__ = ['Configuration', 'Option', 'BoolOption', 'IntOption', 'ListOption', 'PathOption', 'ExtensionOption', 'OrderedExtensionsOption', 'ConfigurationError'] _TRUE_VALUES = ('yes', 'true', 'enabled', 'on', 'aye', '1', 1, True) class ConfigurationError(TracError): """Exception raised when a value in the configuration file is not valid.""" class Configuration(object): """Thin layer over `ConfigParser` from the Python standard library. In addition to providing some convenience methods, the class remembers the last modification time of the configuration file, and reparses it when the file has changed. """ def __init__(self, filename): self.filename = filename self.parser = ConfigParser() self.parent = None self._lastmtime = 0 self._sections = {} self.parse_if_needed() def __contains__(self, name): """Return whether the configuration contains a section of the given name. """ return name in self.sections() def __getitem__(self, name): """Return the configuration section with the specified name.""" if name not in self._sections: self._sections[name] = Section(self, name) return self._sections[name] def __repr__(self): return '<%s %r>' % (self.__class__.__name__, self.filename) def get(self, section, name, default=''): """Return the value of the specified option. Valid default input is a string. Returns a string. """ return self[section].get(name, default) def getbool(self, section, name, default=''): """Return the specified option as boolean value. If the value of the option is one of "yes", "true", "enabled", "on", or "1", this method wll return `True`, otherwise `False`. Valid default input is a string or a bool. Returns a bool. (since Trac 0.9.3, "enabled" added in 0.11) """ return self[section].getbool(name, default) def getint(self, section, name, default=''): """Return the value of the specified option as integer. If the specified option can not be converted to an integer, a `ConfigurationError` exception is raised. Valid default input is a string or an int. Returns an int. (since Trac 0.10) """ return self[section].getint(name, default) def getlist(self, section, name, default='', sep=',', keep_empty=False): """Return a list of values that have been specified as a single comma-separated option. A different separator can be specified using the `sep` parameter. If the `keep_empty` parameter is set to `True`, empty elements are included in the list. Valid default input is a string or a list. Returns a string. (since Trac 0.10) """ return self[section].getlist(name, default, sep, keep_empty) def set(self, section, name, value): """Change a configuration value. These changes are not persistent unless saved with `save()`. """ self[section].set(name, value) def defaults(self): """Returns a dictionary of the default configuration values. (since Trac 0.10) """ defaults = {} for (section, name), option in Option.registry.items(): defaults.setdefault(section, {})[name] = option.default return defaults def options(self, section): """Return a list of `(name, value)` tuples for every option in the specified section. This includes options that have default values that haven't been overridden. """ return self[section].options() def remove(self, section, name): """Remove the specified option.""" if self.parser.has_section(section): self.parser.remove_option(section, name) def sections(self): """Return a list of section names.""" sections = set(self.parser.sections()) parent = self.parent while parent: sections |= set(parent.parser.sections()) parent = parent.parent return sorted(sections) def has_option(self, section, option): """Returns True if option exists in section in either project or parent trac.ini, or available through the Option registry. (since Trac 0.11) """ # Check project trac.ini for file_option, val in self.options(section): if file_option == option: return True # Check parent trac.ini if self.parent: for parent_option, val in self.parent.options(section): if parent_option == option: return True # Check the registry if (section, option) in Option.registry: return True # Not found return False def save(self): """Write the configuration options to the primary file.""" if not self.filename: return # Only save options that differ from the defaults sections = [] for section in self.sections(): options = [] for option in self[section]: default = None if self.parent: default = self.parent.get(section, option) current = self.parser.has_option(section, option) and \ to_unicode(self.parser.get(section, option)) if current is not False and current != default: options.append((option, current)) if options: sections.append((section, sorted(options))) fileobj = open(self.filename, 'w') try: fileobj.write('# -*- coding: utf-8 -*-\n\n') for section, options in sections: fileobj.write('[%s]\n' % section) for key, val in options: if key in self[section].overridden: fileobj.write('# %s = \n' % key) else: val = val.replace(CRLF, '\n').replace('\n', '\n ') fileobj.write('%s = %s\n' % (key, val.encode('utf-8'))) fileobj.write('\n') finally: fileobj.close() def parse_if_needed(self): if not self.filename or not os.path.isfile(self.filename): return False changed = False modtime = os.path.getmtime(self.filename) if modtime > self._lastmtime: self.parser._sections = {} self.parser.read(self.filename) self._lastmtime = modtime changed = True if self.parser.has_option('inherit', 'file'): filename = self.parser.get('inherit', 'file') if not os.path.isabs(filename): filename = os.path.join(os.path.dirname(self.filename), filename) if not self.parent or self.parent.filename != filename: self.parent = Configuration(filename) changed = True else: changed |= self.parent.parse_if_needed() elif self.parent: changed = True self.parent = None return changed def touch(self): if self.filename and os.path.isfile(self.filename) \ and os.access(self.filename, os.W_OK): os.utime(self.filename, None) class Section(object): """Proxy for a specific configuration section. Objects of this class should not be instantiated directly. """ __slots__ = ['config', 'name', 'overridden'] def __init__(self, config, name): self.config = config self.name = name self.overridden = {} def __contains__(self, name): if self.config.parser.has_option(self.name, name): return True if self.config.parent: return name in self.config.parent[self.name] return False def __iter__(self): options = set() if self.config.parser.has_section(self.name): for option in self.config.parser.options(self.name): options.add(option.lower()) yield option if self.config.parent: for option in self.config.parent[self.name]: if option.lower() not in options: yield option def __repr__(self): return '
' % (self.name) def get(self, name, default=''): """Return the value of the specified option. Valid default input is a string. Returns a string. """ if self.config.parser.has_option(self.name, name): value = self.config.parser.get(self.name, name) elif self.config.parent: value = self.config.parent[self.name].get(name, default) else: option = Option.registry.get((self.name, name)) if option: value = option.default or default else: value = default if not value: return u'' elif isinstance(value, basestring): return to_unicode(value) else: return value def getbool(self, name, default=''): """Return the value of the specified option as boolean. This method returns `True` if the option value is one of "yes", "true", "enabled", "on", or "1", ignoring case. Otherwise `False` is returned. Valid default input is a string or a bool. Returns a bool. """ value = self.get(name, default) if isinstance(value, basestring): value = value.lower() in _TRUE_VALUES return bool(value) def getint(self, name, default=''): """Return the value of the specified option as integer. If the specified option can not be converted to an integer, a `ConfigurationError` exception is raised. Valid default input is a string or an int. Returns an int. """ value = self.get(name, default) if not value: return 0 try: return int(value) except ValueError: raise ConfigurationError('expected integer, got %s' % repr(value)) def getlist(self, name, default='', sep=',', keep_empty=True): """Return a list of values that have been specified as a single comma-separated option. A different separator can be specified using the `sep` parameter. If the `keep_empty` parameter is set to `False`, empty elements are omitted from the list. Valid default input is a string or a list. Returns a list. """ value = self.get(name, default) if not value: return [] if isinstance(value, basestring): items = [item.strip() for item in value.split(sep)] else: items = list(value) if not keep_empty: items = filter(None, items) return items def getpath(self, name, default=''): """Return the value of the specified option as a path name, relative to the location of the configuration file the option is defined in. Valid default input is a string. Returns a string with normalised path. """ if self.config.parser.has_option(self.name, name): path = self.config.parser.get(self.name, name) if not path: return default if not os.path.isabs(path): path = os.path.join(os.path.dirname(self.config.filename), path) return os.path.normcase(os.path.realpath(path)) elif self.config.parent: return self.config.parent[self.name].getpath(name, default) else: return default def options(self): """Return `(name, value)` tuples for every option in the section.""" for name in self: yield name, self.get(name) def set(self, name, value): """Change a configuration value. These changes are not persistent unless saved with `save()`. """ if not self.config.parser.has_section(self.name): self.config.parser.add_section(self.name) if value is None: self.overridden[name] = True value = '' else: value = to_unicode(value).encode('utf-8') return self.config.parser.set(self.name, name, value) class Option(object): """Descriptor for configuration options on `Configurable` subclasses.""" registry = {} accessor = Section.get def __init__(self, section, name, default=None, doc=''): """Create the extension point. @param section: the name of the configuration section this option belongs to @param name: the name of the option @param default: the default value for the option @param doc: documentation of the option """ self.section = section self.name = name self.default = default self.registry[(self.section, self.name)] = self self.__doc__ = doc def __get__(self, instance, owner): if instance is None: return self config = getattr(instance, 'config', None) if config and isinstance(config, Configuration): section = config[self.section] value = self.accessor(section, self.name, self.default) return value return None def __set__(self, instance, value): raise AttributeError, 'can\'t set attribute' def __repr__(self): return '<%s [%s] "%s">' % (self.__class__.__name__, self.section, self.name) class BoolOption(Option): """Descriptor for boolean configuration options.""" accessor = Section.getbool class IntOption(Option): """Descriptor for integer configuration options.""" accessor = Section.getint class ListOption(Option): """Descriptor for configuration options that contain multiple values separated by a specific character.""" def __init__(self, section, name, default=None, sep=',', keep_empty=False, doc=''): Option.__init__(self, section, name, default, doc) self.sep = sep self.keep_empty = keep_empty def accessor(self, section, name, default): return section.getlist(name, default, self.sep, self.keep_empty) class PathOption(Option): """Descriptor for file system path configuration options.""" accessor = Section.getpath class ExtensionOption(Option): def __init__(self, section, name, interface, default=None, doc=''): Option.__init__(self, section, name, default, doc) self.xtnpt = ExtensionPoint(interface) def __get__(self, instance, owner): if instance is None: return self value = Option.__get__(self, instance, owner) for impl in self.xtnpt.extensions(instance): if impl.__class__.__name__ == value: return impl raise AttributeError('Cannot find an implementation of the "%s" ' 'interface named "%s". Please update the option ' '%s.%s in trac.ini.' % (self.xtnpt.interface.__name__, value, self.section, self.name)) class OrderedExtensionsOption(ListOption): """A comma separated, ordered, list of components implementing `interface`. Can be empty. If `include_missing` is true (the default) all components implementing the interface are returned, with those specified by the option ordered first.""" def __init__(self, section, name, interface, default=None, include_missing=True, doc=''): ListOption.__init__(self, section, name, default, doc=doc) self.xtnpt = ExtensionPoint(interface) self.include_missing = include_missing def __get__(self, instance, owner): if instance is None: return self order = ListOption.__get__(self, instance, owner) components = [] for impl in self.xtnpt.extensions(instance): if self.include_missing or impl.__class__.__name__ in order: components.append(impl) def compare(x, y): x, y = x.__class__.__name__, y.__class__.__name__ if x not in order: return int(y in order) if y not in order: return -int(x in order) return cmp(order.index(x), order.index(y)) components.sort(compare) return components PKmh9=``trac/config.pyc; Ic @sndklZdkZdklZlZdklZlZdkl Z l Z ddddd d d d d g Z ddddddde fZ d efdYZdefdYZdefdYZdefdYZdefdYZdefdYZd efdYZd efdYZd efdYZd efdYZdS( (s ConfigParserN(sExtensionPoints TracError(ssetssorted(s to_unicodesCRLFs ConfigurationsOptions BoolOptions IntOptions ListOptions PathOptionsExtensionOptionsOrderedExtensionsOptionsConfigurationErrorsyesstruesenabledsonsayes1icBstZdZRS(sEException raised when a value in the configuration file is not valid.(s__name__s __module__s__doc__(((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysConfigurationErrors cBstZdZdZdZdZdZddZddZddZ dd e d Z d Z d Z d ZdZdZdZdZdZdZRS(sThin layer over `ConfigParser` from the Python standard library. In addition to providing some convenience methods, the class remembers the last modification time of the configuration file, and reparses it when the file has changed. cCs>||_t|_t|_d|_h|_|idS(Ni( sfilenamesselfs ConfigParsersparsersNonesparents _lastmtimes _sectionssparse_if_needed(sselfsfilename((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys__init__(s      cCs||ijSdS(sWReturn whether the configuration contains a section of the given name. N(snamesselfssections(sselfsname((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys __contains__0scCs9||ijot|||i|(sselfs __class__s__name__sfilename(sself((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys__repr__<sscCs||i||SdS(svReturn the value of the specified option. Valid default input is a string. Returns a string. N(sselfssectionsgetsnamesdefault(sselfssectionsnamesdefault((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysget?scCs||i||SdS(sWReturn the specified option as boolean value. If the value of the option is one of "yes", "true", "enabled", "on", or "1", this method wll return `True`, otherwise `False`. Valid default input is a string or a bool. Returns a bool. (since Trac 0.9.3, "enabled" added in 0.11) N(sselfssectionsgetboolsnamesdefault(sselfssectionsnamesdefault((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysgetboolFs cCs||i||SdS(s-Return the value of the specified option as integer. If the specified option can not be converted to an integer, a `ConfigurationError` exception is raised. Valid default input is a string or an int. Returns an int. (since Trac 0.10) N(sselfssectionsgetintsnamesdefault(sselfssectionsnamesdefault((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysgetintRs s,cCs||i||||SdS(sReturn a list of values that have been specified as a single comma-separated option. A different separator can be specified using the `sep` parameter. If the `keep_empty` parameter is set to `True`, empty elements are included in the list. Valid default input is a string or a list. Returns a string. (since Trac 0.10) N(sselfssectionsgetlistsnamesdefaultsseps keep_empty(sselfssectionsnamesdefaultsseps keep_empty((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysgetlist^s cCs||i||dS(stChange a configuration value. These changes are not persistent unless saved with `save()`. N(sselfssectionssetsnamesvalue(sselfssectionsnamesvalue((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pyssetlscCsMh}x<tiiD]+\\}}}|i|i|h| s s s%s = %s sutf-8(sselfsfilenamessectionsssectionsoptionssoptionsNonesdefaultsparentsgetsparsers has_options to_unicodescurrentsFalsesappendssortedsopensfileobjswriteskeysvals overriddensreplacesCRLFsencodesclose( sselfskeysoptionsvalsdefaultsoptionsscurrentsfileobjssectionsssection((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pyssaves@    1!   $cCsa|i ptii|i otSnt}tii|i}||i jo2h|i _ |i i |i||_ t }n|i iddo|i idd}tii| o(tiitii|i|}n|i p|ii|jot||_t }qY||iiO}n|iot }t|_n|SdS(Nsinheritsfile(sselfsfilenamesosspathsisfilesFalseschangedsgetmtimesmodtimes _lastmtimesparsers _sectionssreadsTrues has_optionsgetsisabssjoinsdirnamesparents Configurationsparse_if_neededsNone(sselfsmodtimeschangedsfilename((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysparse_if_neededs,"       cCsT|io,tii|ioti|itioti|itndS(N( sselfsfilenamesosspathsisfilesaccesssW_OKsutimesNone(sself((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pystouchs9(s__name__s __module__s__doc__s__init__s __contains__s __getitem__s__repr__sgetsgetboolsgetintsFalsesgetlistssetsdefaultssoptionssremovessectionss has_optionssavesparse_if_neededstouch(((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys Configuration!s$           # sSectioncBstZdZdddgZdZdZdZdZdd Zdd Z dd Z dd e d Z ddZ dZdZRS(stProxy for a specific configuration section. Objects of this class should not be instantiated directly. sconfigsnames overriddencCs||_||_h|_dS(N(sconfigsselfsnames overridden(sselfsconfigsname((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys__init__s  cCsT|iii|i|otSn|iio||ii|ijSntSdS(N(sselfsconfigsparsers has_optionsnamesTruesparentsFalse(sselfsname((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys __contains__s  ccst}|iii|io>x;|iii|iD]}|i|i |Vq;Wn|ii o=x:|ii |iD]!}|i |jo|VqqWndS(N( ssetsoptionssselfsconfigsparsers has_sectionsnamesoptionsaddslowersparent(sselfsoptionssoption((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys__iter__s   cCsd|iSdS(Ns
(sselfsname(sself((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys__repr__ sscCs|iii|i|o|iii|i|}nm|iio#|ii|ii||}n=t i i|i|f}|o|ip|}n|}| odSn#t |t ot|Sn|SdS(svReturn the value of the specified option. Valid default input is a string. Returns a string. uN(sselfsconfigsparsers has_optionsnamesgetsvaluesparentsdefaultsOptionsregistrysoptions isinstances basestrings to_unicode(sselfsnamesdefaultsvaluesoption((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysgets #cCsF|i||}t|to|itj}nt |SdS(s)Return the value of the specified option as boolean. This method returns `True` if the option value is one of "yes", "true", "enabled", "on", or "1", ignoring case. Otherwise `False` is returned. Valid default input is a string or a bool. Returns a bool. N( sselfsgetsnamesdefaultsvalues isinstances basestringslowers _TRUE_VALUESsbool(sselfsnamesdefaultsvalue((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysgetbool%s cCs`|i||}| odSnyt|SWn)tj otdt|nXdS(s Return the value of the specified option as integer. If the specified option can not be converted to an integer, a `ConfigurationError` exception is raised. Valid default input is a string or an int. Returns an int. isexpected integer, got %sN( sselfsgetsnamesdefaultsvaluesints ValueErrorsConfigurationErrorsrepr(sselfsnamesdefaultsvalue((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysgetint2ss,c Cs|i||}| ogSnt|to:gi}|i |D]}||i qI~}n t|}| ott|}n|SdS(soReturn a list of values that have been specified as a single comma-separated option. A different separator can be specified using the `sep` parameter. If the `keep_empty` parameter is set to `False`, empty elements are omitted from the list. Valid default input is a string or a list. Returns a list. N(sselfsgetsnamesdefaultsvalues isinstances basestringsappends_[1]ssplitssepsitemsstripsitemsslists keep_emptysfiltersNone( sselfsnamesdefaultsseps keep_emptysitemssvalues_[1]sitem((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysgetlistBs : cCs|iii|i|o|iii|i|}| o|Sntii | o+tii tii |ii |}ntii tii|Sn3|iio!|ii|ii||Sn|SdS(sReturn the value of the specified option as a path name, relative to the location of the configuration file the option is defined in. Valid default input is a string. Returns a string with normalised path. N(sselfsconfigsparsers has_optionsnamesgetspathsdefaultsossisabssjoinsdirnamesfilenamesnormcasesrealpathsparentsgetpath(sselfsnamesdefaultspath((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysgetpathWs   !ccs(x!|D]}||i|fVqWdS(s>Return `(name, value)` tuples for every option in the section.N(sselfsnamesget(sselfsname((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysoptionsjscCs|iii|i o|iii|in|tjot|i |(sselfs __class__s__name__ssectionsname(sself((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys__repr__s( s__name__s __module__s__doc__sregistrysSectionsgetsaccessorsNones__init__s__get__s__set__s__repr__(((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysOption~s   cBstZdZeiZRS(s-Descriptor for boolean configuration options.(s__name__s __module__s__doc__sSectionsgetboolsaccessor(((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys BoolOptions cBstZdZeiZRS(s-Descriptor for integer configuration options.(s__name__s __module__s__doc__sSectionsgetintsaccessor(((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys IntOptions cBs,tZdZededdZdZRS(shDescriptor for configuration options that contain multiple values separated by a specific character.s,scCs/ti|||||||_||_dS(N( sOptions__init__sselfssectionsnamesdefaultsdocsseps keep_empty(sselfssectionsnamesdefaultsseps keep_emptysdoc((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys__init__s cCs |i|||i|iSdS(N(ssectionsgetlistsnamesdefaultsselfsseps keep_empty(sselfssectionsnamesdefault((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysaccessors(s__name__s __module__s__doc__sNonesFalses__init__saccessor(((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys ListOptions cBstZdZeiZRS(s6Descriptor for file system path configuration options.(s__name__s __module__s__doc__sSectionsgetpathsaccessor(((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys PathOptions cBs tZeddZdZRS(NscCs,ti|||||t||_ dS(N( sOptions__init__sselfssectionsnamesdefaultsdocsExtensionPoints interfacesxtnpt(sselfssectionsnames interfacesdefaultsdoc((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys__init__scCs|tjo|Snti|||}x5|ii|D]!}|i i |jo|Sq=q=Wt d|ii i ||i|ifdS(NslCannot find an implementation of the "%s" interface named "%s". Please update the option %s.%s in trac.ini.(sinstancesNonesselfsOptions__get__sownersvaluesxtnpts extensionssimpls __class__s__name__sAttributeErrors interfacessectionsname(sselfsinstancesownersvaluesimpl((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys__get__s  (s__name__s __module__sNones__init__s__get__(((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysExtensionOptionscBs)tZdZeeddZdZRS(sA comma separated, ordered, list of components implementing `interface`. Can be empty. If `include_missing` is true (the default) all components implementing the interface are returned, with those specified by the option ordered first.scCs8ti||||d|t||_ ||_ dS(Nsdoc( s ListOptions__init__sselfssectionsnamesdefaultsdocsExtensionPoints interfacesxtnptsinclude_missing(sselfssectionsnames interfacesdefaultsinclude_missingsdoc((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys__init__scs|tjo|Snti|||g}xH|ii |D]4}|i p|i i jo|i|qCqCWd}|i||SdS(Ncs|ii|iif\}}|jot|jSn|jot|j Snti|i|SdS(N(sxs __class__s__name__sysordersintscmpsindex(sxsy(sorder(s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pyscompares   (sinstancesNonesselfs ListOptions__get__sownersorders componentssxtnpts extensionssimplsinclude_missings __class__s__name__sappendscomparessort(sselfsinstancesownerscompares componentssordersimpl((sorders0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys__get__s   (s__name__s __module__s__doc__sNonesTrues__init__s__get__(((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pysOrderedExtensionsOptions (s ConfigParsersoss trac.coresExtensionPoints TracErrorstrac.util.compatssetssortedstrac.util.texts to_unicodesCRLFs__all__sTrues _TRUE_VALUESsConfigurationErrorsobjects ConfigurationsSectionsOptions BoolOptions IntOptions ListOptions PathOptionsExtensionOptionsOrderedExtensionsOption(ssets to_unicodes IntOptionsSections PathOptionsExtensionPoints ListOptionsOptions__all__s TracErrors ConfigurationsOrderedExtensionsOptionsExtensionOptionsCRLFssorteds ConfigParsersConfigurationErrors _TRUE_VALUESs BoolOptionsos((s0build/bdist.darwin-9.5.0-i386/egg/trac/config.pys?s  !' PKkh9He trac/core.py# -*- coding: utf-8 -*- # # Copyright (C) 2003-2008 Edgewall Software # Copyright (C) 2003-2004 Jonas Borgström # Copyright (C) 2004-2005 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Jonas Borgström # Christopher Lenz __all__ = ['Component', 'ExtensionPoint', 'implements', 'Interface', 'TracError'] class TracError(Exception): """Exception base class for errors in Trac.""" title = 'Trac Error' def __init__(self, message, title=None, show_traceback=False): """If message is a genshi.builder.tag object, everything up to the first

will be displayed in the red box, and everything after will be displayed below the red box. If title is given, it will be displayed as the large header above the error message. """ Exception.__init__(self, message) self.message = message if title: self.title = title self.show_traceback = show_traceback def __unicode__(self): return unicode(self.message) class Interface(object): """Marker base class for extension point interfaces.""" class ExtensionPoint(property): """Marker class for extension points in components.""" def __init__(self, interface): """Create the extension point. @param interface: the `Interface` subclass that defines the protocol for the extension point """ property.__init__(self, self.extensions) self.interface = interface self.__doc__ = 'List of components that implement `%s`' % \ self.interface.__name__ def extensions(self, component): """Return a list of components that declare to implement the extension point interface. """ extensions = ComponentMeta._registry.get(self.interface, []) return filter(None, [component.compmgr[cls] for cls in extensions]) def __repr__(self): """Return a textual representation of the extension point.""" return '' % self.interface.__name__ class ComponentMeta(type): """Meta class for components. Takes care of component and extension point registration. """ _components = [] _registry = {} def __new__(cls, name, bases, d): """Create the component class.""" new_class = type.__new__(cls, name, bases, d) if name == 'Component': # Don't put the Component base class in the registry return new_class # Only override __init__ for Components not inheriting ComponentManager if True not in [issubclass(x, ComponentManager) for x in bases]: # Allow components to have a no-argument initializer so that # they don't need to worry about accepting the component manager # as argument and invoking the super-class initializer init = d.get('__init__') if not init: # Because we're replacing the initializer, we need to make sure # that any inherited initializers are also called. for init in [b.__init__._original for b in new_class.mro() if issubclass(b, Component) and '__init__' in b.__dict__]: break def maybe_init(self, compmgr, init=init, cls=new_class): if cls not in compmgr.components: compmgr.components[cls] = self if init: try: init(self) except: del compmgr.components[cls] raise maybe_init._original = init new_class.__init__ = maybe_init if d.get('abstract'): # Don't put abstract component classes in the registry return new_class ComponentMeta._components.append(new_class) registry = ComponentMeta._registry for interface in d.get('_implements', []): registry.setdefault(interface, []).append(new_class) for base in [base for base in bases if hasattr(base, '_implements')]: for interface in base._implements: registry.setdefault(interface, []).append(new_class) return new_class class Component(object): """Base class for components. Every component can declare what extension points it provides, as well as what extension points of other components it extends. """ __metaclass__ = ComponentMeta def __new__(cls, *args, **kwargs): """Return an existing instance of the component if it has already been activated, otherwise create a new instance. """ # If this component is also the component manager, just invoke that if issubclass(cls, ComponentManager): self = super(Component, cls).__new__(cls) self.compmgr = self return self # The normal case where the component is not also the component manager compmgr = args[0] self = compmgr.components.get(cls) if self is None: self = super(Component, cls).__new__(cls) self.compmgr = compmgr compmgr.component_activated(self) return self def implements(*interfaces): """Can be used in the class definiton of `Component` subclasses to declare the extension points that are extended. """ import sys frame = sys._getframe(1) locals_ = frame.f_locals # Some sanity checks assert locals_ is not frame.f_globals and '__module__' in locals_, \ 'implements() can only be used in a class definition' locals_.setdefault('_implements', []).extend(interfaces) implements = staticmethod(implements) implements = Component.implements class ComponentManager(object): """The component manager keeps a pool of active components.""" def __init__(self): """Initialize the component manager.""" self.components = {} self.enabled = {} if isinstance(self, Component): self.components[self.__class__] = self def __contains__(self, cls): """Return wether the given class is in the list of active components.""" return cls in self.components def __getitem__(self, cls): """Activate the component instance for the given class, or return the existing the instance if the component has already been activated. """ if cls not in self.enabled: self.enabled[cls] = self.is_component_enabled(cls) if not self.enabled[cls]: return None component = self.components.get(cls) if not component: if cls not in ComponentMeta._components: raise TracError('Component "%s" not registered' % cls.__name__) try: component = cls(self) except TypeError, e: raise TracError('Unable to instantiate component %r (%s)' % (cls, e)) return component def component_activated(self, component): """Can be overridden by sub-classes so that special initialization for components can be provided. """ def is_component_enabled(self, cls): """Can be overridden by sub-classes to veto the activation of a component. If this method returns False, the component with the given class will not be available. """ return True PKmh95:&:& trac/core.pyc; Ic@sdddddgZdefdYZdefdYZdefdYZdefd YZdefd YZ e i Z d efd YZ d S(s ComponentsExtensionPoints implementss Interfaces TracErrorcBs,tZdZdZeedZdZRS(s(Exception base class for errors in Trac.s Trac ErrorcCs:ti||||_|o ||_n||_dS(s#If message is a genshi.builder.tag object, everything up to the first

will be displayed in the red box, and everything after will be displayed below the red box. If title is given, it will be displayed as the large header above the error message. N(s Exceptions__init__sselfsmessagestitlesshow_traceback(sselfsmessagestitlesshow_traceback((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys__init__s   cCst|iSdS(N(sunicodesselfsmessage(sself((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys __unicode__)s(s__name__s __module__s__doc__stitlesNonesFalses__init__s __unicode__(((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys TracErrors  cBstZdZRS(s1Marker base class for extension point interfaces.(s__name__s __module__s__doc__(((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys Interface,s cBs)tZdZdZdZdZRS(s0Marker class for extension points in components.cCs3ti||i||_d|ii|_dS(sCreate the extension point. @param interface: the `Interface` subclass that defines the protocol for the extension point s&List of components that implement `%s`N(spropertys__init__sselfs extensionss interfaces__name__s__doc__(sselfs interface((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys__init__3s cCsQtii|ig}ttgi}|D]}||i |q,~SdS(seReturn a list of components that declare to implement the extension point interface. N( s ComponentMetas _registrysgetsselfs interfaces extensionssfiltersNonesappends_[1]sclss componentscompmgr(sselfs components_[1]s extensionsscls((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys extensions>scCsd|iiSdS(s7Return a textual representation of the extension point.sN(sselfs interfaces__name__(sself((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys__repr__Es(s__name__s __module__s__doc__s__init__s extensionss__repr__(((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pysExtensionPoint0s  s ComponentMetacBs#tZdZgZhZdZRS(sbMeta class for components. Takes care of component and extension point registration. c Csti||||} |djo| Sntgi}|D]} |t | t q>~jo|i d}| ogxdgi}| iD]:}t |to d|ijo||iiqq~D]}PqWn|| d}||_|| _n|i do| Sntii| ti}x3|i dgD]} |i| gi| qKWxpgi}|D]$} t| do|| qq~D]3} x*| iD]} |i| gi| qWqW| SdS(sCreate the component class.s Components__init__cCsU||ijoA||i|<|o)y||WqM|i|=qMXqQndS(N(sclsscompmgrs componentssselfsinit(sselfscompmgrsinitscls((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys maybe_initgs  sabstracts _implementsN(stypes__new__sclssnamesbasessds new_classsTruesappends_[1]sxs issubclasssComponentManagersgetsinitsmrosbs Components__dict__s__init__s _originals maybe_inits ComponentMetas _componentss _registrysregistrys interfaces setdefaultsbaseshasattrs _implements( sclssnamesbasessdsbs maybe_inits_[1]sinitsregistrys interfacesxsbases new_class((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys__new__Rs4 7X    < !(s__name__s __module__s__doc__s _componentss _registrys__new__(((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys ComponentMetaJs cBs2tZdZeZdZdZeeZRS(sBase class for components. Every component can declare what extension points it provides, as well as what extension points of other components it extends. cOst|to)tt|i|}||_|Sn|d}|i i |}|t jo2tt|i|}||_|i |n|SdS(sReturn an existing instance of the component if it has already been activated, otherwise create a new instance. iN( s issubclasssclssComponentManagerssupers Components__new__sselfscompmgrsargss componentssgetsNonescomponent_activated(sclssargsskwargssselfscompmgr((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys__new__s    cGsedk}|id}|i}||ij o d|jp td|idgi|dS(sCan be used in the class definiton of `Component` subclasses to declare the extension points that are extended. Nis __module__s3implements() can only be used in a class definitions _implements( ssyss _getframesframesf_localsslocals_s f_globalssAssertionErrors setdefaultsextends interfaces(s interfacesssysslocals_sframe((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys implementss   '(s__name__s __module__s__doc__s ComponentMetas __metaclass__s__new__s implementss staticmethod(((s.build/bdist.darwin-9.5.0-i386/egg/trac/core.pys Components   sComponentManagercBs;tZdZdZdZdZdZdZRS(s8The component manager keeps a pool of active components.cCs:h|_h|_t|to||i|i # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Daniel Lundin from trac.db import Table, Column, Index # Database version identifier. Used for automatic upgrades. db_version = 21 def __mkreports(reports): """Utility function used to create report data in same syntax as the default data. This extra step is done to simplify editing the default reports.""" result = [] for report in reports: result.append((None, report[0], report[2], report[1])) return result ## ## Database schema ## schema = [ # Common Table('system', key='name')[ Column('name'), Column('value')], Table('permission', key=('username', 'action'))[ Column('username'), Column('action')], Table('auth_cookie', key=('cookie', 'ipnr', 'name'))[ Column('cookie'), Column('name'), Column('ipnr'), Column('time', type='int')], Table('session', key=('sid', 'authenticated'))[ Column('sid'), Column('authenticated', type='int'), Column('last_visit', type='int'), Index(['last_visit']), Index(['authenticated'])], Table('session_attribute', key=('sid', 'authenticated', 'name'))[ Column('sid'), Column('authenticated', type='int'), Column('name'), Column('value')], # Attachments Table('attachment', key=('type', 'id', 'filename'))[ Column('type'), Column('id'), Column('filename'), Column('size', type='int'), Column('time', type='int'), Column('description'), Column('author'), Column('ipnr')], # Wiki system Table('wiki', key=('name', 'version'))[ Column('name'), Column('version', type='int'), Column('time', type='int'), Column('author'), Column('ipnr'), Column('text'), Column('comment'), Column('readonly', type='int'), Index(['time'])], # Version control cache Table('revision', key='rev')[ Column('rev'), Column('time', type='int'), Column('author'), Column('message'), Index(['time'])], Table('node_change', key=('rev', 'path', 'change_type'))[ Column('rev'), Column('path'), Column('node_type', size=1), Column('change_type', size=1), Column('base_path'), Column('base_rev'), Index(['rev'])], # Ticket system Table('ticket', key='id')[ Column('id', auto_increment=True), Column('type'), Column('time', type='int'), Column('changetime', type='int'), Column('component'), Column('severity'), Column('priority'), Column('owner'), Column('reporter'), Column('cc'), Column('version'), Column('milestone'), Column('status'), Column('resolution'), Column('summary'), Column('description'), Column('keywords'), Index(['time']), Index(['status'])], Table('ticket_change', key=('ticket', 'time', 'field'))[ Column('ticket', type='int'), Column('time', type='int'), Column('author'), Column('field'), Column('oldvalue'), Column('newvalue'), Index(['ticket']), Index(['time'])], Table('ticket_custom', key=('ticket', 'name'))[ Column('ticket', type='int'), Column('name'), Column('value')], Table('enum', key=('type', 'name'))[ Column('type'), Column('name'), Column('value')], Table('component', key='name')[ Column('name'), Column('owner'), Column('description')], Table('milestone', key='name')[ Column('name'), Column('due', type='int'), Column('completed', type='int'), Column('description')], Table('version', key='name')[ Column('name'), Column('time', type='int'), Column('description')], # Report system Table('report', key='id')[ Column('id', auto_increment=True), Column('author'), Column('title'), Column('query'), Column('description')], ] ## ## Default Reports ## def get_reports(db): return ( ('Active Tickets', """ * List all active tickets by priority. * Color each row based on priority. """, """ SELECT p.value AS __color__, id AS ticket, summary, component, version, milestone, t.type AS type, owner, status, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status <> 'closed' ORDER BY """ + db.cast('p.value', 'int') + """, milestone, t.type, time """), #---------------------------------------------------------------------------- ('Active Tickets by Version', """ This report shows how to color results by priority, while grouping results by version. Last modification time, description and reporter are included as hidden fields for useful RSS export. """, """ SELECT p.value AS __color__, version AS __group__, id AS ticket, summary, component, version, t.type AS type, owner, status, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status <> 'closed' ORDER BY (version IS NULL),version, """ + db.cast('p.value', 'int') + """, t.type, time """), #---------------------------------------------------------------------------- ('Active Tickets by Milestone', """ This report shows how to color results by priority, while grouping results by milestone. Last modification time, description and reporter are included as hidden fields for useful RSS export. """, """ SELECT p.value AS __color__, %s AS __group__, id AS ticket, summary, component, version, t.type AS type, owner, status, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status <> 'closed' ORDER BY (milestone IS NULL),milestone, %s, t.type, time """ % (db.concat("'Milestone '", 'milestone'), db.cast('p.value', 'int'))), #---------------------------------------------------------------------------- ('Accepted, Active Tickets by Owner', """ List accepted tickets, group by ticket owner, sorted by priority. """, """ SELECT p.value AS __color__, owner AS __group__, id AS ticket, summary, component, milestone, t.type AS type, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status = 'accepted' ORDER BY owner, """ + db.cast('p.value', 'int') + """, t.type, time """), #---------------------------------------------------------------------------- ('Accepted, Active Tickets by Owner (Full Description)', """ List tickets accepted, group by ticket owner. This report demonstrates the use of full-row display. """, """ SELECT p.value AS __color__, owner AS __group__, id AS ticket, summary, component, milestone, t.type AS type, time AS created, description AS _description_, changetime AS _changetime, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status = 'accepted' ORDER BY owner, """ + db.cast('p.value', 'int') + """, t.type, time """), #---------------------------------------------------------------------------- ('All Tickets By Milestone (Including closed)', """ A more complex example to show how to make advanced reports. """, """ SELECT p.value AS __color__, t.milestone AS __group__, (CASE status WHEN 'closed' THEN 'color: #777; background: #ddd; border-color: #ccc;' ELSE (CASE owner WHEN $USER THEN 'font-weight: bold' END) END) AS __style__, id AS ticket, summary, component, status, resolution,version, t.type AS type, priority, owner, changetime AS modified, time AS _time,reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'), (CASE status WHEN 'closed' THEN changetime ELSE (-1) * %s END) DESC """ % db.cast('p.value', 'int')), #---------------------------------------------------------------------------- ('My Tickets', """ This report demonstrates the use of the automatically set USER dynamic variable, replaced with the username of the logged in user when executed. """, """ SELECT p.value AS __color__, (CASE status WHEN 'accepted' THEN 'Accepted' ELSE 'Owned' END) AS __group__, id AS ticket, summary, component, version, milestone, t.type AS type, priority, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE t.status <> 'closed' AND owner = $USER ORDER BY (status = 'accepted') DESC, """ + db.cast('p.value', 'int') + """, milestone, t.type, time """), #---------------------------------------------------------------------------- ('Active Tickets, Mine first', """ * List all active tickets by priority. * Show all tickets owned by the logged in user in a group first. """, """ SELECT p.value AS __color__, (CASE owner WHEN $USER THEN 'My Tickets' ELSE 'Active Tickets' END) AS __group__, id AS ticket, summary, component, version, milestone, t.type AS type, owner, status, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status <> 'closed' ORDER BY (owner = $USER) DESC, """ + db.cast('p.value', 'int') + """, milestone, t.type, time """)) ## ## Default database values ## # (table, (column1, column2), ((row1col1, row1col2), (row2col1, row2col2))) def get_data(db): return (('component', ('name', 'owner'), (('component1', 'somebody'), ('component2', 'somebody'))), ('milestone', ('name', 'due', 'completed'), (('milestone1', 0, 0), ('milestone2', 0, 0), ('milestone3', 0, 0), ('milestone4', 0, 0))), ('version', ('name', 'time'), (('1.0', 0), ('2.0', 0))), ('enum', ('type', 'name', 'value'), (('resolution', 'fixed', 1), ('resolution', 'invalid', 2), ('resolution', 'wontfix', 3), ('resolution', 'duplicate', 4), ('resolution', 'worksforme', 5), ('priority', 'blocker', 1), ('priority', 'critical', 2), ('priority', 'major', 3), ('priority', 'minor', 4), ('priority', 'trivial', 5), ('ticket_type', 'defect', 1), ('ticket_type', 'enhancement', 2), ('ticket_type', 'task', 3))), ('permission', ('username', 'action'), (('anonymous', 'LOG_VIEW'), ('anonymous', 'FILE_VIEW'), ('anonymous', 'WIKI_VIEW'), ('authenticated', 'WIKI_CREATE'), ('authenticated', 'WIKI_MODIFY'), ('anonymous', 'SEARCH_VIEW'), ('anonymous', 'REPORT_VIEW'), ('anonymous', 'REPORT_SQL_VIEW'), ('anonymous', 'TICKET_VIEW'), ('authenticated', 'TICKET_CREATE'), ('authenticated', 'TICKET_MODIFY'), ('anonymous', 'BROWSER_VIEW'), ('anonymous', 'TIMELINE_VIEW'), ('anonymous', 'CHANGESET_VIEW'), ('anonymous', 'ROADMAP_VIEW'), ('anonymous', 'MILESTONE_VIEW'))), ('system', ('name', 'value'), (('database_version', str(db_version)), ('initial_database_version', str(db_version)), ('youngest_rev', ''))), ('report', ('author', 'title', 'query', 'description'), __mkreports(get_reports(db)))) PKmh9זaAz'z'trac/db_default.pyc; Ic@sdklZlZlZdZdZedddededfedddd feded fed dd d dfed eded ed ddfeddddfededddedddedgedgfedddddfededddededfedddddfededededdded ddededed feddddfededdded ddeded edededdded gf ed dd!ed!ed ddeded"ed gfed#dd!d$d%fed!ed$ed&dd'ed%dd'ed(ed)ed!gfed*ddedd+eeded dded,dded-ed.ed/ed0ed1ed2eded3ed4ed5ed6eded7ed ged4gfed8dd*d d9fed*dded ddeded9ed:ed;ed*ged gfed<dd*dfed*ddededfed=dddfedededfed-ddeded0edfed3ddeded>dded?ddedfedddeded ddedfed@ddedd+eededAedBedfgZdCZdDZ dES(F(sTablesColumnsIndexicCsDg}x3|D]+}|it|d|d|dfq W|SdS(sUtility function used to create report data in same syntax as the default data. This extra step is done to simplify editing the default reports.iiiN(sresultsreportssreportsappendsNone(sreportssreportsresult((s4build/bdist.darwin-9.5.0-i386/egg/trac/db_default.pys __mkreportss )ssystemskeysnamesvalues permissionsusernamesactions auth_cookiescookiesipnrstimestypesintssessionssids authenticateds last_visitssession_attributes attachmentsidsfilenamessizes descriptionsauthorswikisversionstextscommentsreadonlysrevisionsrevsmessages node_changespaths change_types node_typeis base_pathsbase_revsticketsauto_increments changetimes componentsseveritysprioritysownersreportersccs milestonesstatuss resolutionssummaryskeywordss ticket_changesfieldsoldvaluesnewvalues ticket_customsenumsdues completedsreportstitlesqueryc Csddd|idddfddd |iddd fd d d |idd|iddffddd|iddd fddd|iddd fddd|iddfddd|idddfddd|idddffSdS(NsActive TicketssN * List all active tickets by priority. * Color each row based on priority. sZ SELECT p.value AS __color__, id AS ticket, summary, component, version, milestone, t.type AS type, owner, status, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status <> 'closed' ORDER BY sp.valuesints, milestone, t.type, time sActive Tickets by Versions This report shows how to color results by priority, while grouping results by version. Last modification time, description and reporter are included as hidden fields for useful RSS export. s SELECT p.value AS __color__, version AS __group__, id AS ticket, summary, component, version, t.type AS type, owner, status, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status <> 'closed' ORDER BY (version IS NULL),version, s, t.type, time sActive Tickets by Milestones This report shows how to color results by priority, while grouping results by milestone. Last modification time, description and reporter are included as hidden fields for useful RSS export. s SELECT p.value AS __color__, %s AS __group__, id AS ticket, summary, component, version, t.type AS type, owner, status, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status <> 'closed' ORDER BY (milestone IS NULL),milestone, %s, t.type, time s 'Milestone 's milestones!Accepted, Active Tickets by OwnersC List accepted tickets, group by ticket owner, sorted by priority. s[ SELECT p.value AS __color__, owner AS __group__, id AS ticket, summary, component, milestone, t.type AS type, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status = 'accepted' ORDER BY owner, s4Accepted, Active Tickets by Owner (Full Description)se List tickets accepted, group by ticket owner. This report demonstrates the use of full-row display. s[ SELECT p.value AS __color__, owner AS __group__, id AS ticket, summary, component, milestone, t.type AS type, time AS created, description AS _description_, changetime AS _changetime, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status = 'accepted' ORDER BY owner, s,All Tickets By Milestone (Including closed)s> A more complex example to show how to make advanced reports. s SELECT p.value AS __color__, t.milestone AS __group__, (CASE status WHEN 'closed' THEN 'color: #777; background: #ddd; border-color: #ccc;' ELSE (CASE owner WHEN $USER THEN 'font-weight: bold' END) END) AS __style__, id AS ticket, summary, component, status, resolution,version, t.type AS type, priority, owner, changetime AS modified, time AS _time,reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'), (CASE status WHEN 'closed' THEN changetime ELSE (-1) * %s END) DESC s My Ticketss This report demonstrates the use of the automatically set USER dynamic variable, replaced with the username of the logged in user when executed. s SELECT p.value AS __color__, (CASE status WHEN 'accepted' THEN 'Accepted' ELSE 'Owned' END) AS __group__, id AS ticket, summary, component, version, milestone, t.type AS type, priority, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE t.status <> 'closed' AND owner = $USER ORDER BY (status = 'accepted') DESC, sActive Tickets, Mine firstsk * List all active tickets by priority. * Show all tickets owned by the logged in user in a group first. s SELECT p.value AS __color__, (CASE owner WHEN $USER THEN 'My Tickets' ELSE 'Active Tickets' END) AS __group__, id AS ticket, summary, component, version, milestone, t.type AS type, owner, status, time AS created, changetime AS _changetime, description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' WHERE status <> 'closed' ORDER BY (owner = $USER) DESC, (sdbscastsconcat(sdb((s4build/bdist.darwin-9.5.0-i386/egg/trac/db_default.pys get_reportsscCsEdddfddfddfffdddd fd d d fd d d fd d d fdd d fffdddfdd fdd fffddddfdddfdddfdddfdddfddd fd!d"dfd!d#dfd!d$dfd!d%dfd!d&d fd'd(dfd'd)dfd'd*dff fd+d,d-fd.d/fd.d0fd.d1fd2d3fd2d4fd.d5fd.d6fd.d7fd.d8fd2d9fd2d:fd.d;fd.d<fd.d=fd.d>fd.d?fffd@ddfdAttfdBttfdCdDfffdEdFdGdHdIftt|ffSdS(JNs componentsnamesowners component1ssomebodys component2s milestonesdues completeds milestone1is milestone2s milestone3s milestone4sversionstimes1.0s2.0senumstypesvalues resolutionsfixedisinvalidiswontfixis duplicateis worksformeisprioritysblockerscriticalsmajorsminorstrivials ticket_typesdefects enhancementstasks permissionsusernamesactions anonymoussLOG_VIEWs FILE_VIEWs WIKI_VIEWs authenticateds WIKI_CREATEs WIKI_MODIFYs SEARCH_VIEWs REPORT_VIEWsREPORT_SQL_VIEWs TICKET_VIEWs TICKET_CREATEs TICKET_MODIFYs BROWSER_VIEWs TIMELINE_VIEWsCHANGESET_VIEWs ROADMAP_VIEWsMILESTONE_VIEWssystemsdatabase_versionsinitial_database_versions youngest_revssreportsauthorstitlesquerys description(sstrs db_versions __mkreportss get_reportssdb(sdb((s4build/bdist.darwin-9.5.0-i386/egg/trac/db_default.pysget_dataPsN( strac.dbsTablesColumnsIndexs db_versions __mkreportssTruesschemas get_reportssget_data(s db_versionsIndexsColumnsget_datas get_reportss __mkreportssTablesschema((s4build/bdist.darwin-9.5.0-i386/egg/trac/db_default.pys?s  PKkh9d\\ trac/env.py# -*- coding: utf-8 -*- # # Copyright (C) 2003-2008 Edgewall Software # Copyright (C) 2003-2007 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Jonas Borgström import os try: import threading except ImportError: import dummy_threading as threading import setuptools import sys from urlparse import urlsplit from trac import db_default from trac.config import * from trac.core import Component, ComponentManager, implements, Interface, \ ExtensionPoint, TracError from trac.db import DatabaseManager from trac.util import get_pkginfo from trac.util.translation import _ from trac.versioncontrol import RepositoryManager from trac.web.href import Href __all__ = ['Environment', 'IEnvironmentSetupParticipant', 'open_environment'] class IEnvironmentSetupParticipant(Interface): """Extension point interface for components that need to participate in the creation and upgrading of Trac environments, for example to create additional database tables.""" def environment_created(): """Called when a new Trac environment is created.""" def environment_needs_upgrade(db): """Called when Trac checks whether the environment needs to be upgraded. Should return `True` if this participant needs an upgrade to be performed, `False` otherwise. """ def upgrade_environment(db): """Actually perform an environment upgrade. Implementations of this method should not commit any database transactions. This is done implicitly after all participants have performed the upgrades they need without an error being raised. """ class Environment(Component, ComponentManager): """Trac stores project information in a Trac environment. A Trac environment consists of a directory structure containing among other things: * a configuration file. * an SQLite database (stores tickets, wiki pages...) * Project specific templates and plugins. * wiki and ticket attachments. """ setup_participants = ExtensionPoint(IEnvironmentSetupParticipant) shared_plugins_dir = PathOption('inherit', 'plugins_dir', '', """Path of the directory containing additional plugins. Plugins in that directory are loaded in addition to those in the directory of the environment `plugins`, with this one taking precedence. (''since 0.11'')""") base_url = Option('trac', 'base_url', '', """Reference URL for the Trac deployment. This is the base URL that will be used when producing documents that will be used outside of the web browsing context, like for example when inserting URLs pointing to Trac resources in notification e-mails.""") base_url_for_redirect = BoolOption('trac', 'use_base_url_for_redirect', False, """Optionally use `[trac] base_url` for redirects. In some configurations, usually involving running Trac behind a HTTP proxy, Trac can't automatically reconstruct the URL that is used to access it. You may need to use this option to force Trac to use the `base_url` setting also for redirects. This introduces the obvious limitation that this environment will only be usable when accessible from that URL, as redirects are frequently used. ''(since 0.10.5)''""") secure_cookies = BoolOption('trac', 'secure_cookies', False, """Restrict cookies to HTTPS connections. When true, set the `secure` flag on all cookies so that they are only sent to the server on HTTPS connections. Use this if your Trac instance is only accessible through HTTPS. (''since 0.11.2'')""") project_name = Option('project', 'name', 'My Project', """Name of the project.""") project_description = Option('project', 'descr', 'My example project', """Short description of the project.""") project_url = Option('project', 'url', '', """URL of the main project web site, usually the website in which the `base_url` resides.""") project_admin = Option('project', 'admin', '', """E-Mail address of the project's administrator.""") project_footer = Option('project', 'footer', 'Visit the Trac open source project at
' '' 'http://trac.edgewall.org/', """Page footer text (right-aligned).""") project_icon = Option('project', 'icon', 'common/trac.ico', """URL of the icon of the project.""") log_type = Option('logging', 'log_type', 'none', """Logging facility to use. Should be one of (`none`, `file`, `stderr`, `syslog`, `winlog`).""") log_file = Option('logging', 'log_file', 'trac.log', """If `log_type` is `file`, this should be a path to the log-file.""") log_level = Option('logging', 'log_level', 'DEBUG', """Level of verbosity in log. Should be one of (`CRITICAL`, `ERROR`, `WARN`, `INFO`, `DEBUG`).""") log_format = Option('logging', 'log_format', None, """Custom logging format. If nothing is set, the following will be used: Trac[$(module)s] $(levelname)s: $(message)s In addition to regular key names supported by the Python logger library library (see http://docs.python.org/lib/node422.html), one could use: - $(path)s the path for the current environment - $(basename)s the last path component of the current environment - $(project)s the project name Note the usage of `$(...)s` instead of `%(...)s` as the latter form would be interpreted by the ConfigParser itself. Example: ($(thread)d) Trac[$(basename)s:$(module)s] $(levelname)s: $(message)s (since 0.10.5)""") def __init__(self, path, create=False, options=[]): """Initialize the Trac environment. @param path: the absolute path to the Trac environment @param create: if `True`, the environment is created and populated with default data; otherwise, the environment is expected to already exist. @param options: A list of `(section, name, value)` tuples that define configuration options """ ComponentManager.__init__(self) self.path = path self.setup_config(load_defaults=create) self.setup_log() from trac import core, __version__ as VERSION self.systeminfo = [ ('Trac', get_pkginfo(core).get('version', VERSION)), ('Python', sys.version), ('setuptools', setuptools.__version__), ] self._href = self._abs_href = None from trac.loader import load_components plugins_dir = self.config.get('inherit', 'plugins_dir') load_components(self, plugins_dir and (plugins_dir,)) if create: self.create(options) else: self.verify() if create: for setup_participant in self.setup_participants: setup_participant.environment_created() def component_activated(self, component): """Initialize additional member variables for components. Every component activated through the `Environment` object gets three member variables: `env` (the environment object), `config` (the environment configuration) and `log` (a logger object).""" component.env = self component.config = self.config component.log = self.log def is_component_enabled(self, cls): """Implemented to only allow activation of components that are not disabled in the configuration. This is called by the `ComponentManager` base class when a component is about to be activated. If this method returns false, the component does not get activated.""" if not isinstance(cls, basestring): component_name = (cls.__module__ + '.' + cls.__name__).lower() else: component_name = cls.lower() rules = [(name.lower(), value.lower() in ('enabled', 'on')) for name, value in self.config.options('components')] rules.sort(lambda a, b: -cmp(len(a[0]), len(b[0]))) for pattern, enabled in rules: if component_name == pattern or pattern.endswith('*') \ and component_name.startswith(pattern[:-1]): # Disable the pre-0.11 WebAdmin plugin # Please note that there's no recommendation to uninstall the # plugin because doing so would obviously break the backwards # compatibility that the new integration administration # interface tries to provide for old WebAdmin extensions if component_name.startswith('webadmin.'): self.log.info('The legacy TracWebAdmin plugin has been ' 'automatically disabled, and the integrated ' 'administration interface will be used ' 'instead.') return False return enabled # versioncontrol components are enabled if the repository is configured # FIXME: this shouldn't be hardcoded like this if component_name.startswith('trac.versioncontrol.'): return self.config.get('trac', 'repository_dir') != '' # By default, all components in the trac package are enabled return component_name.startswith('trac.') def verify(self): """Verify that the provided path points to a valid Trac environment directory.""" fd = open(os.path.join(self.path, 'VERSION'), 'r') try: assert fd.read(26) == 'Trac Environment Version 1' finally: fd.close() def get_db_cnx(self): """Return a database connection from the connection pool.""" return DatabaseManager(self).get_connection() def shutdown(self, tid=None): """Close the environment.""" RepositoryManager(self).shutdown(tid) DatabaseManager(self).shutdown(tid) def get_repository(self, authname=None): """Return the version control repository configured for this environment. @param authname: user name for authorization """ return RepositoryManager(self).get_repository(authname) def create(self, options=[]): """Create the basic directory structure of the environment, initialize the database and populate the configuration file with default values. If options contains ('inherit', 'file'), default values will not be loaded; they are expected to be provided by that file or other options. """ def _create_file(fname, data=None): fd = open(fname, 'w') if data: fd.write(data) fd.close() # Create the directory structure if not os.path.exists(self.path): os.mkdir(self.path) os.mkdir(self.get_log_dir()) os.mkdir(self.get_htdocs_dir()) os.mkdir(os.path.join(self.path, 'plugins')) # Create a few files _create_file(os.path.join(self.path, 'VERSION'), 'Trac Environment Version 1\n') _create_file(os.path.join(self.path, 'README'), 'This directory contains a Trac environment.\n' 'Visit http://trac.edgewall.org/ for more information.\n') # Setup the default configuration os.mkdir(os.path.join(self.path, 'conf')) _create_file(os.path.join(self.path, 'conf', 'trac.ini')) skip_defaults = options and ('inherit', 'file') in [(section, option) \ for (section, option, value) in options] self.setup_config(load_defaults=not skip_defaults) for section, name, value in options: self.config.set(section, name, value) self.config.save() self.config.parse_if_needed() # Full reload to get 'inherit' working # Create the database DatabaseManager(self).init_db() def get_version(self, db=None, initial=False): """Return the current version of the database. If the optional argument `initial` is set to `True`, the version of the database used at the time of creation will be returned. In practice, for database created before 0.11, this will return `False` which is "older" than any db version number. :since 0.11: """ if not db: db = self.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT value FROM system " "WHERE name='%sdatabase_version'" % (initial and 'initial_' or '')) row = cursor.fetchone() return row and int(row[0]) def setup_config(self, load_defaults=False): """Load the configuration file.""" self.config = Configuration(os.path.join(self.path, 'conf', 'trac.ini')) if load_defaults: for section, default_options in self.config.defaults().items(): for name, value in default_options.items(): if self.config.parent and name in self.config.parent[section]: value = None self.config.set(section, name, value) def get_templates_dir(self): """Return absolute path to the templates directory.""" return os.path.join(self.path, 'templates') def get_htdocs_dir(self): """Return absolute path to the htdocs directory.""" return os.path.join(self.path, 'htdocs') def get_log_dir(self): """Return absolute path to the log directory.""" return os.path.join(self.path, 'log') def setup_log(self): """Initialize the logging sub-system.""" from trac.log import logger_factory logtype = self.log_type logfile = self.log_file if logtype == 'file' and not os.path.isabs(logfile): logfile = os.path.join(self.get_log_dir(), logfile) format = self.log_format if format: format = format.replace('$(', '%(') \ .replace('%(path)s', self.path) \ .replace('%(basename)s', os.path.basename(self.path)) \ .replace('%(project)s', self.project_name) self.log = logger_factory(logtype, logfile, self.log_level, self.path, format=format) def get_known_users(self, cnx=None): """Generator that yields information about all known users, i.e. users that have logged in to this Trac environment and possibly set their name and email. This function generates one tuple for every user, of the form (username, name, email) ordered alpha-numerically by username. @param cnx: the database connection; if ommitted, a new connection is retrieved """ if not cnx: cnx = self.get_db_cnx() cursor = cnx.cursor() cursor.execute("SELECT DISTINCT s.sid, n.value, e.value " "FROM session AS s " " LEFT JOIN session_attribute AS n ON (n.sid=s.sid " " and n.authenticated=1 AND n.name = 'name') " " LEFT JOIN session_attribute AS e ON (e.sid=s.sid " " AND e.authenticated=1 AND e.name = 'email') " "WHERE s.authenticated=1 ORDER BY s.sid") for username,name,email in cursor: yield username, name, email def backup(self, dest=None): """Simple SQLite-specific backup of the database. @param dest: Destination file; if not specified, the backup is stored in a file called db_name.trac_version.bak """ import shutil db_str = self.config.get('trac', 'database') if not db_str.startswith('sqlite:'): raise TracError(_('Can only backup sqlite databases')) db_name = os.path.join(self.path, db_str[7:]) if not dest: dest = '%s.%i.bak' % (db_name, self.get_version()) shutil.copy (db_name, dest) def needs_upgrade(self): """Return whether the environment needs to be upgraded.""" db = self.get_db_cnx() for participant in self.setup_participants: if participant.environment_needs_upgrade(db): self.log.warning('Component %s requires environment upgrade', participant) return True return False def upgrade(self, backup=False, backup_dest=None): """Upgrade database. Each db version should have its own upgrade module, names upgrades/dbN.py, where 'N' is the version number (int). @param backup: whether or not to backup before upgrading @param backup_dest: name of the backup file @return: whether the upgrade was performed """ db = self.get_db_cnx() upgraders = [] for participant in self.setup_participants: if participant.environment_needs_upgrade(db): upgraders.append(participant) if not upgraders: return False if backup: self.backup(backup_dest) for participant in upgraders: participant.upgrade_environment(db) db.commit() # Database schema may have changed, so close all connections self.shutdown() return True def _get_href(self): if not self._href: self._href = Href(urlsplit(self.abs_href.base)[2]) return self._href href = property(_get_href, 'The application root path') def _get_abs_href(self): if not self._abs_href: if not self.base_url: self.log.warn('base_url option not set in configuration, ' 'generated links may be incorrect') self._abs_href = Href('') else: self._abs_href = Href(self.base_url) return self._abs_href abs_href = property(_get_abs_href, 'The application URL') class EnvironmentSetup(Component): implements(IEnvironmentSetupParticipant) # IEnvironmentSetupParticipant methods def environment_created(self): """Insert default data into the database.""" db = self.env.get_db_cnx() cursor = db.cursor() for table, cols, vals in db_default.get_data(db): cursor.executemany("INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s' for c in cols])), vals) db.commit() self._update_sample_config() def environment_needs_upgrade(self, db): dbver = self.env.get_version(db) if dbver == db_default.db_version: return False elif dbver > db_default.db_version: raise TracError(_('Database newer than Trac version')) return True def upgrade_environment(self, db): cursor = db.cursor() dbver = self.env.get_version() for i in range(dbver + 1, db_default.db_version + 1): name = 'db%i' % i try: upgrades = __import__('upgrades', globals(), locals(), [name]) script = getattr(upgrades, name) except AttributeError: raise TracError(_('No upgrade module for version %(num)i ' '(%(version)s.py)', num=i, version=name)) script.do_upgrade(self.env, i, cursor) cursor.execute("UPDATE system SET value=%s WHERE " "name='database_version'", (db_default.db_version,)) self.log.info('Upgraded database version from %d to %d', dbver, db_default.db_version) self._update_sample_config() # Internal methods def _update_sample_config(self): filename = os.path.join(self.env.path, 'conf', 'trac.ini.sample') config = Configuration(filename) for section, default_options in config.defaults().iteritems(): for name, value in default_options.iteritems(): config.set(section, name, value) try: config.save() self.log.info('Wrote sample configuration file with the new ' 'settings and their default values: %s', filename) except IOError, e: self.log.warn('Couldn\'t write sample configuration file (%s)', e, exc_info=True) env_cache = {} env_cache_lock = threading.Lock() def open_environment(env_path=None, use_cache=False): """Open an existing environment object, and verify that the database is up to date. @param env_path: absolute path to the environment directory; if ommitted, the value of the `TRAC_ENV` environment variable is used @param use_cache: whether the environment should be cached for subsequent invocations of this function @return: the `Environment` object """ global env_cache, env_cache_lock if not env_path: env_path = os.getenv('TRAC_ENV') if not env_path: raise TracError(_('Missing environment variable "TRAC_ENV". ' 'Trac requires this variable to point to a valid ' 'Trac environment.')) env_path = os.path.normcase(os.path.normpath(env_path)) if use_cache: env_cache_lock.acquire() try: env = env_cache.get(env_path) if env and env.config.parse_if_needed(): # The environment configuration has changed, so shut it down # and remove it from the cache so that it gets reinitialized env.log.info('Reloading environment due to configuration ' 'change') env.shutdown() if hasattr(env.log, '_trac_handler'): hdlr = env.log._trac_handler env.log.removeHandler(hdlr) hdlr.close() del env_cache[env_path] env = None if env is None: env = env_cache.setdefault(env_path, open_environment(env_path)) finally: env_cache_lock.release() else: env = Environment(env_path) needs_upgrade = False try: needs_upgrade = env.needs_upgrade() except Exception, e: # e.g. no database connection env.log.exception(e) if needs_upgrade: raise TracError(_('The Trac Environment needs to be upgraded.\n\n' 'Run "trac-admin %(path)s upgrade"', path=env_path)) return env PKmh9{ N7k7k trac/env.pyc; Ic@sMdkZy dkZWnej odkZnXdkZdkZdklZdkl Z dk Tdk l Z l Z lZlZlZlZdklZdklZdklZdklZd klZd d d gZd efd YZd e e fdYZde fdYZha ei!a"e#e$dZ%dS(N(surlsplit(s db_default(s*(s ComponentsComponentManagers implementss InterfacesExtensionPoints TracError(sDatabaseManager(s get_pkginfo(s_(sRepositoryManager(sHrefs EnvironmentsIEnvironmentSetupParticipantsopen_environmentcBs)tZdZdZdZdZRS(sExtension point interface for components that need to participate in the creation and upgrading of Trac environments, for example to create additional database tables.cCsdS(s.Called when a new Trac environment is created.N((((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysenvironment_created,scCsdS(sCalled when Trac checks whether the environment needs to be upgraded. Should return `True` if this participant needs an upgrade to be performed, `False` otherwise. N((sdb((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysenvironment_needs_upgrade/scCsdS(sActually perform an environment upgrade. Implementations of this method should not commit any database transactions. This is done implicitly after all participants have performed the upgrades they need without an error being raised. N((sdb((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysupgrade_environment6s(s__name__s __module__s__doc__senvironment_createdsenvironment_needs_upgradesupgrade_environment(((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysIEnvironmentSetupParticipant's   cBs6tZdZeeZeddddZeddddZ e dde d Z e dd e d Z ed d ddZed dddZed dddZed dddZed dddZed dddZedddd Zedd!d"d#Zedd$d%d&Zedd'ed(Ze gd)Zd*Zd+Zd,Zd-Zed.Zed/Zgd0Z ee d1Z!e d2Z"d3Z#d4Z$d5Z%d6Z&ed7Z'ed8Z(d9Z)e ed:Z*d;Z+e,e+d<Z-d=Z.e,e.d>Z/RS(?sBTrac stores project information in a Trac environment. A Trac environment consists of a directory structure containing among other things: * a configuration file. * an SQLite database (stores tickets, wiki pages...) * Project specific templates and plugins. * wiki and ticket attachments. sinherits plugins_dirssPath of the directory containing additional plugins. Plugins in that directory are loaded in addition to those in the directory of the environment `plugins`, with this one taking precedence. (''since 0.11'')stracsbase_urlsReference URL for the Trac deployment. This is the base URL that will be used when producing documents that will be used outside of the web browsing context, like for example when inserting URLs pointing to Trac resources in notification e-mails.suse_base_url_for_redirectsOptionally use `[trac] base_url` for redirects. In some configurations, usually involving running Trac behind a HTTP proxy, Trac can't automatically reconstruct the URL that is used to access it. You may need to use this option to force Trac to use the `base_url` setting also for redirects. This introduces the obvious limitation that this environment will only be usable when accessible from that URL, as redirects are frequently used. ''(since 0.10.5)''ssecure_cookiess Restrict cookies to HTTPS connections. When true, set the `secure` flag on all cookies so that they are only sent to the server on HTTPS connections. Use this if your Trac instance is only accessible through HTTPS. (''since 0.11.2'')sprojectsnames My ProjectsName of the project.sdescrsMy example projects!Short description of the project.surls^URL of the main project web site, usually the website in which the `base_url` resides.sadmins.E-Mail address of the project's administrator.sfooterslVisit the Trac open source project at
http://trac.edgewall.org/s!Page footer text (right-aligned).siconscommon/trac.icosURL of the icon of the project.sloggingslog_typesnonesjLogging facility to use. Should be one of (`none`, `file`, `stderr`, `syslog`, `winlog`).slog_filestrac.logs?If `log_type` is `file`, this should be a path to the log-file.s log_levelsDEBUGslLevel of verbosity in log. Should be one of (`CRITICAL`, `ERROR`, `WARN`, `INFO`, `DEBUG`).s log_formatsCustom logging format. If nothing is set, the following will be used: Trac[$(module)s] $(levelname)s: $(message)s In addition to regular key names supported by the Python logger library library (see http://docs.python.org/lib/node422.html), one could use: - $(path)s the path for the current environment - $(basename)s the last path component of the current environment - $(project)s the project name Note the usage of `$(...)s` instead of `%(...)s` as the latter form would be interpreted by the ConfigParser itself. Example: ($(thread)d) Trac[$(basename)s:$(module)s] $(levelname)s: $(message)s (since 0.10.5)c Csti|||_|id||idkl}l }dt |i d|fdt ifdti fg|_t|_|_dkl}|ii dd }|||o|f|o|i|n |i|o"x|iD]}|iqWnd S( sInitialize the Trac environment. @param path: the absolute path to the Trac environment @param create: if `True`, the environment is created and populated with default data; otherwise, the environment is expected to already exist. @param options: A list of `(section, name, value)` tuples that define configuration options s load_defaults(scores __version__sTracsversionsPythons setuptools(sload_componentssinherits plugins_dirN(sComponentManagers__init__sselfspaths setup_configscreates setup_logstracscores __version__sVERSIONs get_pkginfosgetssyssversions setuptoolss systeminfosNones_hrefs _abs_hrefs trac.loadersload_componentssconfigs plugins_dirsoptionssverifyssetup_participantsssetup_participantsenvironment_created( sselfspathscreatesoptionsscoressetup_participantsVERSIONsload_componentss plugins_dir((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys__init__s$    <   cCs%||_|i|_|i|_dS(sInitialize additional member variables for components. Every component activated through the `Environment` object gets three member variables: `env` (the environment object), `config` (the environment configuration) and `log` (a logger object).N(sselfs componentsenvsconfigslog(sselfs component((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pyscomponent_activateds  c CsXt|t o|id|ii}n |i}gi}|i i dD]4\}}||i|iddfjfqU~}|idxu|D]m\}}||jp!|ido|i|d o0|ido|iid tSn|SqqW|id o|i id d d jSn|idSdS(s*Implemented to only allow activation of components that are not disabled in the configuration. This is called by the `ComponentManager` base class when a component is about to be activated. If this method returns false, the component does not get activated.s.s componentssenabledsoncCs"tt|dt|d S(Ni(scmpslensasb(sasb((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysss*is webadmin.sThe legacy TracWebAdmin plugin has been automatically disabled, and the integrated administration interface will be used instead.strac.versioncontrol.stracsrepository_dirsstrac.N(s isinstancesclss basestrings __module__s__name__slowerscomponent_namesappends_[1]sselfsconfigsoptionssnamesvaluesrulesssortspatternsenabledsendswiths startswithslogsinfosFalsesget( sselfsclssnamesrulesspatternsenabledsvalues_[1]scomponent_name((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysis_component_enableds  W 1 cCsTttii|idd}z!|iddjptWd|iXdS(sSVerify that the provided path points to a valid Trac environment directory.sVERSIONsrisTrac Environment Version 1N( sopensosspathsjoinsselfsfdsreadsAssertionErrorsclose(sselfsfd((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysverifys !!cCst|iSdS(s6Return a database connection from the connection pool.N(sDatabaseManagersselfsget_connection(sself((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys get_db_cnxscCs*t|i|t|i|dS(sClose the environment.N(sRepositoryManagersselfsshutdownstidsDatabaseManager(sselfstid((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysshutdown scCst|i|SdS(sReturn the version control repository configured for this environment. @param authname: user name for authorization N(sRepositoryManagersselfsget_repositorysauthname(sselfsauthname((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysget_repositorysc Cstd}tii|i oti|inti|iti|ititii |id|tii |idd|tii |iddtitii |id|tii |idd|o@d d fgi }|D]\}}}|||fq~j}|id | x-|D]%\}}}|ii|||qYW|ii|iit|id S( s7Create the basic directory structure of the environment, initialize the database and populate the configuration file with default values. If options contains ('inherit', 'file'), default values will not be loaded; they are expected to be provided by that file or other options. cCs5t|d}|o|i|n|idS(Nsw(sopensfnamesfdsdataswritesclose(sfnamesdatasfd((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys _create_filesspluginssVERSIONsTrac Environment Version 1 sREADMEsbThis directory contains a Trac environment. Visit http://trac.edgewall.org/ for more information. sconfstrac.inisinheritsfiles load_defaultsN(sNones _create_filesosspathsexistssselfsmkdirs get_log_dirsget_htdocs_dirsjoinsoptionssappends_[1]ssectionsoptionsvalues skip_defaultss setup_configsnamesconfigssetssavesparse_if_neededsDatabaseManagersinit_db( sselfsoptionss _create_filesoptions skip_defaultssvalues_[1]ssectionsname((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pyscreates* I  cCsh| o|i}n|i}|id|odpd|i}|ot|dSdS(s`Return the current version of the database. If the optional argument `initial` is set to `True`, the version of the database used at the time of creation will be returned. In practice, for database created before 0.11, this will return `False` which is "older" than any db version number. :since 0.11: s8SELECT value FROM system WHERE name='%sdatabase_version'sinitial_siN( sdbsselfs get_db_cnxscursorsexecutesinitialsfetchonesrowsint(sselfsdbsinitialscursorsrow((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys get_version@s   cCsttii|idd|_|ox|iiiD]m\}}x^|iD]P\}}|ii o||ii |jo t}n|ii|||qZWqAWndS(sLoad the configuration file.sconfstrac.iniN(s Configurationsosspathsjoinsselfsconfigs load_defaultssdefaultssitemsssectionsdefault_optionssnamesvaluesparentsNonesset(sselfs load_defaultssnamesvaluesdefault_optionsssection((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys setup_configSs$  $ cCstii|idSdS(s0Return absolute path to the templates directory.s templatesN(sosspathsjoinsself(sself((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysget_templates_dir]scCstii|idSdS(s-Return absolute path to the htdocs directory.shtdocsN(sosspathsjoinsself(sself((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysget_htdocs_dirascCstii|idSdS(s*Return absolute path to the log directory.slogN(sosspathsjoinsself(sself((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys get_log_direscCsdkl}|i}|i}|djotii | otii |i |}n|i }|oO|iddid|iidtii|iid|i}n||||i|id||_d S( s"Initialize the logging sub-system.(slogger_factorysfiles$(s%(s%(path)ss %(basename)ss %(project)ssformatN(strac.logslogger_factorysselfslog_typeslogtypeslog_fileslogfilesosspathsisabssjoins get_log_dirs log_formatsformatsreplacesbasenames project_names log_levelslog(sselfsformatslogtypeslogfileslogger_factory((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys setup_logis   ! Occs\| o|i}n|i}|idx$|D]\}}}|||fVq8WdS(sGenerator that yields information about all known users, i.e. users that have logged in to this Trac environment and possibly set their name and email. This function generates one tuple for every user, of the form (username, name, email) ordered alpha-numerically by username. @param cnx: the database connection; if ommitted, a new connection is retrieved sSELECT DISTINCT s.sid, n.value, e.value FROM session AS s LEFT JOIN session_attribute AS n ON (n.sid=s.sid and n.authenticated=1 AND n.name = 'name') LEFT JOIN session_attribute AS e ON (e.sid=s.sid AND e.authenticated=1 AND e.name = 'email') WHERE s.authenticated=1 ORDER BY s.sidN(scnxsselfs get_db_cnxscursorsexecutesusernamesnamesemail(sselfscnxsusernamesnamescursorsemail((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysget_known_usersys   cCsdk}|iidd}|id ottdnti i |i |d}| od||i f}n|i||dS(sSimple SQLite-specific backup of the database. @param dest: Destination file; if not specified, the backup is stored in a file called db_name.trac_version.bak Nstracsdatabasessqlite:s Can only backup sqlite databasesis %s.%i.bak(sshutilsselfsconfigsgetsdb_strs startswiths TracErrors_sosspathsjoinsdb_namesdests get_versionscopy(sselfsdestsdb_strsdb_namesshutil((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysbackups cCsS|i}x<|iD]1}|i|o|iid|tSqqWt SdS(s4Return whether the environment needs to be upgraded.s)Component %s requires environment upgradeN( sselfs get_db_cnxsdbssetup_participantss participantsenvironment_needs_upgradeslogswarningsTruesFalse(sselfs participantsdb((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys needs_upgrades    cCs|i}g}x2|iD]'}|i|o|i|qqW| otSn|o|i |nx|D]}|i |qvW|i |i tSdS(sNUpgrade database. Each db version should have its own upgrade module, names upgrades/dbN.py, where 'N' is the version number (int). @param backup: whether or not to backup before upgrading @param backup_dest: name of the backup file @return: whether the upgrade was performed N(sselfs get_db_cnxsdbs upgradersssetup_participantss participantsenvironment_needs_upgradesappendsFalsesbackups backup_destsupgrade_environmentscommitsshutdownsTrue(sselfsbackups backup_dests upgraderss participantsdb((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysupgrades"     cCs9|i o#tt|iid|_n|iSdS(Ni(sselfs_hrefsHrefsurlsplitsabs_hrefsbase(sself((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys _get_hrefs #sThe application root pathcCsZ|i oD|i o#|iidtd|_qOt|i|_n|iSdS(NsJbase_url option not set in configuration, generated links may be incorrects(sselfs _abs_hrefsbase_urlslogswarnsHref(sself((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys _get_abs_hrefs   sThe application URL(0s__name__s __module__s__doc__sExtensionPointsIEnvironmentSetupParticipantssetup_participantss PathOptionsshared_plugins_dirsOptionsbase_urls BoolOptionsFalsesbase_url_for_redirectssecure_cookiess project_namesproject_descriptions project_urls project_adminsproject_footers project_iconslog_typeslog_files log_levelsNones log_formats__init__scomponent_activatedsis_component_enabledsverifys get_db_cnxsshutdownsget_repositoryscreates get_versions setup_configsget_templates_dirsget_htdocs_dirs get_log_dirs setup_logsget_known_userssbackups needs_upgradesupgrades _get_hrefspropertyshrefs _get_abs_hrefsabs_href(((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys Environment?sl                              % (    )         sEnvironmentSetupcBs6tZeedZdZdZdZRS(Nc Cs|ii}|i}xpti|D]_\}}}|i d|di |di gi }|D]}|dqf~f|q+W|i|idS(s&Insert default data into the database.sINSERT INTO %s (%s) VALUES (%s)s,s%sN(sselfsenvs get_db_cnxsdbscursors db_defaultsget_datastablescolssvalss executemanysjoinsappends_[1]scscommits_update_sample_config(sselfs_[1]scsdbscolsscursorsvalsstable((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysenvironment_createds I  cCsX|ii|}|tijotSn'|tijott dnt SdS(Ns Database newer than Trac version( sselfsenvs get_versionsdbsdbvers db_defaults db_versionsFalses TracErrors_sTrue(sselfsdbsdbver((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysenvironment_needs_upgrades cCs|i}|ii}xt|dtidD]}d|}y1t dt t |g}t||}Wn1tj o%ttdd|d|nX|i|i||q6W|idtif|iid|ti|idS( Nisdb%isupgradess6No upgrade module for version %(num)i (%(version)s.py)snumsversions8UPDATE system SET value=%s WHERE name='database_version's'Upgraded database version from %d to %d(sdbscursorsselfsenvs get_versionsdbversranges db_defaults db_versionsisnames __import__sglobalsslocalssupgradessgetattrsscriptsAttributeErrors TracErrors_s do_upgradesexecuteslogsinfos_update_sample_config(sselfsdbsdbversnamesscriptsiscursorsupgrades((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysupgrade_environments"       cCstii|iidd}t|}xP|ii D]<\}}x-|i D]\}}|i|||qVWq=Wy!|i|iid|Wn.tj o"}|iid|dtnXdS(Nsconfstrac.ini.samplesRWrote sample configuration file with the new settings and their default values: %ss-Couldn't write sample configuration file (%s)sexc_info(sosspathsjoinsselfsenvsfilenames Configurationsconfigsdefaultss iteritemsssectionsdefault_optionssnamesvaluessetssaveslogsinfosIOErrorseswarnsTrue(sselfsesnamesvaluesfilenamesdefault_optionssconfigssection((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys_update_sample_configs      (s__name__s __module__s implementssIEnvironmentSetupParticipantsenvironment_createdsenvironment_needs_upgradesupgrade_environments_update_sample_config(((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysEnvironmentSetups    cCs| otid}n| ottdntiitii|}|ot i zt i |}|o |iioh|iid|it|ido*|ii}|ii||int |=t}n|tjot i|t|}nWdt iXnnt|}t}y|i}Wn%tj o}|ii!|nX|ottdd|n|SdS(sOpen an existing environment object, and verify that the database is up to date. @param env_path: absolute path to the environment directory; if ommitted, the value of the `TRAC_ENV` environment variable is used @param use_cache: whether the environment should be cached for subsequent invocations of this function @return: the `Environment` object sTRAC_ENVsjMissing environment variable "TRAC_ENV". Trac requires this variable to point to a valid Trac environment.s1Reloading environment due to configuration changes _trac_handlerNsMThe Trac Environment needs to be upgraded. Run "trac-admin %(path)s upgrade"spath("senv_pathsossgetenvs TracErrors_spathsnormcasesnormpaths use_cachesenv_cache_locksacquires env_cachesgetsenvsconfigsparse_if_neededslogsinfosshutdownshasattrs _trac_handlershdlrs removeHandlersclosesNones setdefaultsopen_environmentsreleases EnvironmentsFalses needs_upgrades Exceptionses exception(senv_paths use_caches needs_upgradesenvseshdlr((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pysopen_environmentsD         (&soss threadings ImportErrorsdummy_threadings setuptoolsssyssurlparsesurlsplitstracs db_defaults trac.configs trac.cores ComponentsComponentManagers implementss InterfacesExtensionPoints TracErrorstrac.dbsDatabaseManagers trac.utils get_pkginfostrac.util.translations_strac.versioncontrolsRepositoryManagers trac.web.hrefsHrefs__all__sIEnvironmentSetupParticipants EnvironmentsEnvironmentSetups env_cachesLocksenv_cache_locksNonesFalsesopen_environment(sEnvironmentSetups TracErrors get_pkginfos EnvironmentsRepositoryManagersExtensionPointsurlsplits__all__s ComponentsHrefsopen_environments InterfacesComponentManagerssyssIEnvironmentSetupParticipants_s implementss threadings db_defaultsDatabaseManagers setuptoolssos((s-build/bdist.darwin-9.5.0-i386/egg/trac/env.pys?s.      +     < PKkh9UNhhtrac/loader.py# -*- coding: utf-8 -*- # # Copyright (C) 2005-2008 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Christopher Lenz from glob import glob import imp import pkg_resources from pkg_resources import working_set, DistributionNotFound, VersionConflict, \ UnknownExtra import os import sys from trac.util.compat import set __all__ = ['load_components'] def _enable_plugin(env, module): """Enable the given plugin module by adding an entry to the configuration. """ if module + '.*' not in env.config['components']: env.config['components'].set(module + '.*', 'enabled') def load_eggs(entry_point_name): """Loader that loads any eggs on the search path and `sys.path`.""" def _load_eggs(env, search_path, auto_enable=None): # Note that the following doesn't seem to support unicode search_path distributions, errors = working_set.find_plugins( pkg_resources.Environment(search_path) ) for dist in distributions: env.log.debug('Adding plugin %s from %s', dist, dist.location) working_set.add(dist) def _log_error(item, e): if isinstance(e, DistributionNotFound): env.log.debug('Skipping "%s": ("%s" not found)', item, e) elif isinstance(e, VersionConflict): env.log.error('Skipping "%s": (version conflict "%s")', item, e) elif isinstance(e, UnknownExtra): env.log.error('Skipping "%s": (unknown extra "%s")', item, e) elif isinstance(e, ImportError): env.log.error('Skipping "%s": (can\'t import "%s")', item, e) else: env.log.error('Skipping "%s": (error "%s")', item, e) for dist, e in errors.iteritems(): _log_error(dist, e) for entry in working_set.iter_entry_points(entry_point_name): env.log.debug('Loading %s from %s', entry.name, entry.dist.location) try: entry.load(require=True) except (ImportError, DistributionNotFound, VersionConflict, UnknownExtra), e: _log_error(entry, e) else: if os.path.dirname(entry.dist.location) == auto_enable: _enable_plugin(env, entry.module_name) return _load_eggs def load_py_files(): """Loader that look for Python source files in the plugins directories, which simply get imported, thereby registering them with the component manager if they define any components. """ def _load_py_files(env, search_path, auto_enable=None): for path in search_path: plugin_files = glob(os.path.join(path, '*.py')) for plugin_file in plugin_files: try: plugin_name = os.path.basename(plugin_file[:-3]) env.log.debug('Loading file plugin %s from %s' % \ (plugin_name, plugin_file)) if plugin_name not in sys.modules: module = imp.load_source(plugin_name, plugin_file) if path == auto_enable: _enable_plugin(env, plugin_name) except Exception, e: env.log.error('Failed to load plugin from %s', plugin_file, exc_info=True) return _load_py_files def load_components(env, extra_path=None, loaders=(load_eggs('trac.plugins'), load_py_files())): """Load all plugin components found on the given search path.""" plugins_dir = os.path.normcase(os.path.realpath( os.path.join(env.path, 'plugins') )) search_path = [plugins_dir] if extra_path: search_path += list(extra_path) for loadfunc in loaders: loadfunc(env, search_path, auto_enable=plugins_dir) PKmh9+@/trac/loader.pyc; Ic@sdklZdkZdkZdklZlZlZlZdkZdkZdk l Z dgZ dZ dZ dZee defd ZdS( (sglobN(s working_setsDistributionNotFoundsVersionConflicts UnknownExtra(ssetsload_componentscCs;|d|idjo|idi|ddndS(sLEnable the given plugin module by adding an entry to the configuration. s.*s componentssenabledN(smodulesenvsconfigsset(senvsmodule((s0build/bdist.darwin-9.5.0-i386/egg/trac/loader.pys_enable_pluginscstd}|SdS(s=Loader that loads any eggs on the search path and `sys.path`.c s@titi|\}}x4|D],}i i d||i ti |q%Wd}x'|iD]\}}|||qnWxtiD]}i i d|i|ii y|idtWn.ttttfj o}|||qXtii|ii |jot|iqqWdS(NsAdding plugin %s from %scst|toiid||nt|toiid||nkt|t oiid||nAt|t oiid||niid||dS(NsSkipping "%s": ("%s" not found)s&Skipping "%s": (version conflict "%s")s#Skipping "%s": (unknown extra "%s")s"Skipping "%s": (can't import "%s")sSkipping "%s": (error "%s")( s isinstancesesDistributionNotFoundsenvslogsdebugsitemsVersionConflictserrors UnknownExtras ImportError(sitemse(senv(s0build/bdist.darwin-9.5.0-i386/egg/trac/loader.pys _log_error.s sLoading %s from %ssrequire( s working_sets find_pluginss pkg_resourcess Environments search_paths distributionsserrorssdistsenvslogsdebugslocationsadds _log_errors iteritemssesiter_entry_pointssentry_point_namesentrysnamesloadsTrues ImportErrorsDistributionNotFoundsVersionConflicts UnknownExtrasosspathsdirnames auto_enables_enable_plugins module_name( senvs search_paths auto_enableserrorssdists _log_errorsentryses distributions(sentry_point_name(senvs0build/bdist.darwin-9.5.0-i386/egg/trac/loader.pys _load_eggs%s&  N(sNones _load_eggs(sentry_point_names _load_eggs((sentry_point_names0build/bdist.darwin-9.5.0-i386/egg/trac/loader.pys load_eggs#s$cCstd}|SdS(sLoader that look for Python source files in the plugins directories, which simply get imported, thereby registering them with the component manager if they define any components. c Csx|D]}ttii|d}x|D]}yxtii|d }|i i d||f|t i joti||}n||jot||nWq/tj o"}|i id|dtq/Xq/WqWdS(Ns*.pyisLoading file plugin %s from %ssFailed to load plugin from %ssexc_info(s search_pathspathsglobsossjoins plugin_filess plugin_filesbasenames plugin_namesenvslogsdebugssyssmodulessimps load_sourcesmodules auto_enables_enable_plugins ExceptionseserrorsTrue( senvs search_paths auto_enableses plugin_names plugin_filesmodulespaths plugin_files((s0build/bdist.darwin-9.5.0-i386/egg/trac/loader.pys_load_py_filesPs N(sNones_load_py_files(s_load_py_files((s0build/bdist.darwin-9.5.0-i386/egg/trac/loader.pys load_py_filesKs s trac.pluginscCs|tiitiitii|id}|g}|o|t |7}nx!|D]}|||d|q[WdS(s:Load all plugin components found on the given search path.spluginss auto_enableN( sosspathsnormcasesrealpathsjoinsenvs plugins_dirs search_paths extra_pathslistsloaderssloadfunc(senvs extra_pathsloaderssloadfuncs search_paths plugins_dir((s0build/bdist.darwin-9.5.0-i386/egg/trac/loader.pysload_componentsbs0 (sglobsimps pkg_resourcess working_setsDistributionNotFoundsVersionConflicts UnknownExtrasosssysstrac.util.compatssets__all__s_enable_plugins load_eggss load_py_filessNonesload_components(s load_eggsssets__all__sglobsVersionConflicts_enable_plugins pkg_resourcesssyssimps UnknownExtrasload_componentss load_py_filess working_setsossDistributionNotFound((s0build/bdist.darwin-9.5.0-i386/egg/trac/loader.pys?s         ( PKkh9.{'Z Z trac/log.py# -*- coding: utf-8 -*- # # Copyright (C) 2003-2008 Edgewall Software # Copyright (C) 2003-2005 Daniel Lundin # Copyright (C) 2006 Christian Boos # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Daniel Lundin import logging import logging.handlers import sys def logger_factory(logtype='syslog', logfile=None, level='WARNING', logid='Trac', format=None): logger = logging.getLogger(logid) logtype = logtype.lower() if logtype == 'file': hdlr = logging.FileHandler(logfile) elif logtype in ('winlog', 'eventlog', 'nteventlog'): # Requires win32 extensions hdlr = logging.handlers.NTEventLogHandler(logid, logtype='Application') elif logtype in ('syslog', 'unix'): hdlr = logging.handlers.SysLogHandler('/dev/log') elif logtype in ('stderr'): hdlr = logging.StreamHandler(sys.stderr) else: hdlr = logging.handlers.BufferingHandler(0) # Note: this _really_ throws away log events, as a `MemoryHandler` # would keep _all_ records in case there's no target handler (a bug?) if not format: format = 'Trac[%(module)s] %(levelname)s: %(message)s' if logtype in ('file', 'stderr'): format = '%(asctime)s ' + format datefmt = '' if logtype == 'stderr': datefmt = '%X' level = level.upper() if level in ('DEBUG', 'ALL'): logger.setLevel(logging.DEBUG) elif level == 'INFO': logger.setLevel(logging.INFO) elif level == 'ERROR': logger.setLevel(logging.ERROR) elif level == 'CRITICAL': logger.setLevel(logging.CRITICAL) else: logger.setLevel(logging.WARNING) formatter = logging.Formatter(format, datefmt) hdlr.setFormatter(formatter) logger.addHandler(hdlr) # Remember our handler so that we can remove it later logger._trac_handler = hdlr return logger PKmh9.wK trac/log.pyc; Ic@s7dkZdkZdkZdeddedZdS(NssyslogsWARNINGsTracc Csti|}|i}|djoti|}n|dddfjoti i |dd}n_|ddfjoti i d }n6|d joti t i}nti id }| o+d }|dd fjod |}qnd}|d jo d}n|i}|ddfjo|itint|djo|itinS|djo|itin2|djo|itin|ititi||}|i||i|||_|SdS(Nsfileswinlogseventlogs nteventlogslogtypes Applicationssyslogsunixs/dev/logsstderris+Trac[%(module)s] %(levelname)s: %(message)ss %(asctime)s ss%XsDEBUGsALLsINFOsERRORsCRITICAL(sloggings getLoggerslogidsloggerslogtypeslowers FileHandlerslogfileshdlrshandlerssNTEventLogHandlers SysLogHandlers StreamHandlerssyssstderrsBufferingHandlersformatsdatefmtslevelsupperssetLevelsDEBUGsINFOsERRORsCRITICALsWARNINGs Formatters formatters setFormatters addHandlers _trac_handler( slogtypeslogfileslevelslogidsformatsdatefmts formattersloggershdlr((s-build/bdist.darwin-9.5.0-i386/egg/trac/log.pyslogger_factorysD             (sloggingslogging.handlersssyssNoneslogger_factory(ssyssloggingslogger_factory((s-build/bdist.darwin-9.5.0-i386/egg/trac/log.pys?s   PKkh9\CCtrac/notification.py# -*- coding: utf-8 -*- # # Copyright (C) 2003-2008 Edgewall Software # Copyright (C) 2003-2005 Daniel Lundin # Copyright (C) 2005-2006 Emmanuel Blot # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. import time import smtplib import re from genshi.builder import tag from trac import __version__ from trac.config import BoolOption, IntOption, Option from trac.core import * from trac.util.text import CRLF from trac.util.translation import _ MAXHEADERLEN = 76 EMAIL_LOOKALIKE_PATTERN = ( # the local part r"[a-zA-Z0-9.'=+_-]+" '@' # the domain name part (RFC:1035) '(?:[a-zA-Z0-9_-]+\.)+' # labels (but also allow '_') '[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?' # TLD ) class NotificationSystem(Component): smtp_enabled = BoolOption('notification', 'smtp_enabled', 'false', """Enable SMTP (email) notification.""") smtp_server = Option('notification', 'smtp_server', 'localhost', """SMTP server hostname to use for email notifications.""") smtp_port = IntOption('notification', 'smtp_port', 25, """SMTP server port to use for email notification.""") smtp_user = Option('notification', 'smtp_user', '', """Username for SMTP server. (''since 0.9'').""") smtp_password = Option('notification', 'smtp_password', '', """Password for SMTP server. (''since 0.9'').""") smtp_from = Option('notification', 'smtp_from', 'trac@localhost', """Sender address to use in notification emails.""") smtp_from_name = Option('notification', 'smtp_from_name', '', """Sender name to use in notification emails.""") smtp_replyto = Option('notification', 'smtp_replyto', 'trac@localhost', """Reply-To address to use in notification emails.""") smtp_always_cc = Option('notification', 'smtp_always_cc', '', """Email address(es) to always send notifications to, addresses can be seen by all recipients (Cc:).""") smtp_always_bcc = Option('notification', 'smtp_always_bcc', '', """Email address(es) to always send notifications to, addresses do not appear publicly (Bcc:). (''since 0.10'').""") smtp_default_domain = Option('notification', 'smtp_default_domain', '', """Default host/domain to append to address that do not specify one""") ignore_domains = Option('notification', 'ignore_domains', '', """Comma-separated list of domains that should not be considered part of email addresses (for usernames with Kerberos domains)""") admit_domains = Option('notification', 'admit_domains', '', """Comma-separated list of domains that should be considered as valid for email addresses (such as localdomain)""") mime_encoding = Option('notification', 'mime_encoding', 'base64', """Specifies the MIME encoding scheme for emails. Valid options are 'base64' for Base64 encoding, 'qp' for Quoted-Printable, and 'none' for no encoding. Note that the no encoding means that non-ASCII characters in text are going to cause problems with notifications (''since 0.10'').""") use_public_cc = BoolOption('notification', 'use_public_cc', 'false', """Recipients can see email addresses of other CC'ed recipients. If this option is disabled (the default), recipients are put on BCC (''since 0.10'').""") use_short_addr = BoolOption('notification', 'use_short_addr', 'false', """Permit email address without a host/domain (i.e. username only) The SMTP server should accept those addresses, and either append a FQDN or use local delivery (''since 0.10'').""") use_tls = BoolOption('notification', 'use_tls', 'false', """Use SSL/TLS to send notifications (''since 0.10'').""") smtp_subject_prefix = Option('notification', 'smtp_subject_prefix', '__default__', """Text to prepend to subject line of notification emails. If the setting is not defined, then the [$project_name] prefix. If no prefix is desired, then specifying an empty option will disable it.(''since 0.10.1'').""") class Notify(object): """Generic notification class for Trac. Subclass this to implement different methods. """ def __init__(self, env): self.env = env self.config = env.config self.db = env.get_db_cnx() from trac.web.chrome import Chrome self.template = Chrome(self.env).load_template(self.template_name, method='text') # FIXME: actually, we would need a Context with a different # PermissionCache for each recipient self.data = Chrome(self.env).populate_data(None, {'CRLF': CRLF}) def notify(self, resid): (torcpts, ccrcpts) = self.get_recipients(resid) self.begin_send() self.send(torcpts, ccrcpts) self.finish_send() def get_recipients(self, resid): """Return a pair of list of subscribers to the resource 'resid'. First list represents the direct recipients (To:), second list represents the recipients in carbon copy (Cc:). """ raise NotImplementedError def begin_send(self): """Prepare to send messages. Called before sending begins. """ def send(self, torcpts, ccrcpts): """Send message to recipients.""" raise NotImplementedError def finish_send(self): """Clean up after sending all messages. Called after sending all messages. """ class NotifyEmail(Notify): """Baseclass for notification by email.""" smtp_server = 'localhost' smtp_port = 25 from_email = 'trac+tickets@localhost' subject = '' template_name = None nodomaddr_re = re.compile(r'[\w\d_\.\-]+') addrsep_re = re.compile(r'[;\s,]+') def __init__(self, env): global EMAIL_LOOKALIKE_PATTERN Notify.__init__(self, env) addrfmt = EMAIL_LOOKALIKE_PATTERN admit_domains = self.env.config.get('notification', 'admit_domains') if admit_domains: pos = addrfmt.find('@') domains = '|'.join([x.strip() for x in \ admit_domains.replace('.','\.').split(',')]) addrfmt = r'%s@(?:(?:%s)|%s)' % (addrfmt[:pos], addrfmt[pos+1:], domains) self.shortaddr_re = re.compile(r'%s$' % addrfmt) self.longaddr_re = re.compile(r'^\s*(.*)\s+<(%s)>\s*$' % addrfmt); self._use_tls = self.env.config.getbool('notification', 'use_tls') self._init_pref_encoding() domains = self.env.config.get('notification', 'ignore_domains', '') self._ignore_domains = [x.strip() for x in domains.lower().split(',')] # Get the email addresses of all known users self.email_map = {} for username, name, email in self.env.get_known_users(self.db): if email: self.email_map[username] = email def _init_pref_encoding(self): from email.Charset import Charset, QP, BASE64 self._charset = Charset() self._charset.input_charset = 'utf-8' pref = self.env.config.get('notification', 'mime_encoding').lower() if pref == 'base64': self._charset.header_encoding = BASE64 self._charset.body_encoding = BASE64 self._charset.output_charset = 'utf-8' self._charset.input_codec = 'utf-8' self._charset.output_codec = 'utf-8' elif pref in ['qp', 'quoted-printable']: self._charset.header_encoding = QP self._charset.body_encoding = QP self._charset.output_charset = 'utf-8' self._charset.input_codec = 'utf-8' self._charset.output_codec = 'utf-8' elif pref == 'none': self._charset.header_encoding = None self._charset.body_encoding = None self._charset.input_codec = None self._charset.output_charset = 'ascii' else: raise TracError(_('Invalid email encoding setting: %s' % pref)) def notify(self, resid, subject): self.subject = subject if not self.config.getbool('notification', 'smtp_enabled'): return self.smtp_server = self.config['notification'].get('smtp_server') self.smtp_port = self.config['notification'].getint('smtp_port') self.from_email = self.config['notification'].get('smtp_from') self.from_name = self.config['notification'].get('smtp_from_name') self.replyto_email = self.config['notification'].get('smtp_replyto') self.from_email = self.from_email or self.replyto_email if not self.from_email and not self.replyto_email: raise TracError(tag(tag.p('Unable to send email due to identity ' 'crisis.'), tag.p('Neither ', tag.b('notification.from'), ' nor ', tag.b('notification.reply_to'), 'are specified in the configuration.')), 'SMTP Notification Error') # Authentication info (optional) self.user_name = self.config['notification'].get('smtp_user') self.password = self.config['notification'].get('smtp_password') Notify.notify(self, resid) def format_header(self, key, name, email=None): from email.Header import Header maxlength = MAXHEADERLEN-(len(key)+2) # Do not sent ridiculous short headers if maxlength < 10: raise TracError(_("Header length is too short")) try: tmp = name.encode('ascii') header = Header(tmp, 'ascii', maxlinelen=maxlength) except UnicodeEncodeError: header = Header(name, self._charset, maxlinelen=maxlength) if not email: return header else: return '"%s" <%s>' % (header, email) def add_headers(self, msg, headers): for h in headers: msg[h] = self.encode_header(h, headers[h]) def get_smtp_address(self, address): if not address: return None def is_email(address): pos = address.find('@') if pos == -1: return False if address[pos+1:].lower() in self._ignore_domains: return False return True if not is_email(address): if address == 'anonymous': return None if self.email_map.has_key(address): address = self.email_map[address] elif NotifyEmail.nodomaddr_re.match(address): if self.config.getbool('notification', 'use_short_addr'): return address domain = self.config.get('notification', 'smtp_default_domain') if domain: address = "%s@%s" % (address, domain) else: self.env.log.info("Email address w/o domain: %s" % address) return None mo = self.shortaddr_re.search(address) if mo: return mo.group(0) mo = self.longaddr_re.search(address) if mo: return mo.group(2) self.env.log.info("Invalid email address: %s" % address) return None def encode_header(self, key, value): if isinstance(value, tuple): return self.format_header(key, value[0], value[1]) if isinstance(value, list): items = [] for v in value: items.append(self.encode_header(v)) return ',\n\t'.join(items) mo = self.longaddr_re.match(value) if mo: return self.format_header(key, mo.group(1), mo.group(2)) return self.format_header(key, value) def begin_send(self): self.server = smtplib.SMTP(self.smtp_server, self.smtp_port) # self.server.set_debuglevel(True) if self._use_tls: self.server.ehlo() if not self.server.esmtp_features.has_key('starttls'): raise TracError(_("TLS enabled but server does not support " \ "TLS")) self.server.starttls() self.server.ehlo() if self.user_name: self.server.login(self.user_name, self.password) def send(self, torcpts, ccrcpts, mime_headers={}): from email.MIMEText import MIMEText from email.Utils import formatdate stream = self.template.generate(**self.data) body = stream.render('text') projname = self.config.get('project', 'name') public_cc = self.config.getbool('notification', 'use_public_cc') headers = {} headers['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__ headers['X-Trac-Version'] = __version__ headers['X-Trac-Project'] = projname headers['X-URL'] = self.config.get('project', 'url') headers['Precedence'] = 'bulk' headers['Auto-Submitted'] = 'auto-generated' headers['Subject'] = self.subject headers['From'] = (self.from_name or projname, self.from_email) headers['Reply-To'] = self.replyto_email def build_addresses(rcpts): """Format and remove invalid addresses""" return filter(lambda x: x, \ [self.get_smtp_address(addr) for addr in rcpts]) def remove_dup(rcpts, all): """Remove duplicates""" tmp = [] for rcpt in rcpts: if not rcpt in all: tmp.append(rcpt) all.append(rcpt) return (tmp, all) toaddrs = build_addresses(torcpts) ccaddrs = build_addresses(ccrcpts) accparam = self.config.get('notification', 'smtp_always_cc') accaddrs = accparam and \ build_addresses(accparam.replace(',', ' ').split()) or [] bccparam = self.config.get('notification', 'smtp_always_bcc') bccaddrs = bccparam and \ build_addresses(bccparam.replace(',', ' ').split()) or [] recipients = [] (toaddrs, recipients) = remove_dup(toaddrs, recipients) (ccaddrs, recipients) = remove_dup(ccaddrs, recipients) (accaddrs, recipients) = remove_dup(accaddrs, recipients) (bccaddrs, recipients) = remove_dup(bccaddrs, recipients) # if there is not valid recipient, leave immediately if len(recipients) < 1: self.env.log.info('no recipient for a ticket notification') return pcc = accaddrs if public_cc: pcc += ccaddrs if toaddrs: headers['To'] = ', '.join(toaddrs) if pcc: headers['Cc'] = ', '.join(pcc) headers['Date'] = formatdate() # sanity check if not self._charset.body_encoding: try: dummy = body.encode('ascii') except UnicodeDecodeError: raise TracError(_("Ticket contains non-ASCII chars. " \ "Please change encoding setting")) msg = MIMEText(body, 'plain') # Message class computes the wrong type from MIMEText constructor, # which does not take a Charset object as initializer. Reset the # encoding type to force a new, valid evaluation del msg['Content-Transfer-Encoding'] msg.set_charset(self._charset) self.add_headers(msg, headers); self.add_headers(msg, mime_headers); self.env.log.info("Sending SMTP notification to %s:%d to %s" % (self.smtp_server, self.smtp_port, recipients)) msgtext = msg.as_string() # Ensure the message complies with RFC2822: use CRLF line endings recrlf = re.compile("\r?\n") msgtext = CRLF.join(recrlf.split(msgtext)) start = time.time() self.server.sendmail(msg['From'], recipients, msgtext) t = time.time() - start if t > 5: self.env.log.warning('Slow mail submission (%.2f s), ' 'check your mail setup' % t) def finish_send(self): if self._use_tls: # avoid false failure detection when the server closes # the SMTP connection with TLS enabled import socket try: self.server.quit() except socket.sslerror: pass else: self.server.quit() PKmh9}fnInItrac/notification.pyc; Ic@sdkZdkZdkZdklZdklZdklZl Z l Z dk Tdk l Z dklZdZdad efd YZd efd YZd efdYZdS(N(stag(s __version__(s BoolOptions IntOptionsOption(s*(sCRLF(s_iLsK[a-zA-Z0-9.'=+_-]+@(?:[a-zA-Z0-9_-]+\.)+[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?sNotificationSystemcBstZeddddZeddddZeddd d Zedd d d Zeddd dZ eddddZ eddd dZ eddddZ eddd dZ eddd dZeddd dZeddd dZeddd d Zedd!d"d#Zedd$dd%Zedd&dd'Zedd(dd)Zedd*d+d,ZRS(-Ns notifications smtp_enabledsfalses!Enable SMTP (email) notification.s smtp_servers localhosts4SMTP server hostname to use for email notifications.s smtp_portis/SMTP server port to use for email notification.s smtp_userss*Username for SMTP server. (''since 0.9'').s smtp_passwords*Password for SMTP server. (''since 0.9'').s smtp_fromstrac@localhosts-Sender address to use in notification emails.ssmtp_from_names*Sender name to use in notification emails.s smtp_replytos/Reply-To address to use in notification emails.ssmtp_always_ccslEmail address(es) to always send notifications to, addresses can be seen by all recipients (Cc:).ssmtp_always_bccsxEmail address(es) to always send notifications to, addresses do not appear publicly (Bcc:). (''since 0.10'').ssmtp_default_domains@Default host/domain to append to address that do not specify onesignore_domainssComma-separated list of domains that should not be considered part of email addresses (for usernames with Kerberos domains)s admit_domainsstComma-separated list of domains that should be considered as valid for email addresses (such as localdomain)s mime_encodingsbase64sASpecifies the MIME encoding scheme for emails. Valid options are 'base64' for Base64 encoding, 'qp' for Quoted-Printable, and 'none' for no encoding. Note that the no encoding means that non-ASCII characters in text are going to cause problems with notifications (''since 0.10'').s use_public_ccsRecipients can see email addresses of other CC'ed recipients. If this option is disabled (the default), recipients are put on BCC (''since 0.10'').suse_short_addrsPermit email address without a host/domain (i.e. username only) The SMTP server should accept those addresses, and either append a FQDN or use local delivery (''since 0.10'').suse_tlss3Use SSL/TLS to send notifications (''since 0.10'').ssmtp_subject_prefixs __default__sText to prepend to subject line of notification emails. If the setting is not defined, then the [$project_name] prefix. If no prefix is desired, then specifying an empty option will disable it.(''since 0.10.1'').(s__name__s __module__s BoolOptions smtp_enabledsOptions smtp_servers IntOptions smtp_ports smtp_users smtp_passwords smtp_fromssmtp_from_names smtp_replytossmtp_always_ccssmtp_always_bccssmtp_default_domainsignore_domainss admit_domainss mime_encodings use_public_ccsuse_short_addrsuse_tlsssmtp_subject_prefix(((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pysNotificationSystem%sJ                                   sNotifycBsDtZdZdZdZdZdZdZdZRS(s`Generic notification class for Trac. Subclass this to implement different methods. cCs||_|i|_|i|_dkl}||ii|idd|_ ||ii t hdt <|_ dS(N(sChromesmethodstextsCRLF(senvsselfsconfigs get_db_cnxsdbstrac.web.chromesChromes load_templates template_namestemplates populate_datasNonesCRLFsdata(sselfsenvsChrome((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys__init__xs   cCs=|i|\}}|i|i|||idS(N(sselfsget_recipientssresidstorcptssccrcptss begin_sendssends finish_send(sselfsresidsccrcptsstorcpts((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pysnotifys cCs tdS(sReturn a pair of list of subscribers to the resource 'resid'. First list represents the direct recipients (To:), second list represents the recipients in carbon copy (Cc:). N(sNotImplementedError(sselfsresid((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pysget_recipientsscCsdS(sQPrepare to send messages. Called before sending begins. N((sself((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys begin_sendscCs tdS(sSend message to recipients.N(sNotImplementedError(sselfstorcptssccrcpts((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pyssendscCsdS(saClean up after sending all messages. Called after sending all messages. N((sself((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys finish_sends( s__name__s __module__s__doc__s__init__snotifysget_recipientss begin_sendssends finish_send(((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pysNotifyrs     s NotifyEmailcBstZdZdZdZdZdZeZe i dZ e i dZ dZ dZd Zed Zd Zd Zd ZdZhdZdZRS(s$Baseclass for notification by email.s localhostistrac+tickets@localhostss [\w\d_\.\-]+s[;\s,]+c Csti||t}|iiidd}|o}|i d}di gi }|iddidD]} || iqm~}d|| ||d |f}ntid ||_tid ||_|iiidd |_|i|iiidd d}gi }|iidD]} || iq;~|_h|_x>|ii|iD]'\}}} | o| |i|\s*$suse_tlssignore_domainss("sNotifys__init__sselfsenvsEMAIL_LOOKALIKE_PATTERNsaddrfmtsconfigsgets admit_domainssfindspossjoinsappends_[1]sreplacessplitsxsstripsdomainssrescompiles shortaddr_res longaddr_resgetbools_use_tlss_init_pref_encodingslowers_ignore_domainss email_mapsget_known_userssdbsusernamesnamesemail( sselfsenvsusernames admit_domainsspossnames_[1]saddrfmtsdomainssxsemail((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys__init__s&K# ? cCsJdkl}l}l}||_d|i_|iii ddi }|djo@||i_ ||i_ d|i_d|i_d|i_n|ddgjo@||i_ ||i_ d|i_d|i_d|i_nX|djo4t|i_ t|i_ t|i_d |i_nttd |dS( N(sCharsetsQPsBASE64sutf-8s notifications mime_encodingsbase64sqpsquoted-printablesnonesasciis"Invalid email encoding setting: %s(s email.CharsetsCharsetsQPsBASE64sselfs_charsets input_charsetsenvsconfigsgetslowersprefsheader_encodings body_encodingsoutput_charsets input_codecs output_codecsNones TracErrors_(sselfsQPsCharsetsprefsBASE64((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys_init_pref_encodings,               c Csc||_|iidd odSn|idid|_|idid|_|idid|_|idid|_ |idid|_ |ip|i |_|i o|i oLt t t i dt i d t id d t id d dn|idid|_|idid|_ti||dS(Ns notifications smtp_enableds smtp_servers smtp_ports smtp_fromssmtp_from_names smtp_replytos,Unable to send email due to identity crisis.sNeither snotification.froms nor snotification.reply_tos#are specified in the configuration.sSMTP Notification Errors smtp_users smtp_password(ssubjectsselfsconfigsgetboolsgets smtp_serversgetints smtp_ports from_emails from_names replyto_emails TracErrorstagspsbs user_namespasswordsNotifysnotifysresid(sselfsresidssubject((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pysnotifys$   cCsdkl}tt|d}|djottdny(|i d}||dd|}Wn+t j o|||id|}nX| o|Snd||fSdS(N(sHeaderii sHeader length is too shortsasciis maxlinelens "%s" <%s>(s email.HeadersHeaders MAXHEADERLENslenskeys maxlengths TracErrors_snamesencodestmpsheadersUnicodeEncodeErrorsselfs_charsetsemail(sselfskeysnamesemailstmpsheadersHeaders maxlength((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys format_headers  cCs/x(|D] }|i|||||g}x$|D]}|i |i |qLWdi |Sn|i i |}|o)|i||id|idSn|i||SdS(Niis, i(s isinstancesvaluestuplesselfs format_headerskeyslistsitemssvsappends encode_headersjoins longaddr_resmatchsmosgroup(sselfskeysvaluesitemssvsmo((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys encode_header0s)cCsti|i|i|_|ioX|ii|iii d ot t dn|ii |iin|i o|ii|i |indS(Nsstarttlss+TLS enabled but server does not support TLS(ssmtplibsSMTPsselfs smtp_servers smtp_portsservers_use_tlssehlosesmtp_featuresshas_keys TracErrors_sstarttlss user_namesloginspassword(sself((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys begin_send=s    csdkl}dkl}iii}|i d}i i dd} i idd}h}dt|d i?|d||t<i<| }|d'joi$i%iAd(|ndS()N(sMIMEText(s formatdatestextsprojectsnames notifications use_public_ccsTrac %s, by Edgewall SoftwaresX-MailersX-Trac-VersionsX-Trac-ProjectsurlsX-URLsbulks Precedencesauto-generatedsAuto-SubmittedsSubjectsFromsReply-Tocs>tdgi}|D]}|i|q~SdS(s#Format and remove invalid addressescCs|S(N(sx(sx((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys^sN(sfiltersappends_[1]srcptssaddrsselfsget_smtp_address(srcptss_[1]saddr(sself(s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pysbuild_addresses\s cCsQg}x:|D]2}||j o|i||i|q q W||fSdS(sRemove duplicatesN(stmpsrcptssrcptsallsappend(srcptssallstmpsrcpt((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys remove_dupas ssmtp_always_ccs,s ssmtp_always_bccis&no recipient for a ticket notifications, sTosCcsDatesasciis?Ticket contains non-ASCII chars. Please change encoding settingsplainsContent-Transfer-Encodings(Sending SMTP notification to %s:%d to %ss ? is4Slow mail submission (%.2f s), check your mail setup(Bsemail.MIMETextsMIMETexts email.Utilss formatdatesselfstemplatesgeneratesdatasstreamsrendersbodysconfigsgetsprojnamesgetbools public_ccsheaderss __version__ssubjects from_names from_emails replyto_emailsbuild_addressess remove_dupstorcptsstoaddrssccrcptssccaddrssaccparamsreplacessplitsaccaddrssbccparamsbccaddrss recipientsslensenvslogsinfospccsjoins_charsets body_encodingsencodesdummysUnicodeDecodeErrors TracErrors_smsgs set_charsets add_headerss mime_headerss smtp_servers smtp_ports as_stringsmsgtextsrescompilesrecrlfsCRLFstimesstartsserverssendmailstswarning(sselfstorcptssccrcptss mime_headerssstreamsrecrlfsccaddrss public_ccsMIMETextsprojnamestoaddrssstartsbccparamsbuild_addressess formatdatesmsgspccsaccaddrssbodys recipientssmsgtextsbccaddrssdummys remove_dupsheadersstsaccparam((sselfs6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pyssendJsv           ,,  &   cCsR|io7dk}y|iiWqN|ij oqNXn|iidS(N(sselfs_use_tlsssocketsserversquitssslerror(sselfssocket((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys finish_sends   (s__name__s __module__s__doc__s smtp_servers smtp_ports from_emailssubjectsNones template_namesrescompiles nodomaddr_res addrsep_res__init__s_init_pref_encodingsnotifys format_headers add_headerssget_smtp_addresss encode_headers begin_sendssends finish_send(((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys NotifyEmails$       $ X(stimessmtplibsresgenshi.builderstagstracs __version__s trac.configs BoolOptions IntOptionsOptions trac.corestrac.util.textsCRLFstrac.util.translations_s MAXHEADERLENsEMAIL_LOOKALIKE_PATTERNs ComponentsNotificationSystemsobjectsNotifys NotifyEmail(s NotifyEmailsOptions IntOptionsrestagsCRLFs MAXHEADERLENstimes BoolOptionssmtplibs __version__sNotificationSystems_sNotify((s6build/bdist.darwin-9.5.0-i386/egg/trac/notification.pys?s       M1PKkh96TT trac/perm.py# -*- coding: utf-8 -*- # # Copyright (C) 2003-2008 Edgewall Software # Copyright (C) 2003-2004 Jonas Borgström # Copyright (C) 2005 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Jonas Borgström # Christopher Lenz """Management of permissions.""" from time import time from trac.config import ExtensionOption, OrderedExtensionsOption from trac.core import * from trac.resource import Resource, get_resource_name from trac.util.compat import set from trac.util.translation import _ __all__ = ['IPermissionRequestor', 'IPermissionStore', 'IPermissionGroupProvider', 'PermissionError', 'PermissionSystem'] class PermissionError(StandardError): """Insufficient permissions to complete the operation""" def __init__ (self, action=None, resource=None, env=None): StandardError.__init__(self) self.action = action self.resource = resource self.env = env def __str__ (self): if self.action: if self.resource: return _('%(perm)s privileges are required to perform ' 'this operation on %(resource)s', perm=self.action, resource=get_resource_name(self.env, self.resource)) else: return _('%(perm)s privileges are required to perform ' 'this operation', perm=self.action) else: return _('Insufficient privileges to perform this operation.') class IPermissionRequestor(Interface): """Extension point interface for components that define actions.""" def get_permission_actions(): """Return a list of actions defined by this component. The items in the list may either be simple strings, or `(string, sequence)` tuples. The latter are considered to be "meta permissions" that group several simple actions under one name for convenience. """ class IPermissionStore(Interface): """Extension point interface for components that provide storage and management of permissions.""" def get_user_permissions(username): """Return all permissions for the user with the specified name. The permissions are returned as a dictionary where the key is the name of the permission, and the value is either `True` for granted permissions or `False` for explicitly denied permissions.""" def get_users_with_permissions(permissions): """Retrieve a list of users that have any of the specified permissions. Users are returned as a list of usernames. """ def get_all_permissions(): """Return all permissions for all users. The permissions are returned as a list of (subject, action) formatted tuples.""" def grant_permission(username, action): """Grant a user permission to perform an action.""" def revoke_permission(username, action): """Revokes the permission of the given user to perform an action.""" class IPermissionGroupProvider(Interface): """Extension point interface for components that provide information about user groups. """ def get_permission_groups(username): """Return a list of names of the groups that the user with the specified name is a member of.""" class IPermissionPolicy(Interface): """A security policy provider used for fine grained permission checks.""" def check_permission(action, username, resource, perm): """Check that the action can be performed by username on the resource :param action: the name of the permission :param username: the username string or 'anonymous' if there's no authenticated user :param resource: the resource on which the check applies. Will be `None`, if the check is a global one and not made on a resource in particular :param perm: the permission cache for that username and resource, which can be used for doing secondary checks on other permissions. Care must be taken to avoid recursion. :return: `True` if action is allowed, `False` if action is denied, or `None` if indifferent. If `None` is returned, the next policy in the chain will be used, and so on. Note that when checking a permission on a realm resource (i.e. when `.id` is `None`), this usually corresponds to some preliminary check done before making a fine-grained check on some resource. Therefore the `IPermissionPolicy` should be conservative and return: * `True` if the action *can* be allowed for some resources in that realm. Later, for specific resource, the policy will be able to return `True` (allow), `False` (deny) or `None` (don't decide). * `None` if the action *can not* be performed for *some* resources. This corresponds to situation where the policy is only interested in returning `False` or `None` on specific resources. * `False` if the action *can not* be performed for *any* resource in that realm (that's a very strong decision as that will usually prevent any fine-grained check to even happen). Note that performing permission checks on realm resources may seem redundant for now as the action name itself contains the realm, but this will probably change in the future (e.g. `'VIEW' in ...`). """ class DefaultPermissionStore(Component): """Default implementation of permission storage and simple group management. This component uses the `PERMISSION` table in the database to store both permissions and groups. """ implements(IPermissionStore) group_providers = ExtensionPoint(IPermissionGroupProvider) def get_user_permissions(self, username): """Retrieve the permissions for the given user and return them in a dictionary. The permissions are stored in the database as (username, action) records. There's simple support for groups by using lowercase names for the action column: such a record represents a group and not an actual permission, and declares that the user is part of that group. """ subjects = set([username]) for provider in self.group_providers: subjects.update(provider.get_permission_groups(username)) actions = set([]) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT username,action FROM permission") rows = cursor.fetchall() while True: num_users = len(subjects) num_actions = len(actions) for user, action in rows: if user in subjects: if action.isupper() and action not in actions: actions.add(action) if not action.isupper() and action not in subjects: # action is actually the name of the permission group # here subjects.add(action) if num_users == len(subjects) and num_actions == len(actions): break return list(actions) def get_users_with_permissions(self, permissions): """Retrieve a list of users that have any of the specified permissions Users are returned as a list of usernames. """ # get_user_permissions() takes care of the magic 'authenticated' group. # The optimized loop we had before didn't. This is very inefficient, # but it works. db = self.env.get_db_cnx() cursor = db.cursor() result = set() users = set([u[0] for u in self.env.get_known_users()]) for user in users: userperms = self.get_user_permissions(user) for group in permissions: if group in userperms: result.add(user) return list(result) def get_all_permissions(self): """Return all permissions for all users. The permissions are returned as a list of (subject, action) formatted tuples.""" db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT username,action FROM permission") return [(row[0], row[1]) for row in cursor] def grant_permission(self, username, action): """Grants a user the permission to perform the specified action.""" db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("INSERT INTO permission VALUES (%s, %s)", (username, action)) self.log.info('Granted permission for %s to %s' % (action, username)) db.commit() def revoke_permission(self, username, action): """Revokes a users' permission to perform the specified action.""" db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("DELETE FROM permission WHERE username=%s AND action=%s", (username, action)) self.log.info('Revoked permission for %s to %s' % (action, username)) db.commit() class DefaultPermissionGroupProvider(Component): """Provides the basic builtin permission groups 'anonymous' and 'authenticated'.""" implements(IPermissionGroupProvider) def get_permission_groups(self, username): groups = ['anonymous'] if username and username != 'anonymous': groups.append('authenticated') return groups class DefaultPermissionPolicy(Component): """Default permission policy using the IPermissionStore system.""" implements(IPermissionPolicy) # Number of seconds a cached user permission set is valid for. CACHE_EXPIRY = 5 # How frequently to clear the entire permission cache CACHE_REAP_TIME = 60 def __init__(self): self.permission_cache = {} self.last_reap = time() # IPermissionPolicy methods def check_permission(self, action, username, resource, perm): now = time() if now - self.last_reap > self.CACHE_REAP_TIME: self.permission_cache = {} self.last_reap = time() timestamp, permissions = self.permission_cache.get(username, (0, None)) # Cache hit? if now - timestamp > self.CACHE_EXPIRY: # No, pull permissions from database. permissions = PermissionSystem(self.env). \ get_user_permissions(username) self.permission_cache[username] = (now, permissions) return action in permissions or None class PermissionSystem(Component): """Sub-system that manages user permissions.""" implements(IPermissionRequestor) requestors = ExtensionPoint(IPermissionRequestor) store = ExtensionOption('trac', 'permission_store', IPermissionStore, 'DefaultPermissionStore', """Name of the component implementing `IPermissionStore`, which is used for managing user and group permissions.""") policies = OrderedExtensionsOption('trac', 'permission_policies', IPermissionPolicy, 'DefaultPermissionPolicy, LegacyAttachmentPolicy', False, """List of components implementing `IPermissionPolicy`, in the order in which they will be applied. These components manage fine-grained access control to Trac resources. Defaults to the DefaultPermissionPolicy (pre-0.11 behavior) and LegacyAttachmentPolicy (map ATTACHMENT_* permissions to realm specific ones)""") # Public API def grant_permission(self, username, action): """Grant the user with the given name permission to perform to specified action.""" if action.isupper() and action not in self.get_actions(): raise TracError(_('%(name)s is not a valid action.', name=action)) self.store.grant_permission(username, action) def revoke_permission(self, username, action): """Revokes the permission of the specified user to perform an action.""" self.store.revoke_permission(username, action) def get_actions(self): actions = [] for requestor in self.requestors: for action in requestor.get_permission_actions(): if isinstance(action, tuple): actions.append(action[0]) else: actions.append(action) return actions def get_user_permissions(self, username=None): """Return the permissions of the specified user. The return value is a dictionary containing all the actions as keys, and a boolean value. `True` means that the permission is granted, `False` means the permission is denied.""" actions = [] for requestor in self.requestors: actions += list(requestor.get_permission_actions()) permissions = {} if username: # Return all permissions that the given user has meta = {} for action in actions: if isinstance(action, tuple): name, value = action meta[name] = value def _expand_meta(action): permissions[action] = True if meta.has_key(action): [_expand_meta(perm) for perm in meta[action]] for perm in self.store.get_user_permissions(username): _expand_meta(perm) else: # Return all permissions available in the system for action in actions: if isinstance(action, tuple): permissions[action[0]] = True else: permissions[action] = True return permissions def get_all_permissions(self): """Return all permissions for all users. The permissions are returned as a list of (subject, action) formatted tuples.""" return self.store.get_all_permissions() def get_users_with_permission(self, permission): """Return all users that have the specified permission. Users are returned as a list of user names. """ # this should probably be cached parent_map = {} for requestor in self.requestors: for action in requestor.get_permission_actions(): for child in action[1]: parent_map.setdefault(child, []).append(action[0]) satisfying_perms = {} def _append_with_parents(action): if action in satisfying_perms: return # avoid unneccesary work and infinite loops satisfying_perms[action] = True if action in parent_map: map(_append_with_parents, parent_map[action]) _append_with_parents(permission) return self.store.get_users_with_permissions(satisfying_perms.keys()) def expand_actions(self, actions): """Helper method for expanding all meta actions.""" meta = {} for requestor in self.requestors: for m in requestor.get_permission_actions(): if isinstance(m, tuple): meta[m[0]] = m[1] expanded_actions = set(actions) def expand_action(action): actions = meta.get(action, []) expanded_actions.update(actions) [expand_action(a) for a in actions] [expand_action(a) for a in actions] return expanded_actions def check_permission(self, action, username=None, resource=None, perm=None): """Return True if permission to perform action for the given resource is allowed.""" if username is None: username = 'anonymous' if resource and resource.realm is None: resource = None for policy in self.policies: decision = policy.check_permission(action, username, resource, perm) if decision is not None: if not decision: self.log.debug("%s denies %s performing %s on %r" % (policy.__class__.__name__, username, action, resource)) return decision self.log.debug("No policy allowed %s performing %s on %r" % (username, action, resource)) return False # IPermissionRequestor methods def get_permission_actions(self): """Implement the global `TRAC_ADMIN` meta permission. Implements also the `EMAIL_VIEW` permission which allows for showing email addresses even if `[trac] show_email_addresses` is `false`. """ actions = ['EMAIL_VIEW'] for requestor in [r for r in self.requestors if r is not self]: for action in requestor.get_permission_actions(): if isinstance(action, tuple): actions.append(action[0]) else: actions.append(action) return [('TRAC_ADMIN', actions), 'EMAIL_VIEW'] class PermissionCache(object): """Cache that maintains the permissions of a single user. Permissions are usually checked using the following syntax: 'WIKI_MODIFY' in perm One can also apply more fine grained permission checks and specify a specific resource for which the permission should be available: 'WIKI_MODIFY' in perm('wiki', 'WikiStart') If there's already a `page` object available, the check is simply: 'WIKI_MODIFY' in perm(page.resource) If instead of a check, one wants to assert that a given permission is available, the following form should be used: perm.require('WIKI_MODIFY') or perm('wiki', 'WikiStart').require('WIKI_MODIFY') or perm(page.resource).require('WIKI_MODIFY') When using `require`, a `PermissionError` exception is raised if the permission is missing. """ __slots__ = ('env', 'username', '_resource', '_cache') def __init__(self, env, username=None, resource=None, cache=None, groups=None): self.env = env self.username = username or 'anonymous' self._resource = resource if cache is None: cache = {} self._cache = cache def _normalize_resource(self, realm_or_resource, id, version): if realm_or_resource: return Resource(realm_or_resource, id, version) else: return self._resource def __call__(self, realm_or_resource, id=False, version=False): """Convenience function for using thus: 'WIKI_VIEW' in perm(context) or 'WIKI_VIEW' in perm(realm, id, version) or 'WIKI_VIEW' in perm(resource) """ resource = Resource(realm_or_resource, id, version) if resource and self._resource and resource == self._resource: return self else: return PermissionCache(self.env, self.username, resource, self._cache) def has_permission(self, action, realm_or_resource=None, id=False, version=False): resource = self._normalize_resource(realm_or_resource, id, version) return self._has_permission(action, resource) def _has_permission(self, action, resource): key = (self.username, hash(resource), action) cached = self._cache.get(key) if cached: cache_decision, cache_resource = cached if resource == cache_resource: return cache_decision perm = self if resource is not self._resource: perm = PermissionCache(self.env, self.username, resource, self._cache) decision = PermissionSystem(self.env). \ check_permission(action, perm.username, resource, perm) self._cache[key] = (decision, resource) return decision __contains__ = has_permission def require(self, action, realm_or_resource=None, id=False, version=False): resource = self._normalize_resource(realm_or_resource, id, version) if not self._has_permission(action, resource): raise PermissionError(action, resource, self.env) assert_permission = require def permissions(self): """Deprecated (but still used by the HDF compatibility layer)""" self.env.log.warning('perm.permissions() is deprecated and ' 'is only present for HDF compatibility') perm = PermissionSystem(self.env) actions = perm.get_user_permissions(self.username) return [action for action in actions if action in self] PKmh9țCoo trac/perm.pyc; Ic@sOdZdklZdklZlZdkTdklZlZdk l Z dk l Z ddd d d gZ d efd YZdefd YZdefdYZd efdYZdefdYZdefdYZdefdYZdefdYZd efdYZdefdYZdS(sManagement of permissions.(stime(sExtensionOptionsOrderedExtensionsOption(s*(sResourcesget_resource_name(sset(s_sIPermissionRequestorsIPermissionStoresIPermissionGroupProvidersPermissionErrorsPermissionSystemcBs)tZdZeeedZdZRS(s2Insufficient permissions to complete the operationcCs,ti|||_||_||_dS(N(s StandardErrors__init__sselfsactionsresourcesenv(sselfsactionsresourcesenv((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pys__init__#s   cCse|ioM|io,tdd|idt|i|iSqatdd|iSn tdSdS(NsJ%(perm)s privileges are required to perform this operation on %(resource)sspermsresources:%(perm)s privileges are required to perform this operations2Insufficient privileges to perform this operation.(sselfsactionsresources_sget_resource_namesenv(sself((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pys__str__)s   (s__name__s __module__s__doc__sNones__init__s__str__(((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysPermissionError s cBstZdZdZRS(s=Extension point interface for components that define actions.cCsdS(s.Return a list of actions defined by this component. The items in the list may either be simple strings, or `(string, sequence)` tuples. The latter are considered to be "meta permissions" that group several simple actions under one name for convenience. N((((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysget_permission_actions:s(s__name__s __module__s__doc__sget_permission_actions(((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysIPermissionRequestor7s cBs;tZdZdZdZdZdZdZRS(s`Extension point interface for components that provide storage and management of permissions.cCsdS(sReturn all permissions for the user with the specified name. The permissions are returned as a dictionary where the key is the name of the permission, and the value is either `True` for granted permissions or `False` for explicitly denied permissions.N((susername((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysget_user_permissionsHscCsdS(sRetrieve a list of users that have any of the specified permissions. Users are returned as a list of usernames. N((s permissions((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysget_users_with_permissionsOscCsdS(sReturn all permissions for all users. The permissions are returned as a list of (subject, action) formatted tuples.N((((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysget_all_permissionsUscCsdS(s-Grant a user permission to perform an action.N((susernamesaction((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysgrant_permission[scCsdS(s>Revokes the permission of the given user to perform an action.N((susernamesaction((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysrevoke_permission^s(s__name__s __module__s__doc__sget_user_permissionssget_users_with_permissionssget_all_permissionssgrant_permissionsrevoke_permission(((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysIPermissionStoreDs     cBstZdZdZRS(s]Extension point interface for components that provide information about user groups. cCsdS(sbReturn a list of names of the groups that the user with the specified name is a member of.N((susername((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysget_permission_groupsgs(s__name__s __module__s__doc__sget_permission_groups(((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysIPermissionGroupProviderbs sIPermissionPolicycBstZdZdZRS(sCA security policy provider used for fine grained permission checks.cCsdS(sCheck that the action can be performed by username on the resource :param action: the name of the permission :param username: the username string or 'anonymous' if there's no authenticated user :param resource: the resource on which the check applies. Will be `None`, if the check is a global one and not made on a resource in particular :param perm: the permission cache for that username and resource, which can be used for doing secondary checks on other permissions. Care must be taken to avoid recursion. :return: `True` if action is allowed, `False` if action is denied, or `None` if indifferent. If `None` is returned, the next policy in the chain will be used, and so on. Note that when checking a permission on a realm resource (i.e. when `.id` is `None`), this usually corresponds to some preliminary check done before making a fine-grained check on some resource. Therefore the `IPermissionPolicy` should be conservative and return: * `True` if the action *can* be allowed for some resources in that realm. Later, for specific resource, the policy will be able to return `True` (allow), `False` (deny) or `None` (don't decide). * `None` if the action *can not* be performed for *some* resources. This corresponds to situation where the policy is only interested in returning `False` or `None` on specific resources. * `False` if the action *can not* be performed for *any* resource in that realm (that's a very strong decision as that will usually prevent any fine-grained check to even happen). Note that performing permission checks on realm resources may seem redundant for now as the action name itself contains the realm, but this will probably change in the future (e.g. `'VIEW' in ...`). N((sactionsusernamesresourcesperm((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pyscheck_permissionos#(s__name__s __module__s__doc__scheck_permission(((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysIPermissionPolicyls sDefaultPermissionStorecBsQtZdZeeeeZdZdZ dZ dZ dZ RS(sDefault implementation of permission storage and simple group management. This component uses the `PERMISSION` table in the database to store both permissions and groups. c CsXt|g}x'|iD]}|i|i|qWtg}|i i }|i }|i d|i} xtot|}t|}x|| D]t\} } | |jo[| io | |jo|i| n| i o | |jo|i| qqqW|t|jo|t|joPq|q|Wt|SdS(sRetrieve the permissions for the given user and return them in a dictionary. The permissions are stored in the database as (username, action) records. There's simple support for groups by using lowercase names for the action column: such a record represents a group and not an actual permission, and declares that the user is part of that group. s&SELECT username,action FROM permissionN(ssetsusernamessubjectssselfsgroup_providerssprovidersupdatesget_permission_groupssactionssenvs get_db_cnxsdbscursorsexecutesfetchallsrowssTrueslens num_userss num_actionssusersactionsisuppersaddslist( sselfsusernames num_userssdbsactionss num_actionsscursorssubjectssprovidersactionsrowssuser((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysget_user_permissionss0         & c Cs|ii}|i}t} tgi}|ii D]}||dq>~}xL|D]D}|i |} x,|D]$}|| jo| i|qqWqeWt| SdS(sRetrieve a list of users that have any of the specified permissions Users are returned as a list of usernames. iN(sselfsenvs get_db_cnxsdbscursorssetsresultsappends_[1]sget_known_userssususerssusersget_user_permissionss userpermss permissionssgroupsaddslist( sselfs permissionssusers_[1]sgroupsuserssdbscursorsusresults userperms((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysget_users_with_permissionss  : cCs_|ii}|i}|idgi}|D]}||d|dfq6~SdS(sReturn all permissions for all users. The permissions are returned as a list of (subject, action) formatted tuples.s&SELECT username,action FROM permissioniiN( sselfsenvs get_db_cnxsdbscursorsexecutesappends_[1]srow(sselfscursors_[1]sdbsrow((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysget_all_permissionss   cCsY|ii}|i}|id||f|ii d||f|i dS(s=Grants a user the permission to perform the specified action.s&INSERT INTO permission VALUES (%s, %s)sGranted permission for %s to %sN( sselfsenvs get_db_cnxsdbscursorsexecutesusernamesactionslogsinfoscommit(sselfsusernamesactionsdbscursor((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysgrant_permissions   cCsY|ii}|i}|id||f|ii d||f|i dS(s<Revokes a users' permission to perform the specified action.s6DELETE FROM permission WHERE username=%s AND action=%ssRevoked permission for %s to %sN( sselfsenvs get_db_cnxsdbscursorsexecutesusernamesactionslogsinfoscommit(sselfsusernamesactionsdbscursor((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysrevoke_permissions   ( s__name__s __module__s__doc__s implementssIPermissionStoresExtensionPointsIPermissionGroupProvidersgroup_providerssget_user_permissionssget_users_with_permissionssget_all_permissionssgrant_permissionsrevoke_permission(((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysDefaultPermissionStores    !  sDefaultPermissionGroupProvidercBs!tZdZeedZRS(sQProvides the basic builtin permission groups 'anonymous' and 'authenticated'.cCs6dg}|o |djo|idn|SdS(Ns anonymouss authenticated(sgroupssusernamesappend(sselfsusernamesgroups((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysget_permission_groupss (s__name__s __module__s__doc__s implementssIPermissionGroupProvidersget_permission_groups(((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pysDefaultPermissionGroupProviders  sDefaultPermissionPolicycBs6tZdZeedZdZdZdZRS(s<Default permission policy using the IPermissionStore system.ii<cCsh|_t|_dS(N(sselfspermission_cachestimes last_reap(sself((s.build/bdist.darwin-9.5.0-i386/egg/trac/perm.pys__init__s cCst}||i|ijoh|_t|_n|ii|dtf\}}|||i jo/t |i i|}||f|i|x:|D]2} t | t ot| d # Copyright (C) 2007 Christian Boos # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Christian Boos # Alec Thomas from trac.core import * from trac.util.compat import reversed from trac.util.translation import _ class ResourceNotFound(TracError): """Thrown when a non-existent resource is requested""" class IResourceManager(Interface): def get_resource_realms(): """Return resource realms managed by the component. :rtype: `basestring` generator """ def get_resource_url(resource, href, **kwargs): """Return the canonical URL for displaying the given resource. :param resource: a `Resource` :param href: an `Href` used for creating the URL Note that if there's no special rule associated to this realm for creating URLs (i.e. the standard convention of using realm/id applies), then it's OK to not define this method. """ def get_resource_description(resource, format='default', context=None, **kwargs): """Return a string representation of the resource, according to the `format`. :param resource: the `Resource` to describe :param format: the kind of description wanted. Typical formats are: `'default'`, `'compact'` or `'summary'`. :param context: an optional rendering context to allow rendering rich output (like markup containing links) :type context: `Context` Additional keyword arguments can be given as extra information for some formats. For example, the ticket with the id 123 is represented as: - `'#123'` in `'compact'` format, - `'Ticket #123'` for the `default` format. - `'Ticket #123 (closed defect): This is the summary'` for the `'summary'` format Note that it is also OK to not define this method if there's no special way to represent the resource, in which case the standard representations 'realm:id' (in compact mode) or 'Realm id' (in default mode) will be used. """ class Resource(object): """Resource identifier. This specifies as precisely as possible *which* resource from a Trac environment is manipulated. A resource is identified by: (- a `project` identifier) 0.12? - a `realm` (a string like `'wiki'` or `'ticket'`) - an `id`, which uniquely identifies a resource within its realm. If the `id` information is not set, then the resource represents the realm as a whole. - an optional `version` information. If `version` is `None`, this refers by convention to the latest version of the resource. Some generic and commonly used rendering methods are associated as well to the Resource object. Those properties and methods actually delegate the real work to the Resource's manager. """ __slots__ = ('realm', 'id', 'version', 'parent') def __repr__(self): if self.realm is None: return '' path = [] r = self while r: name = r.realm if r.id: name += ':' + unicode(r.id) # id can be numerical if r.version is not None: name += '@' + unicode(r.version) path.append(name) r = r.parent return '' % (', '.join(reversed(path))) def __eq__(self, other): return self.realm == other.realm and \ self.id == other.id and \ self.version == other.version and \ self.parent == other.parent def __hash__(self): """Hash this resource descriptor, including its hierarchy.""" path = () current = self while current: path += (self.realm, self.id, self.version) current = current.parent return hash(path) # -- methods for creating other Resource identifiers def __new__(cls, resource_or_realm=None, id=False, version=False, parent=False): """Create a new Resource object from a specification. :param resource_or_realm: this can be either: - a `Resource`, which is then used as a base for making a copy - a `basestring`, used to specify a `realm` :param id: the resource identifier :param version: the version or `None` for indicating the latest version >>> main = Resource('wiki', 'WikiStart') >>> repr(main) "" >>> Resource(main) is main True >>> main3 = Resource(main, version=3) >>> repr(main3) "" >>> main0 = main3(version=0) >>> repr(main0) "" In a copy, if `id` is overriden, then the original `version` value will not be reused. >>> repr(Resource(main3, id="WikiEnd")) "" >>> repr(Resource(None)) '' """ realm = resource_or_realm if isinstance(resource_or_realm, Resource): if id is False and version is False and parent is False: return resource_or_realm else: # copy and override realm = resource_or_realm.realm if id is False: id = resource_or_realm.id if version is False: if id == resource_or_realm.id: version = resource_or_realm.version # could be 0... else: version = None if parent is False: parent = resource_or_realm.parent else: if id is False: id = None if version is False: version = None if parent is False: parent = None resource = super(Resource, cls).__new__(cls) resource.realm = realm resource.id = id resource.version = version resource.parent = parent return resource def __call__(self, realm=False, id=False, version=False, parent=False): """Create a new Resource using the current resource as a template. Optional keyword arguments can be given to override `id` and `version`. """ return Resource(realm is False and self or realm, id, version, parent) # -- methods for retrieving children Resource identifiers def child(self, realm, id=False, version=False): """Retrieve a child resource for a secondary `realm`. Same as `__call__`, except that this one sets the parent to `self`. """ return self.__call__(realm, id, version, self) class ResourceSystem(Component): """Resource identification and description. This component makes the link between `Resource` identifiers and their corresponding manager `Component`. """ resource_managers = ExtensionPoint(IResourceManager) def __init__(self): self._resource_managers_map = None # Public methods def get_resource_manager(self, realm): """Return the component responsible for resources in the given `realm` :param realm: the realm name :return: a `Component` implementing `IResourceManager` or `None` """ # build a dict of realm keys to IResourceManager implementations if not self._resource_managers_map: map = {} for manager in self.resource_managers: for manager_realm in manager.get_resource_realms(): map[manager_realm] = manager self._resource_managers_map = map return self._resource_managers_map.get(realm) def get_known_realms(self): """Return a list of all the realm names of resource managers.""" realms = [] for manager in self.resource_managers: for realm in manager.get_resource_realms(): realms.append(realm) return realms # -- Utilities for manipulating resources in a generic way def get_resource_url(env, resource, href, **kwargs): """Retrieve the canonical URL for the given resource. This function delegates the work to the resource manager for that resource if it implements a `get_resource_url` method, otherwise reverts to simple '/realm/identifier' style URLs. :param env: the `Environment` where `IResourceManager` components live :param resource: the `Resource` object specifying the Trac resource :param href: an `Href` object used for building the URL Additional keyword arguments are translated as query paramaters in the URL. >>> from trac.test import EnvironmentStub >>> from trac.web.href import Href >>> env = EnvironmentStub() >>> href = Href('/trac.cgi') >>> main = Resource('generic', 'Main') >>> get_resource_url(env, main, href) '/trac.cgi/generic/Main' >>> get_resource_url(env, main(version=3), href) '/trac.cgi/generic/Main?version=3' >>> get_resource_url(env, main(version=3), href) '/trac.cgi/generic/Main?version=3' >>> get_resource_url(env, main(version=3), href, action='diff') '/trac.cgi/generic/Main?action=diff&version=3' >>> get_resource_url(env, main(version=3), href, action='diff', version=5) '/trac.cgi/generic/Main?action=diff&version=5' """ manager = ResourceSystem(env).get_resource_manager(resource.realm) if not manager or not hasattr(manager, 'get_resource_url'): args = {'version': resource.version} args.update(kwargs) return href(resource.realm, resource.id, **args) else: return manager.get_resource_url(resource, href, **kwargs) def get_resource_description(env, resource, format='default', **kwargs): """Retrieve a standardized description for the given resource. This function delegates the work to the resource manager for that resource if it implements a `get_resource_description` method, otherwise reverts to simple presentation of the realm and identifier information. :param env: the `Environment` where `IResourceManager` components live :param resource: the `Resource` object specifying the Trac resource :param format: which formats to use for the description Additional keyword arguments can be provided and will be propagated to resource manager that might make use of them (typically, a `context` parameter for creating context dependent output). >>> from trac.test import EnvironmentStub >>> env = EnvironmentStub() >>> main = Resource('generic', 'Main') >>> get_resource_description(env, main) 'generic:Main' >>> get_resource_description(env, main(version=3)) 'generic:Main' >>> get_resource_description(env, main(version=3), format='summary') 'generic:Main at version 3' """ manager = ResourceSystem(env).get_resource_manager(resource.realm) if not manager or not hasattr(manager, 'get_resource_description'): name = '%s:%s' % (resource.realm, resource.id) if format == 'summary': name += _(' at version %(version)s', version=resource.version) return name else: return manager.get_resource_description(resource, format, **kwargs) def get_resource_name(env, resource): return get_resource_description(env, resource) def get_resource_shortname(env, resource): return get_resource_description(env, resource, 'compact') def get_resource_summary(env, resource): return get_resource_description(env, resource, 'summary') def get_relative_url(env, resource, href, path='', **kwargs): """Build an URL relative to a resource given as reference. :param path: path leading to another resource within the same realm. >>> from trac.test import EnvironmentStub >>> env = EnvironmentStub() >>> from trac.web.href import Href >>> href = Href('/trac.cgi') >>> main = Resource('wiki', 'Main', version=3) Without parameters, return the canonical URL for the resource, like `get_resource_url` does. >>> get_relative_url(env, main, href) '/trac.cgi/wiki/Main?version=3' Paths are relative to the given resource: >>> get_relative_url(env, main, href, '.') '/trac.cgi/wiki/Main?version=3' >>> get_relative_url(env, main, href, './Sub') '/trac.cgi/wiki/Main/Sub' >>> get_relative_url(env, main, href, './Sub/Infra') '/trac.cgi/wiki/Main/Sub/Infra' >>> get_relative_url(env, main, href, './Sub/') '/trac.cgi/wiki/Main/Sub' >>> mainsub = main(id='Main/Sub') >>> get_relative_url(env, mainsub, href, '..') '/trac.cgi/wiki/Main' >>> get_relative_url(env, main, href, '../Other') '/trac.cgi/wiki/Other' References always stay within the current resource realm: >>> get_relative_url(env, mainsub, href, '../..') '/trac.cgi/wiki' >>> get_relative_url(env, mainsub, href, '../../..') '/trac.cgi/wiki' >>> get_relative_url(env, mainsub, href, '/toplevel') '/trac.cgi/wiki/toplevel' Extra keyword arguments are forwarded as query parameters: >>> get_relative_url(env, main, href, action='diff') '/trac.cgi/wiki/Main?action=diff&version=3' """ if path in (None, '', '.'): return get_resource_url(env, resource, href, **kwargs) else: base = unicode(path[0] != '/' and resource.id or '').split('/') for comp in path.split('/'): if comp in ('.', ''): continue elif comp == '..': if base: base.pop() elif comp: base.append(comp) return get_resource_url(env, resource(id=base and '/'.join(base) or None), href, **kwargs) def render_resource_link(env, context, resource, format='default'): """Utility for generating a link `Element` to the given resource. Some component manager may directly use an extra `context` parameter in order to directly generate rich content. Otherwise, the textual output is wrapped in a link to the resource. """ from genshi.builder import Element, tag link = get_resource_description(env, resource, format, context=context) if not isinstance(link, Element): link = tag.a(link, href=get_resource_url(env, resource, context.href)) return link PKmh9qEEtrac/resource.pyc; Ic@sdkTdklZdklZdefdYZdefdYZde fdYZ d e fd YZ d Z d d ZdZdZdZddZd dZdS((s*(sreversed(s_sResourceNotFoundcBstZdZRS(s0Thrown when a non-existent resource is requested(s__name__s __module__s__doc__(((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysResourceNotFounds sIResourceManagercBs)tZdZdZdedZRS(NcCsdS(saReturn resource realms managed by the component. :rtype: `basestring` generator N((((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysget_resource_realmsscKsdS(soReturn the canonical URL for displaying the given resource. :param resource: a `Resource` :param href: an `Href` used for creating the URL Note that if there's no special rule associated to this realm for creating URLs (i.e. the standard convention of using realm/id applies), then it's OK to not define this method. N((sresourceshrefskwargs((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysget_resource_url$s sdefaultcKsdS(s4Return a string representation of the resource, according to the `format`. :param resource: the `Resource` to describe :param format: the kind of description wanted. Typical formats are: `'default'`, `'compact'` or `'summary'`. :param context: an optional rendering context to allow rendering rich output (like markup containing links) :type context: `Context` Additional keyword arguments can be given as extra information for some formats. For example, the ticket with the id 123 is represented as: - `'#123'` in `'compact'` format, - `'Ticket #123'` for the `default` format. - `'Ticket #123 (closed defect): This is the summary'` for the `'summary'` format Note that it is also OK to not define this method if there's no special way to represent the resource, in which case the standard representations 'realm:id' (in compact mode) or 'Realm id' (in default mode) will be used. N((sresourcesformatscontextskwargs((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysget_resource_description/s(s__name__s __module__sget_resource_realmssget_resource_urlsNonesget_resource_description(((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysIResourceManagers  sResourcecBsttZdZddddfZdZdZdZeeeedZ eeeed Z eed Z RS( sResource identifier. This specifies as precisely as possible *which* resource from a Trac environment is manipulated. A resource is identified by: (- a `project` identifier) 0.12? - a `realm` (a string like `'wiki'` or `'ticket'`) - an `id`, which uniquely identifies a resource within its realm. If the `id` information is not set, then the resource represents the realm as a whole. - an optional `version` information. If `version` is `None`, this refers by convention to the latest version of the resource. Some generic and commonly used rendering methods are associated as well to the Resource object. Those properties and methods actually delegate the real work to the Resource's manager. srealmsidsversionsparentcCs|itjodSng}|}x{|os|i}|io|dt|i7}n|itj o|dt|i7}n|i ||i }q'Wddi t |SdS(Ns s:s@s s, ( sselfsrealmsNonespathsrsnamesidsunicodesversionsappendsparentsjoinsreversed(sselfsnamesrspath((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pys__repr__bs   cCsM|i|ijo6|i|ijo#|i|ijo|i|ijSdS(N(sselfsrealmsothersidsversionsparent(sselfsother((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pys__eq__qscCsNf}|}x1|o)||i|i|if7}|i}qWt|SdS(s7Hash this resource descriptor, including its hierarchy.N(spathsselfscurrentsrealmsidsversionsparentshash(sselfscurrentspath((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pys__hash__wscCsC|}t|to|tjo|tjo |tjo|Sn |i}|tjo |i}n|tjo'||ijo |i}qt}n|tjo |i}qnF|tjo t}n|tjo t}n|tjo t}nt t|i |}||_||_||_||_|SdS(sCreate a new Resource object from a specification. :param resource_or_realm: this can be either: - a `Resource`, which is then used as a base for making a copy - a `basestring`, used to specify a `realm` :param id: the resource identifier :param version: the version or `None` for indicating the latest version >>> main = Resource('wiki', 'WikiStart') >>> repr(main) "" >>> Resource(main) is main True >>> main3 = Resource(main, version=3) >>> repr(main3) "" >>> main0 = main3(version=0) >>> repr(main0) "" In a copy, if `id` is overriden, then the original `version` value will not be reused. >>> repr(Resource(main3, id="WikiEnd")) "" >>> repr(Resource(None)) '' N( sresource_or_realmsrealms isinstancesResourcesidsFalsesversionsparentsNonessupersclss__new__sresource(sclssresource_or_realmsidsversionsparentsrealmsresource((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pys__new__s4!'                 cCs+t|tjo|p||||SdS(sCreate a new Resource using the current resource as a template. Optional keyword arguments can be given to override `id` and `version`. N(sResourcesrealmsFalsesselfsidsversionsparent(sselfsrealmsidsversionsparent((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pys__call__scCs|i||||SdS(sRetrieve a child resource for a secondary `realm`. Same as `__call__`, except that this one sets the parent to `self`. N(sselfs__call__srealmsidsversion(sselfsrealmsidsversion((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pyschilds( s__name__s __module__s__doc__s __slots__s__repr__s__eq__s__hash__sNonesFalses__new__s__call__schild(((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysResourceKs    @ sResourceSystemcBs5tZdZeeZdZdZdZRS(sResource identification and description. This component makes the link between `Resource` identifiers and their corresponding manager `Component`. cCs t|_dS(N(sNonesselfs_resource_managers_map(sself((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pys__init__scCsg|i oHh}x2|iD]'}x|iD]}|||>> from trac.test import EnvironmentStub >>> from trac.web.href import Href >>> env = EnvironmentStub() >>> href = Href('/trac.cgi') >>> main = Resource('generic', 'Main') >>> get_resource_url(env, main, href) '/trac.cgi/generic/Main' >>> get_resource_url(env, main(version=3), href) '/trac.cgi/generic/Main?version=3' >>> get_resource_url(env, main(version=3), href) '/trac.cgi/generic/Main?version=3' >>> get_resource_url(env, main(version=3), href, action='diff') '/trac.cgi/generic/Main?action=diff&version=3' >>> get_resource_url(env, main(version=3), href, action='diff', version=5) '/trac.cgi/generic/Main?action=diff&version=5' sget_resource_urlsversionN(sResourceSystemsenvsget_resource_managersresourcesrealmsmanagershasattrsversionsargssupdateskwargsshrefsidsget_resource_url(senvsresourceshrefskwargssargssmanager((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysget_resource_urls! sdefaultcKst|i|i}| pt|d oHd|i|if}|djo|t dd|i 7}n|Sn|i |||SdS(s:Retrieve a standardized description for the given resource. This function delegates the work to the resource manager for that resource if it implements a `get_resource_description` method, otherwise reverts to simple presentation of the realm and identifier information. :param env: the `Environment` where `IResourceManager` components live :param resource: the `Resource` object specifying the Trac resource :param format: which formats to use for the description Additional keyword arguments can be provided and will be propagated to resource manager that might make use of them (typically, a `context` parameter for creating context dependent output). >>> from trac.test import EnvironmentStub >>> env = EnvironmentStub() >>> main = Resource('generic', 'Main') >>> get_resource_description(env, main) 'generic:Main' >>> get_resource_description(env, main(version=3)) 'generic:Main' >>> get_resource_description(env, main(version=3), format='summary') 'generic:Main at version 3' sget_resource_descriptions%s:%sssummarys at version %(version)ssversionN(sResourceSystemsenvsget_resource_managersresourcesrealmsmanagershasattrsidsnamesformats_sversionsget_resource_descriptionskwargs(senvsresourcesformatskwargssnamesmanager((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysget_resource_description(s cCst||SdS(N(sget_resource_descriptionsenvsresource(senvsresource((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysget_resource_nameNscCst||dSdS(Nscompact(sget_resource_descriptionsenvsresource(senvsresource((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysget_resource_shortnameQscCst||dSdS(Nssummary(sget_resource_descriptionsenvsresource(senvsresource((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysget_resource_summaryTsscKs|tddfjot||||Snt|ddjo|ipdi d}xo|i dD]^}|ddfjoqmqm|djo|o|i qqm|o|i |qmqmWt||d|o di|pt||SdS(s/Build an URL relative to a resource given as reference. :param path: path leading to another resource within the same realm. >>> from trac.test import EnvironmentStub >>> env = EnvironmentStub() >>> from trac.web.href import Href >>> href = Href('/trac.cgi') >>> main = Resource('wiki', 'Main', version=3) Without parameters, return the canonical URL for the resource, like `get_resource_url` does. >>> get_relative_url(env, main, href) '/trac.cgi/wiki/Main?version=3' Paths are relative to the given resource: >>> get_relative_url(env, main, href, '.') '/trac.cgi/wiki/Main?version=3' >>> get_relative_url(env, main, href, './Sub') '/trac.cgi/wiki/Main/Sub' >>> get_relative_url(env, main, href, './Sub/Infra') '/trac.cgi/wiki/Main/Sub/Infra' >>> get_relative_url(env, main, href, './Sub/') '/trac.cgi/wiki/Main/Sub' >>> mainsub = main(id='Main/Sub') >>> get_relative_url(env, mainsub, href, '..') '/trac.cgi/wiki/Main' >>> get_relative_url(env, main, href, '../Other') '/trac.cgi/wiki/Other' References always stay within the current resource realm: >>> get_relative_url(env, mainsub, href, '../..') '/trac.cgi/wiki' >>> get_relative_url(env, mainsub, href, '../../..') '/trac.cgi/wiki' >>> get_relative_url(env, mainsub, href, '/toplevel') '/trac.cgi/wiki/toplevel' Extra keyword arguments are forwarded as query parameters: >>> get_relative_url(env, main, href, action='diff') '/trac.cgi/wiki/Main?action=diff&version=3' ss.is/s..sidN(spathsNonesget_resource_urlsenvsresourceshrefskwargssunicodesidssplitsbasescompspopsappendsjoin(senvsresourceshrefspathskwargsscompsbase((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysget_relative_urlWs60 )cCsldkl}l}t|||d|}t || o(|i |dt |||i }n|SdS(sUtility for generating a link `Element` to the given resource. Some component manager may directly use an extra `context` parameter in order to directly generate rich content. Otherwise, the textual output is wrapped in a link to the resource. (sElementstagscontextshrefN( sgenshi.buildersElementstagsget_resource_descriptionsenvsresourcesformatscontextslinks isinstancesasget_resource_urlshref(senvscontextsresourcesformatsElementstagslink((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pysrender_resource_links (N(s trac.corestrac.util.compatsreversedstrac.util.translations_s TracErrorsResourceNotFounds InterfacesIResourceManagersobjectsResources ComponentsResourceSystemsget_resource_urlsget_resource_descriptionsget_resource_namesget_resource_shortnamesget_resource_summarysget_relative_urlsrender_resource_link( sget_relative_urlsget_resource_namesResourcesreversedsget_resource_summarysResourceSystemsget_resource_shortnamesget_resource_descriptionsResourceNotFoundsget_resource_urlsIResourceManagersrender_resource_links_((s2build/bdist.darwin-9.5.0-i386/egg/trac/resource.pys?s  /) * &    FPKkh9 V trac/test.py#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2003-2008 Edgewall Software # Copyright (C) 2003-2005 Jonas Borgström # Copyright (C) 2005 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Jonas Borgström # Christopher Lenz import os import unittest import sys import pkg_resources from fnmatch import fnmatch from trac.config import Configuration from trac.core import Component, ComponentManager, ExtensionPoint from trac.env import Environment from trac.db.sqlite_backend import SQLiteConnection from trac.ticket.default_workflow import load_workflow_config_snippet def Mock(bases=(), *initargs, **kw): """ Simple factory for dummy classes that can be used as replacement for the real implementation in tests. Base classes for the mock can be specified using the first parameter, which must be either a tuple of class objects or a single class object. If the bases parameter is omitted, the base class of the mock will be object. So to create a mock that is derived from the builtin dict type, you can do: >>> mock = Mock(dict) >>> mock['foo'] = 'bar' >>> mock['foo'] 'bar' Attributes of the class are provided by any additional keyword parameters. >>> mock = Mock(foo='bar') >>> mock.foo 'bar' Objects produces by this function have the special feature of not requiring the 'self' parameter on methods, because you should keep data at the scope of the test function. So you can just do: >>> mock = Mock(add=lambda x,y: x+y) >>> mock.add(1, 1) 2 To access attributes from the mock object from inside a lambda function, just access the mock itself: >>> mock = Mock(dict, do=lambda x: 'going to the %s' % mock[x]) >>> mock['foo'] = 'bar' >>> mock.do('foo') 'going to the bar' Because assignments or other types of statements don't work in lambda functions, assigning to a local variable from a mock function requires some extra work: >>> myvar = [None] >>> mock = Mock(set=lambda x: myvar.__setitem__(0, x)) >>> mock.set(1) >>> myvar[0] 1 """ if not isinstance(bases, tuple): bases = (bases,) cls = type('Mock', bases, {}) mock = cls(*initargs) for k,v in kw.items(): setattr(mock, k, v) return mock class MockPerm(object): """Fake permission class. Necessary as Mock can not be used with operator overloading.""" def has_permission(self, x): return True __contains__ = has_permission def __call__(self, *a, **kw): return self def require(self, *a, **kw): pass assert_permission = require class TestSetup(unittest.TestSuite): """ Test suite decorator that allows a fixture to be setup for a complete suite of test cases. """ def setUp(self): """Sets up the fixture, and sets self.fixture if needed""" pass def tearDown(self): """Tears down the fixture""" pass def run(self, result): """Setup the fixture (self.setUp), call .setFixture on all the tests, and tear down the fixture (self.tearDown).""" self.setUp() if hasattr(self, 'fixture'): for test in self._tests: if hasattr(test, 'setFixture'): test.setFixture(self.fixture) unittest.TestSuite.run(self, result) self.tearDown() return result class TestCaseSetup(unittest.TestCase): def setFixture(self, fixture): self.fixture = fixture class InMemoryDatabase(SQLiteConnection): """ DB-API connection object for an SQLite in-memory database, containing all the default Trac tables but no data. """ def __init__(self): SQLiteConnection.__init__(self, ':memory:') cursor = self.cnx.cursor() from trac.db_default import schema from trac.db.sqlite_backend import _to_sql for table in schema: for stmt in _to_sql(table): cursor.execute(stmt) self.cnx.commit() class EnvironmentStub(Environment): """A stub of the trac.env.Environment object for testing.""" href = abs_href = None def __init__(self, default_data=False, enable=None): """Construct a new Environment stub object. default_data: If True, populate the database with some defaults. enable: A list of component classes or name globs to activate in the stub environment. """ ComponentManager.__init__(self) Component.__init__(self) self.enabled_components = enable or ['trac.*'] self.db = InMemoryDatabase() self.systeminfo = [('Python', sys.version)] import trac self.path = os.path.dirname(trac.__file__) if not os.path.isabs(self.path): self.path = os.path.join(os.getcwd(), self.path) self.config = Configuration(None) # We have to have a ticket-workflow config for ''lots'' of things to # work. So insert the basic-workflow config here. There may be a # better solution than this. load_workflow_config_snippet(self.config, 'basic-workflow.ini') from trac.log import logger_factory self.log = logger_factory('test') from trac.web.href import Href self.href = Href('/trac.cgi') self.abs_href = Href('http://example.org/trac.cgi') from trac import db_default if default_data: cursor = self.db.cursor() for table, cols, vals in db_default.get_data(self.db): cursor.executemany("INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s' for c in cols])), vals) self.db.commit() self.known_users = [] def is_component_enabled(self, cls): for component in self.enabled_components: if component is cls: return True if isinstance(component, basestring) and \ fnmatch(cls.__module__ + '.' + cls.__name__, component): return True return False def get_db_cnx(self): return self.db def get_known_users(self, cnx=None): return self.known_users def locate(fn): """Locates a binary on the path. Returns the fully-qualified path, or None. """ import os exec_suffix = os.name == 'nt' and '.exe' or '' for p in ["."] + os.environ['PATH'].split(os.pathsep): f = os.path.join(p, fn + exec_suffix) if os.path.exists(f): return f return None INCLUDE_FUNCTIONAL_TESTS = True def suite(): import trac.tests import trac.admin.tests import trac.db.tests import trac.mimeview.tests import trac.ticket.tests import trac.util.tests import trac.versioncontrol.tests import trac.versioncontrol.web_ui.tests import trac.web.tests import trac.wiki.tests suite = unittest.TestSuite() suite.addTest(trac.tests.basicSuite()) if INCLUDE_FUNCTIONAL_TESTS: suite.addTest(trac.tests.functionalSuite()) suite.addTest(trac.admin.tests.suite()) suite.addTest(trac.db.tests.suite()) suite.addTest(trac.mimeview.tests.suite()) suite.addTest(trac.ticket.tests.suite()) suite.addTest(trac.util.tests.suite()) suite.addTest(trac.versioncontrol.tests.suite()) suite.addTest(trac.versioncontrol.web_ui.tests.suite()) suite.addTest(trac.web.tests.suite()) suite.addTest(trac.wiki.tests.suite()) return suite if __name__ == '__main__': import doctest, sys doctest.testmod(sys.modules[__name__]) #FIXME: this is a bit inelegant if '--skip-functional-tests' in sys.argv: sys.argv.remove('--skip-functional-tests') INCLUDE_FUNCTIONAL_TESTS = False unittest.main(defaultTest='suite') PKmh9M-- trac/test.pyc; Ic@sdkZdkZdkZdkZdklZdklZdklZl Z l Z dk l Z dk lZdklZfdZdefd YZd eifd YZd eifd YZdefdYZde fdYZdZeZdZedjoddkZdkZei ei!edei"joei"i#de$Znei%ddndS(N(sfnmatch(s Configuration(s ComponentsComponentManagersExtensionPoint(s Environment(sSQLiteConnection(sload_workflow_config_snippetcOsqt|t o |f}ntd|h}||}x*|iD]\}}t |||qIW|SdS(s Simple factory for dummy classes that can be used as replacement for the real implementation in tests. Base classes for the mock can be specified using the first parameter, which must be either a tuple of class objects or a single class object. If the bases parameter is omitted, the base class of the mock will be object. So to create a mock that is derived from the builtin dict type, you can do: >>> mock = Mock(dict) >>> mock['foo'] = 'bar' >>> mock['foo'] 'bar' Attributes of the class are provided by any additional keyword parameters. >>> mock = Mock(foo='bar') >>> mock.foo 'bar' Objects produces by this function have the special feature of not requiring the 'self' parameter on methods, because you should keep data at the scope of the test function. So you can just do: >>> mock = Mock(add=lambda x,y: x+y) >>> mock.add(1, 1) 2 To access attributes from the mock object from inside a lambda function, just access the mock itself: >>> mock = Mock(dict, do=lambda x: 'going to the %s' % mock[x]) >>> mock['foo'] = 'bar' >>> mock.do('foo') 'going to the bar' Because assignments or other types of statements don't work in lambda functions, assigning to a local variable from a mock function requires some extra work: >>> myvar = [None] >>> mock = Mock(set=lambda x: myvar.__setitem__(0, x)) >>> mock.set(1) >>> myvar[0] 1 sMockN( s isinstancesbasesstuplestypesclssinitargssmockskwsitemssksvssetattr(sbasessinitargsskwsksvsmockscls((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pysMock!s/   sMockPermcBs5tZdZdZeZdZdZeZRS(sWFake permission class. Necessary as Mock can not be used with operator overloading.cCstSdS(N(sTrue(sselfsx((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pyshas_permission]scOs|SdS(N(sself(sselfsaskw((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pys__call__ascOsdS(N((sselfsaskw((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pysrequireds(s__name__s __module__s__doc__shas_permissions __contains__s__call__srequiresassert_permission(((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pysMockPermZs    s TestSetupcBs)tZdZdZdZdZRS(sh Test suite decorator that allows a fixture to be setup for a complete suite of test cases. cCsdS(s4Sets up the fixture, and sets self.fixture if neededN((sself((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pyssetUpnscCsdS(sTears down the fixtureN((sself((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pystearDownrscCs{|it|do<x9|iD]*}t|do|i|iq$q$Wntii |||i |SdS(suSetup the fixture (self.setUp), call .setFixture on all the tests, and tear down the fixture (self.tearDown).sfixtures setFixtureN( sselfssetUpshasattrs_testsstests setFixturesfixturesunittests TestSuitesrunsresultstearDown(sselfsresultstest((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pysrunvs   (s__name__s __module__s__doc__ssetUpstearDownsrun(((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pys TestSetupis   s TestCaseSetupcBstZdZRS(NcCs ||_dS(N(sfixturesself(sselfsfixture((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pys setFixtures(s__name__s __module__s setFixture(((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pys TestCaseSetupssInMemoryDatabasecBstZdZdZRS(s| DB-API connection object for an SQLite in-memory database, containing all the default Trac tables but no data. cCsti|d|ii}dkl}dkl}x2|D]*}x!||D]}|i |qSWq@W|ii dS(Ns:memory:(sschema(s_to_sql( sSQLiteConnections__init__sselfscnxscursorstrac.db_defaultsschemastrac.db.sqlite_backends_to_sqlstablesstmtsexecutescommit(sselfsstmtscursors_to_sqlstablesschema((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pys__init__s   (s__name__s __module__s__doc__s__init__(((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pysInMemoryDatabases sEnvironmentStubcBsEtZdZeZZeedZdZdZ edZ RS(s6A stub of the trac.env.Environment object for testing.c Csti|ti||pdg|_t|_dti fg|_ dk }t i i|i|_ t i i|i  o%t i it i|i |_ ntt|_t|iddkl} | d|_dkl}|d|_|d |_d k l}|o|ii }xs|i!|iD]_\} }} |i%d | d i|d igi&} |D]}| d q~ f| qGW|ii)ng|_*dS(sConstruct a new Environment stub object. default_data: If True, populate the database with some defaults. enable: A list of component classes or name globs to activate in the stub environment. strac.*sPythonNsbasic-workflow.ini(slogger_factorystest(sHrefs /trac.cgishttp://example.org/trac.cgi(s db_defaultsINSERT INTO %s (%s) VALUES (%s)s,s%s(+sComponentManagers__init__sselfs Componentsenablesenabled_componentssInMemoryDatabasesdbssyssversions systeminfostracsosspathsdirnames__file__sisabssjoinsgetcwds ConfigurationsNonesconfigsload_workflow_config_snippetstrac.logslogger_factoryslogs trac.web.hrefsHrefshrefsabs_hrefs db_defaults default_datascursorsget_datastablescolssvalss executemanysappends_[1]scscommits known_users( sselfs default_datasenablescursorstracscscolss db_defaultsHrefs_[1]svalsstableslogger_factory((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pys__init__s4    %   I cCsgx\|iD]Q}||jotSnt|tot|id|i |otSq q Wt SdS(Ns.( sselfsenabled_componentss componentsclssTrues isinstances basestringsfnmatchs __module__s__name__sFalse(sselfsclss component((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pysis_component_enableds  . cCs |iSdS(N(sselfsdb(sself((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pys get_db_cnxscCs |iSdS(N(sselfs known_users(sselfscnx((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pysget_known_userss( s__name__s __module__s__doc__sNoneshrefsabs_hrefsFalses__init__sis_component_enableds get_db_cnxsget_known_users(((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pysEnvironmentStubs  + cCsdk}|idjodpd}x\dg|idi|iD]:}|ii|||}|ii |o|SqGqGWt SdS(sRLocates a binary on the path. Returns the fully-qualified path, or None. Nsnts.exess.sPATH( sossnames exec_suffixsenvironssplitspathsepspspathsjoinsfnsfsexistssNone(sfns exec_suffixsfspsos((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pyslocates ! cCsdk}dk}dk}dk}dk}dk}dk}dk}dk }dk }t i }|i|iito|i|iin|i|iii |i|iii |i|iii |i|iii |i|iii |i|iii |i|iiii |i|iii |i|iii |SdS(N(s trac.testsstracstrac.admin.testss trac.db.testsstrac.mimeview.testsstrac.ticket.testsstrac.util.testsstrac.versioncontrol.testss trac.versioncontrol.web_ui.testsstrac.web.testsstrac.wiki.testssunittests TestSuitessuitesaddTeststestss basicSuitesINCLUDE_FUNCTIONAL_TESTSsfunctionalSuitesadminsdbsmimeviewsticketsutilsversioncontrolsweb_uiswebswiki(ssuitestrac((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pyssuites0           s__main__s--skip-functional-testss defaultTestssuite(&sossunittestssyss pkg_resourcessfnmatchs trac.configs Configurations trac.cores ComponentsComponentManagersExtensionPointstrac.envs Environmentstrac.db.sqlite_backendsSQLiteConnectionstrac.ticket.default_workflowsload_workflow_config_snippetsMocksobjectsMockPerms TestSuites TestSetupsTestCases TestCaseSetupsInMemoryDatabasesEnvironmentStubslocatesTruesINCLUDE_FUNCTIONAL_TESTSssuites__name__sdocteststestmodsmodulessargvsremovesFalsesmain(sEnvironmentStubsMockPermsunittests EnvironmentsInMemoryDatabasesExtensionPointsMocks Components TestSetupsSQLiteConnectionssuites ConfigurationslocatesComponentManagerssyss pkg_resourcessdoctestsINCLUDE_FUNCTIONAL_TESTSsload_workflow_config_snippetsfnmatchs TestCaseSetupsos((s.build/bdist.darwin-9.5.0-i386/egg/trac/test.pys?s2          9@    PKkh9c  trac/admin/__init__.py# -*- coding: utf-8 -*- # # Copyright (C) 2006 Edgewall Software # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. from trac.admin.api import * PKmh9*ֻtrac/admin/__init__.pyc; Ic@s dkTdS((s*N(strac.admin.api(((s8build/bdist.darwin-9.5.0-i386/egg/trac/admin/__init__.pys?sPKkh9(@Ftrac/admin/api.py# -*- coding: utf-8 -*- # # Copyright (C) 2006 Edgewall Software # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. from trac.core import * class IAdminPanelProvider(Interface): """Extension point interface for adding panels to the web-based administration interface. """ def get_admin_panels(req): """Return a list of available admin panels. The items returned by this function must be tuples of the form `(category, category_label, page, page_label)`. """ def render_admin_panel(req, category, page, path_info): """Process a request for an admin panel. This function should return a tuple of the form `(template, data)`, where `template` is the name of the template to use and `data` is the data to be passed to the template. """ PKmh9Z @rtrac/admin/api.pyc; Ic@s!dkTdefdYZdS((s*sIAdminPanelProvidercBs tZdZdZdZRS(s_Extension point interface for adding panels to the web-based administration interface. cCsdS(sReturn a list of available admin panels. The items returned by this function must be tuples of the form `(category, category_label, page, page_label)`. N((sreq((s3build/bdist.darwin-9.5.0-i386/egg/trac/admin/api.pysget_admin_panelsscCsdS(sProcess a request for an admin panel. This function should return a tuple of the form `(template, data)`, where `template` is the name of the template to use and `data` is the data to be passed to the template. N((sreqscategoryspages path_info((s3build/bdist.darwin-9.5.0-i386/egg/trac/admin/api.pysrender_admin_panels(s__name__s __module__s__doc__sget_admin_panelssrender_admin_panel(((s3build/bdist.darwin-9.5.0-i386/egg/trac/admin/api.pysIAdminPanelProviders  N(s trac.cores InterfacesIAdminPanelProvider(sIAdminPanelProvider((s3build/bdist.darwin-9.5.0-i386/egg/trac/admin/api.pys?sPKkh9V?wwtrac/admin/console.py#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2003-2008 Edgewall Software # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. import cmd from datetime import datetime import getpass import locale import os import pkg_resources import shlex import shutil import StringIO import sys import time import traceback import urllib from trac import __version__ as VERSION from trac import perm, util, db_default from trac.core import TracError from trac.env import Environment from trac.perm import PermissionSystem from trac.ticket.model import * from trac.util import arity from trac.util.datefmt import parse_date, format_date, format_datetime, utc from trac.util.html import html from trac.util.text import to_unicode, wrap, unicode_quote, unicode_unquote, \ print_table, console_print from trac.util.translation import _, ngettext from trac.wiki import WikiPage from trac.wiki.api import WikiSystem from trac.wiki.macros import WikiMacroBase TRAC_VERSION = pkg_resources.get_distribution('Trac').version def printout(*args): console_print(sys.stdout, *args) def printerr(*args): console_print(sys.stderr, *args) def copytree(src, dst, symlinks=False, skip=[]): """Recursively copy a directory tree using copy2() (from shutil.copytree.) Added a `skip` parameter consisting of absolute paths which we don't want to copy. """ def str_path(path): if isinstance(path, unicode): path = path.encode(sys.getfilesystemencoding() or locale.getpreferredencoding()) return path skip = [str_path(f) for f in skip] def copytree_rec(src, dst): names = os.listdir(src) os.mkdir(dst) errors = [] for name in names: srcname = os.path.join(src, name) if srcname in skip: continue dstname = os.path.join(dst, name) try: if symlinks and os.path.islink(srcname): linkto = os.readlink(srcname) os.symlink(linkto, dstname) elif os.path.isdir(srcname): copytree_rec(srcname, dstname) else: shutil.copy2(srcname, dstname) # XXX What about devices, sockets etc.? except (IOError, os.error), why: errors.append((srcname, dstname, why)) if errors: raise shutil.Error, errors copytree_rec(str_path(src), str_path(dst)) class TracAdmin(cmd.Cmd): intro = '' doc_header = 'Trac Admin Console %(version)s\n' \ 'Available Commands:\n' \ % {'version': TRAC_VERSION} ruler = '' prompt = "Trac> " __env = None _date_format = '%Y-%m-%d' _datetime_format = '%Y-%m-%d %H:%M:%S' _date_format_hint = 'YYYY-MM-DD' def __init__(self, envdir=None): cmd.Cmd.__init__(self) self.interactive = False if envdir: self.env_set(os.path.abspath(envdir)) self._permsys = None def emptyline(self): pass def onecmd(self, line): """`line` may be a `str` or an `unicode` object""" try: if isinstance(line, str): if self.interactive: encoding = sys.stdin.encoding else: encoding = locale.getpreferredencoding() # sys.argv line = to_unicode(line, encoding) if self.interactive: line = line.replace('\\', '\\\\') rv = cmd.Cmd.onecmd(self, line) or 0 except SystemExit: raise except TracError, e: printerr(_("Command failed:"), e) rv = 2 if not self.interactive: return rv def run(self): self.interactive = True printout(_("""Welcome to trac-admin %(version)s Interactive Trac administration console. Copyright (c) 2003-2008 Edgewall Software Type: '?' or 'help' for help on commands. """, version=TRAC_VERSION)) self.cmdloop() ## ## Environment methods ## def env_set(self, envname, env=None): self.envname = envname self.prompt = "Trac [%s]> " % self.envname if env is not None: self.__env = env def env_check(self): try: self.__env = Environment(self.envname) except: return 0 return 1 def env_open(self): try: if not self.__env: self.__env = Environment(self.envname) return self.__env except Exception, e: printerr(_("Failed to open environment."), e) traceback.print_exc() sys.exit(1) def db_open(self): return self.env_open().get_db_cnx() def db_query(self, sql, cursor=None, params=None): if not cursor: cnx = self.db_open() cursor = cnx.cursor() if params: cursor.execute(sql, params) else: cursor.execute(sql) for row in cursor: yield row def db_update(self, sql, cursor=None, params=None): if not cursor: cnx = self.db_open() cursor = cnx.cursor() else: cnx = None if params: cursor.execute(sql, params) else: cursor.execute(sql) if cnx: cnx.commit() ## ## Utility methods ## def arg_tokenize (self, argstr): """`argstr` is an `unicode` string ... but shlex is not unicode friendly. """ return [unicode(token, 'utf-8') for token in shlex.split(argstr.encode('utf-8'))] or [''] def word_complete (self, text, words): return [a for a in words if a.startswith (text)] def print_doc(cls, docs, stream=None): if stream is None: stream = sys.stdout if not docs: return for cmd, doc in docs: console_print(stream, cmd) console_print(stream, '\t-- %s\n' % doc) print_doc = classmethod(print_doc) def get_component_list(self): rows = self.db_query("SELECT name FROM component") return [row[0] for row in rows] def get_user_list(self): rows = self.db_query("SELECT DISTINCT username FROM permission") return [row[0] for row in rows] def get_wiki_list(self): rows = self.db_query('SELECT DISTINCT name FROM wiki') return [row[0] for row in rows] def get_dir_list(self, pathstr, justdirs=False): dname = os.path.dirname(pathstr) d = os.path.join(os.getcwd(), dname) dlist = os.listdir(d) if justdirs: result = [] for entry in dlist: try: if os.path.isdir(entry): result.append(entry) except: pass else: result = dlist return result def get_enum_list(self, type): rows = self.db_query("SELECT name FROM enum WHERE type=%s", params=[type]) return [row[0] for row in rows] def get_milestone_list(self): rows = self.db_query("SELECT name FROM milestone") return [row[0] for row in rows] def get_version_list(self): rows = self.db_query("SELECT name FROM version") return [row[0] for row in rows] def _format_date(self, t): return format_date(t, self._date_format) def _format_datetime(self, t): return format_datetime(t, self._datetime_format) ## ## Available Commands ## ## Help _help_help = [('help', 'Show documentation')] def all_docs(cls): return (cls._help_help + cls._help_initenv + cls._help_hotcopy + cls._help_resync + cls._help_upgrade + cls._help_deploy + cls._help_permission + cls._help_wiki + cls._help_ticket + cls._help_ticket_type + cls._help_priority + cls._help_severity + cls._help_component + cls._help_version + cls._help_milestone + cls._help_resolution) all_docs = classmethod(all_docs) def do_help(self, line=None): arg = self.arg_tokenize(line) if arg[0]: try: doc = getattr(self, "_help_" + arg[0]) self.print_doc(doc) except AttributeError: printerr(_("No documentation found for '%(cmd)s'", cmd=arg[0])) else: printout(_("trac-admin - The Trac Administration Console " "%(version)s", version=TRAC_VERSION)) if not self.interactive: print printout(_("Usage: trac-admin " "[command [subcommand] [option ...]]\n") ) printout(_("Invoking trac-admin without command starts " "interactive mode.")) self.print_doc(self.all_docs()) ## Quit / EOF _help_quit = [['quit', 'Exit the program']] _help_exit = _help_quit _help_EOF = _help_quit def do_quit(self, line): print sys.exit() do_exit = do_quit # Alias do_EOF = do_quit # Alias # Component _help_component = [('component list', 'Show available components'), ('component add ', 'Add a new component'), ('component rename ', 'Rename a component'), ('component remove ', 'Remove/uninstall component'), ('component chown ', 'Change component ownership')] def complete_component(self, text, line, begidx, endidx): if begidx in (16, 17): comp = self.get_component_list() elif begidx > 15 and line.startswith('component chown '): comp = self.get_user_list() else: comp = ['list', 'add', 'rename', 'remove', 'chown'] return self.word_complete(text, comp) def do_component(self, line): arg = self.arg_tokenize(line) if arg[0] == 'list': self._do_component_list() elif arg[0] == 'add' and len(arg)==3: name = arg[1] owner = arg[2] self._do_component_add(name, owner) elif arg[0] == 'rename' and len(arg)==3: name = arg[1] newname = arg[2] self._do_component_rename(name, newname) elif arg[0] == 'remove' and len(arg)==2: name = arg[1] self._do_component_remove(name) elif arg[0] == 'chown' and len(arg)==3: name = arg[1] owner = arg[2] self._do_component_set_owner(name, owner) else: self.do_help ('component') def _do_component_list(self): data = [] for c in Component.select(self.env_open()): data.append((c.name, c.owner)) print_table(data, ['Name', 'Owner']) def _do_component_add(self, name, owner): component = Component(self.env_open()) component.name = name component.owner = owner component.insert() def _do_component_rename(self, name, newname): component = Component(self.env_open(), name) component.name = newname component.update() def _do_component_remove(self, name): component = Component(self.env_open(), name) component.delete() def _do_component_set_owner(self, name, owner): component = Component(self.env_open(), name) component.owner = owner component.update() ## Permission _help_permission = [('permission list [user]', 'List permission rules'), ('permission add [action] [...]', 'Add a new permission rule'), ('permission remove [action] [...]', 'Remove permission rule')] def complete_permission(self, text, line, begidx, endidx): argv = self.arg_tokenize(line) argc = len(argv) if line[-1] == ' ': # Space starts new argument argc += 1 if argc == 2: comp = ['list', 'add', 'remove'] elif argc >= 4: comp = perm.permissions + perm.meta_permissions.keys() comp.sort() return self.word_complete(text, comp) def do_permission(self, line): arg = self.arg_tokenize(line) if arg[0] == 'list': user = None if len(arg) > 1: user = arg[1] self._do_permission_list(user) elif arg[0] == 'add' and len(arg) >= 3: user = arg[1] for action in arg[2:]: self._do_permission_add(user, action) elif arg[0] == 'remove' and len(arg) >= 3: user = arg[1] for action in arg[2:]: self._do_permission_remove(user, action) else: self.do_help('permission') def _do_permission_list(self, user=None): if not self._permsys: self._permsys = PermissionSystem(self.env_open()) if user: rows = [] perms = self._permsys.get_user_permissions(user) for action in perms: if perms[action]: rows.append((user, action)) else: rows = self._permsys.get_all_permissions() rows.sort() print_table(rows, ['User', 'Action']) print printout(_("Available actions:")) actions = self._permsys.get_actions() actions.sort() text = ', '.join(actions) printout(wrap(text, initial_indent=' ', subsequent_indent=' ', linesep='\n')) print def _do_permission_add(self, user, action): if not self._permsys: self._permsys = PermissionSystem(self.env_open()) if not action.islower() and not action.isupper(): printout(_("Group names must be in lower case and actions in " "upper case")) return self._permsys.grant_permission(user, action) def _do_permission_remove(self, user, action): if not self._permsys: self._permsys = PermissionSystem(self.env_open()) rows = self._permsys.get_all_permissions() if action == '*': for row in rows: if user != '*' and user != row[0]: continue self._permsys.revoke_permission(row[0], row[1]) else: for row in rows: if action != row[1]: continue if user != '*' and user != row[0]: continue self._permsys.revoke_permission(row[0], row[1]) ## Initenv _help_initenv = [('initenv', 'Create and initialize a new environment interactively'), ('initenv ', 'Create and initialize a new environment from arguments')] def do_initdb(self, line): self.do_initenv(line) def get_initenv_args(self): returnvals = [] printout(_("Creating a new Trac environment at %(envname)s", envname=self.envname)) printout(_(""" Trac will first ask a few questions about your environment in order to initialize and prepare the project database. Please enter the name of your project. This name will be used in page titles and descriptions. """)) dp = 'My Project' returnvals.append(raw_input(_("Project Name [%(default)s]> ", default=dp)).strip() or dp) printout(_(""" Please specify the connection string for the database to use. By default, a local SQLite database is created in the environment directory. It is also possible to use an already existing PostgreSQL database (check the Trac documentation for the exact connection string syntax). """)) ddb = 'sqlite:db/trac.db' prompt = _("Database connection string [%(default)s]> ", default=ddb) returnvals.append(raw_input(prompt).strip() or ddb) printout(_(""" Please specify the type of version control system, By default, it will be svn. If you don't want to use Trac with version control integration, choose the default here and don\'t specify a repository directory. in the next question. """)) drpt = 'svn' prompt = _("Repository type [%(default)s]> ", default=drpt) returnvals.append(raw_input(prompt).strip() or drpt) printout(_(""" Please specify the absolute path to the version control repository, or leave it blank to use Trac without a repository. You can also set the repository location later. """)) prompt = _("Path to repository [/path/to/repos]> ") returnvals.append(raw_input(prompt).strip()) print return returnvals def do_initenv(self, line): def initenv_error(msg): printerr(_("Initenv for '%(env)s' failed.", env=self.envname), "\n", msg) if self.env_check(): initenv_error("Does an environment already exist?") return 2 if os.path.exists(self.envname) and os.listdir(self.envname): initenv_error("Directory exists and is not empty.") return 2 arg = self.arg_tokenize(line) inherit_file = '' for num, item in enumerate(arg): if item.startswith('--inherit='): inherit_file = arg.pop(num)[10:] arg = arg or [''] # Reset to usual empty in case we popped the only one project_name = None db_str = None repository_dir = None if len(arg) == 1 and not arg[0]: returnvals = self.get_initenv_args() project_name, db_str, repository_type, repository_dir = returnvals elif len(arg) != 4: initenv_error('Wrong number of arguments: %d' % len(arg)) return 2 else: project_name, db_str, repository_type, repository_dir = arg[:4] try: printout(_("Creating and Initializing Project")) options = [ ('trac', 'database', db_str), ('trac', 'repository_type', repository_type), ('trac', 'repository_dir', repository_dir), ('project', 'name', project_name), ] if inherit_file: options.append(('inherit', 'file', inherit_file)) try: self.__env = Environment(self.envname, create=True, options=options) except Exception, e: initenv_error('Failed to create environment.') printerr(e) traceback.print_exc() sys.exit(1) # Add a few default wiki pages printout(_(" Installing default wiki pages")) cnx = self.__env.get_db_cnx() cursor = cnx.cursor() pages_dir = pkg_resources.resource_filename('trac.wiki', 'default-pages') self._do_wiki_load(pages_dir, cursor) cnx.commit() if repository_dir: try: repos = self.__env.get_repository() if repos: printout(_(" Indexing repository")) repos.sync(self._resync_feedback) except TracError, e: printerr(_(""" --------------------------------------------------------------------- Warning: couldn't index the repository. This can happen for a variety of reasons: wrong repository type, no appropriate third party library for this repository type, no actual repository at the specified repository path... You can nevertheless start using your Trac environment, but you'll need to check again your trac.ini file and the [trac] repository_type and repository_path settings in order to enable the Trac repository browser. """)) except Exception, e: initenv_error(to_unicode(e)) traceback.print_exc() return 2 printout(_(""" --------------------------------------------------------------------- Project environment for '%(project_name)s' created. You may now configure the environment by editing the file: %(config_path)s If you'd like to take this new project environment for a test drive, try running the Trac standalone web server `tracd`: tracd --port 8000 %(project_path)s Then point your browser to http://localhost:8000/%(project_dir)s. There you can also browse the documentation for your installed version of Trac, including information on further setup (such as deploying Trac to a real web server). The latest documentation can also always be found on the project website: http://trac.edgewall.org/ Congratulations! """, project_name=project_name, project_path=self.envname, project_dir=os.path.basename(self.envname), config_path=os.path.join(self.envname, 'conf', 'trac.ini'))) _help_resync = [('resync', 'Re-synchronize trac with the repository'), ('resync ', 'Re-synchronize only the given ')] def _resync_feedback(self, rev): sys.stdout.write(' [%s]\r' % rev) sys.stdout.flush() ## Resync def do_resync(self, line): env = self.env_open() argv = self.arg_tokenize(line) if argv: rev = argv[0] if rev: env.get_repository().sync_changeset(rev) printout(_("%(rev)s resynced.", rev=rev)) return from trac.versioncontrol.cache import CACHE_METADATA_KEYS printout(_("Resyncing repository history... ")) cnx = self.db_open() cursor = cnx.cursor() cursor.execute("DELETE FROM revision") cursor.execute("DELETE FROM node_change") cursor.executemany("DELETE FROM system WHERE name=%s", [(k,) for k in CACHE_METADATA_KEYS]) cursor.executemany("INSERT INTO system (name, value) VALUES (%s, %s)", [(k, '') for k in CACHE_METADATA_KEYS]) cnx.commit() repos = env.get_repository().sync(self._resync_feedback) cursor.execute("SELECT count(rev) FROM revision") for cnt, in cursor: printout(ngettext("%(num)s revision cached.", "%(num)s revisions cached.", num=cnt)) printout(_("Done.")) ## Wiki _help_wiki = [('wiki list', 'List wiki pages'), ('wiki remove ', 'Remove wiki page'), ('wiki export [file]', 'Export wiki page to file or stdout'), ('wiki import [file]', 'Import wiki page from file or stdin'), ('wiki dump ', 'Export all wiki pages to files named by title'), ('wiki load ', 'Import all wiki pages from directory'), ('wiki upgrade', 'Upgrade default wiki pages to current version')] def complete_wiki(self, text, line, begidx, endidx): argv = self.arg_tokenize(line) argc = len(argv) if line[-1] == ' ': # Space starts new argument argc += 1 if argc == 2: comp = ['list', 'remove', 'import', 'export', 'dump', 'load', 'upgrade'] else: if argv[1] in ('dump', 'load'): comp = self.get_dir_list(argv[-1], 1) elif argv[1] == 'remove': comp = self.get_wiki_list() elif argv[1] in ('export', 'import'): if argc == 3: comp = self.get_wiki_list() elif argc == 4: comp = self.get_dir_list(argv[-1]) return self.word_complete(text, comp) def do_wiki(self, line): arg = self.arg_tokenize(line) if arg[0] == 'list': self._do_wiki_list() elif arg[0] == 'remove' and len(arg)==2: name = arg[1] self._do_wiki_remove(name) elif arg[0] == 'import' and len(arg) == 3: title = arg[1] file = arg[2] self._do_wiki_import(file, title) elif arg[0] == 'export' and len(arg) in [2,3]: page = arg[1] file = (len(arg) == 3 and arg[2]) or None self._do_wiki_export(page, file) elif arg[0] == 'dump' and len(arg) in [1,2]: dir = (len(arg) == 2 and arg[1]) or '' self._do_wiki_dump(dir) elif arg[0] == 'load' and len(arg) in [1,2]: dir = (len(arg) == 2 and arg[1]) or '' self._do_wiki_load(dir) elif arg[0] == 'upgrade' and len(arg) == 1: self._do_wiki_load(pkg_resources.resource_filename('trac.wiki', 'default-pages'), ignore=['WikiStart', 'checkwiki.py'], create_only=['InterMapTxt']) else: self.do_help ('wiki') def _do_wiki_list(self): rows = self.db_query("SELECT name, max(version), max(time) " "FROM wiki GROUP BY name ORDER BY name") print_table([(r[0], int(r[1]), self._format_datetime(datetime.fromtimestamp(r[2], utc))) for r in rows], ['Title', 'Edits', 'Modified']) def _do_wiki_remove(self, name): if name.endswith('*'): env = self.env_open() pages = [(p,) for p in WikiSystem(env).get_pages(name.rstrip('*') \ or None)] for p in pages: page = WikiPage(env, p[0]) page.delete() print_table(pages, ['Deleted pages']) else: page = WikiPage(self.env_open(), name) page.delete() def _do_wiki_import(self, filename, title, cursor=None, create_only=[]): if not os.path.isfile(filename): raise Exception, '%s is not a file' % filename f = open(filename,'r') data = to_unicode(f.read(), 'utf-8') # Make sure we don't insert the exact same page twice rows = self.db_query("SELECT text FROM wiki WHERE name=%s " "ORDER BY version DESC LIMIT 1", cursor, params=(title,)) old = list(rows) if old and title in create_only: printout(' %s already exists.' % title) return False if old and data == old[0][0]: printout(' %s already up to date.' % title) return False f.close() self.db_update("INSERT INTO wiki(version,name,time,author,ipnr,text) " " SELECT 1+COALESCE(max(version),0),%s,%s," " 'trac','127.0.0.1',%s FROM wiki " " WHERE name=%s", cursor, (title, int(time.time()), data, title)) return True def _do_wiki_export(self, page, filename=''): data = self.db_query("SELECT text FROM wiki WHERE name=%s " "ORDER BY version DESC LIMIT 1", params=[page]) text = data.next()[0] if not filename: printout(text) else: if os.path.isfile(filename): raise Exception("File '%s' exists" % filename) f = open(filename,'w') f.write(text.encode('utf-8')) f.close() def _do_wiki_dump(self, dir): pages = self.get_wiki_list() if not os.path.isdir(dir): if not os.path.exists(dir): os.mkdir(dir) else: raise TracError("%s is not a directory" % dir) for p in pages: dst = os.path.join(dir, unicode_quote(p, '')) printout(_(" %(src)s => %(dst)s", src=p, dst=dst)) self._do_wiki_export(p, dst) def _do_wiki_load(self, dir, cursor=None, ignore=[], create_only=[]): cons_charset = getattr(sys.stdout, 'encoding', None) or 'utf-8' for page in os.listdir(dir): if page in ignore: continue filename = os.path.join(dir, page) page = unicode_unquote(page.encode('utf-8')) if os.path.isfile(filename): if self._do_wiki_import(filename, page, cursor, create_only): printout(_(" %(page)s imported from %(filename)s", filename=filename, page=page)) ## Ticket _help_ticket = [('ticket remove ', 'Remove ticket')] def complete_ticket(self, text, line, begidx, endidx): argv = self.arg_tokenize(line) argc = len(argv) if line[-1] == ' ': # Space starts new argument argc += 1 comp = [] if argc == 2: comp = ['remove'] return self.word_complete(text, comp) def do_ticket(self, line): arg = self.arg_tokenize(line) if arg[0] == 'remove' and len(arg)==2: try: number = int(arg[1]) except ValueError: printerr(_(" must be a number")) return self._do_ticket_remove(number) else: self.do_help ('ticket') def _do_ticket_remove(self, num): ticket = Ticket(self.env_open(), num) ticket.delete() printout(_("Ticket %(num)s and all associated data removed.", num=num)) ## (Ticket) Type _help_ticket_type = [('ticket_type list', 'Show possible ticket types'), ('ticket_type add ', 'Add a ticket type'), ('ticket_type change ', 'Change a ticket type'), ('ticket_type remove ', 'Remove a ticket type'), ('ticket_type order up|down', 'Move a ticket type up or down in the list')] def complete_ticket_type (self, text, line, begidx, endidx): if begidx == 19: comp = self.get_enum_list ('ticket_type') elif begidx < 18: comp = ['list', 'add', 'change', 'remove', 'order'] return self.word_complete(text, comp) def do_ticket_type(self, line): self._do_enum('ticket_type', line) ## (Ticket) Priority _help_priority = [('priority list', 'Show possible ticket priorities'), ('priority add ', 'Add a priority value option'), ('priority change ', 'Change a priority value'), ('priority remove ', 'Remove priority value'), ('priority order up|down', 'Move a priority value up or down in the list')] def complete_priority (self, text, line, begidx, endidx): if begidx == 16: comp = self.get_enum_list ('priority') elif begidx < 15: comp = ['list', 'add', 'change', 'remove', 'order'] return self.word_complete(text, comp) def do_priority(self, line): self._do_enum('priority', line) ## (Ticket) Severity _help_severity = [('severity list', 'Show possible ticket severities'), ('severity add ', 'Add a severity value option'), ('severity change ', 'Change a severity value'), ('severity remove ', 'Remove severity value'), ('severity order up|down', 'Move a severity value up or down in the list')] def complete_severity (self, text, line, begidx, endidx): if begidx == 16: comp = self.get_enum_list ('severity') elif begidx < 15: comp = ['list', 'add', 'change', 'remove', 'order'] return self.word_complete(text, comp) def do_severity(self, line): self._do_enum('severity', line) ## (Ticket) Resolution _help_resolution = [('resolution list', 'Show possible ticket resolutions'), ('resolution add ', 'Add a resolution value option'), ('resolution change ', 'Change a resolution value'), ('resolution remove ', 'Remove resolution value'), ('resolution order up|down', 'Move a resolution value up or down in the list')] def complete_resolution (self, text, line, begidx, endidx): if begidx == 18: comp = self.get_enum_list ('resolution') elif begidx < 17: comp = ['list', 'add', 'change', 'remove', 'order'] return self.word_complete(text, comp) def do_resolution(self, line): self._do_enum('resolution', line) # Type, priority, severity and resolution share the same datastructure # and methods: _enum_map = {'ticket_type': Type, 'priority': Priority, 'severity': Severity, 'resolution': Resolution} def _do_enum(self, type, line): arg = self.arg_tokenize(line) if arg[0] == 'list': self._do_enum_list(type) elif arg[0] == 'add' and len(arg) == 2: name = arg[1] self._do_enum_add(type, name) elif arg[0] == 'change' and len(arg) == 3: name = arg[1] newname = arg[2] self._do_enum_change(type, name, newname) elif arg[0] == 'remove' and len(arg) == 2: name = arg[1] self._do_enum_remove(type, name) elif arg[0] == 'order' and len(arg) == 3 and arg[2] in ('up', 'down'): name = arg[1] if arg[2] == 'up': direction = -1 else: direction = 1 self._do_enum_order(type, name, direction) else: self.do_help(type) def _do_enum_list(self, type): enum_cls = self._enum_map[type] print_table([(e.name,) for e in enum_cls.select(self.env_open())], ['Possible Values']) def _do_enum_add(self, type, name): cnx = self.db_open() sql = ("INSERT INTO enum(value,type,name) " " SELECT 1+COALESCE(max(%(cast)s),0),'%(type)s','%(name)s'" " FROM enum WHERE type='%(type)s'" % {'type':type, 'name':name, 'cast': cnx.cast('value', 'int')}) cursor = cnx.cursor() self.db_update(sql, cursor) cnx.commit() def _do_enum_change(self, type, name, newname): enum_cls = self._enum_map[type] enum = enum_cls(self.env_open(), name) enum.name = newname enum.update() def _do_enum_remove(self, type, name): enum_cls = self._enum_map[type] enum = enum_cls(self.env_open(), name) enum.delete() def _do_enum_order(self, type, name, direction): env = self.env_open() enum_cls = self._enum_map[type] enum1 = enum_cls(env, name) enum1.value = int(float(enum1.value) + direction) for enum2 in enum_cls.select(env): if int(float(enum2.value)) == enum1.value: enum2.value = int(float(enum2.value) - direction) break else: return enum1.update() enum2.update() ## Milestone _help_milestone = [('milestone list', 'Show milestones'), ('milestone add [due]', 'Add milestone'), ('milestone rename ', 'Rename milestone'), ('milestone due ', 'Set milestone due date (Format: "%s", "now" or "")' % _date_format_hint), ('milestone completed ', 'Set milestone completed date ' '(Format: "%s", "now" or "")' % _date_format_hint), ('milestone remove ', 'Remove milestone')] def complete_milestone (self, text, line, begidx, endidx): if begidx in (15, 17): comp = self.get_milestone_list() elif begidx < 15: comp = ['list', 'add', 'rename', 'time', 'remove'] return self.word_complete(text, comp) def do_milestone(self, line): arg = self.arg_tokenize(line) if arg[0] == 'list': self._do_milestone_list() elif arg[0] == 'add' and len(arg) in [2,3]: self._do_milestone_add(arg[1]) if len(arg) == 3: self._do_milestone_set_due(arg[1], arg[2]) elif arg[0] == 'rename' and len(arg) == 3: self._do_milestone_rename(arg[1], arg[2]) elif arg[0] == 'remove' and len(arg) == 2: self._do_milestone_remove(arg[1]) elif arg[0] == 'due' and len(arg) == 3: self._do_milestone_set_due(arg[1], arg[2]) elif arg[0] == 'completed' and len(arg) == 3: self._do_milestone_set_completed(arg[1], arg[2]) else: self.do_help('milestone') def _do_milestone_list(self): data = [] for m in Milestone.select(self.env_open()): data.append((m.name, m.due and self._format_date(m.due), m.completed and self._format_datetime(m.completed))) print_table(data, ['Name', 'Due', 'Completed']) def _do_milestone_rename(self, name, newname): milestone = Milestone(self.env_open(), name) milestone.name = newname milestone.update() def _do_milestone_add(self, name): milestone = Milestone(self.env_open()) milestone.name = name milestone.insert() def _do_milestone_remove(self, name): milestone = Milestone(self.env_open(), name) milestone.delete(author=getpass.getuser()) def _do_milestone_set_due(self, name, t): milestone = Milestone(self.env_open(), name) milestone.due = t and parse_date(t) milestone.update() def _do_milestone_set_completed(self, name, t): milestone = Milestone(self.env_open(), name) milestone.completed = t and parse_date(t) milestone.update() ## Version _help_version = [('version list', 'Show versions'), ('version add [time]', 'Add version'), ('version rename ', 'Rename version'), ('version time

Administration

${select("*|text()")}
PKkh9U.yy&trac/admin/templates/admin_basics.html Basics

Basic Settings

Project
PKkh9 mm*trac/admin/templates/admin_components.html Components

Manage Components

Modify Component:
${owner_field(component.owner)}

Add Component:
${owner_field()}
  NameOwnerDefault
$comp.name $comp.owner

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.

PKkh9<^2I I %trac/admin/templates/admin_enums.html $label_plural

Manage $label_plural

Modify $label_singular
Add $label_singular
  NameDefaultOrder
${enum.name}

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.

PKkh9:&trac/admin/templates/admin_legacy.html $page_title $page_body PKkh9²'trac/admin/templates/admin_logging.html Logging

Logging

Configuration

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

PKkh9~  *trac/admin/templates/admin_milestones.html Milestones

Manage Milestones

Modify Milestone:

Add Milestone:
  NameDueCompletedDefaultTickets
${milestone.name} ${format_date(milestone.due)} ${format_datetime(milestone.completed)} ${ticket_count}

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.

PKkh9AU %trac/admin/templates/admin_perms.html Permissions

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
$subject

Note that Subject or Group names can't be all upper-case, as that is reserved for permission names.

PKkh9'trac/admin/templates/admin_plugins.html Plugins

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.

${plugin.name} ${plugin.version}

${plugin.info.get('summary')}

Author:
${plugin.info.author_email or plugin.info.author}
Home page:
${plugin.info.home_page}
License:
${plugin.info.license}
ComponentEnabled
${component.name}

${component.module}

PKkh9[,z(trac/admin/templates/admin_versions.html Versions

Manage Versions

Modify Version:

Add Version:
  NameReleasedDefault
${version.name} ${version.time and format_datetime(version.time)}

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.

PKkh9A,?]]$trac/admin/templates/deploy_trac.cgi#!${executable} # -*- coding: utf-8 -*- # # Copyright (C) 2003-2008 Edgewall Software # Copyright (C) 2003-2004 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Jonas Borgström try: import os import pkg_resources if 'TRAC_ENV' not in os.environ and \ 'TRAC_ENV_PARENT_DIR' not in os.environ: os.environ['TRAC_ENV'] = '${env.path}' if 'PYTHON_EGG_CACHE' not in os.environ: if 'TRAC_ENV' in os.environ: egg_cache = os.path.join(os.environ['TRAC_ENV'], '.egg-cache') elif 'TRAC_ENV_PARENT_DIR' in os.environ: egg_cache = os.path.join(os.environ['TRAC_ENV_PARENT_DIR'], '.egg-cache') pkg_resources.set_extraction_path(egg_cache) from trac.web import cgi_frontend cgi_frontend.run() except SystemExit: raise except Exception, e: import sys import traceback print>>sys.stderr, e traceback.print_exc(file=sys.stderr) print 'Status: 500 Internal Server Error' print 'Content-Type: text/plain' print print 'Oops...' print print 'Trac detected an internal error:', e print traceback.print_exc(file=sys.stdout) PKkh9211%trac/admin/templates/deploy_trac.fcgi#!${executable} # -*- coding: utf-8 -*- # # Copyright (C) 2003-2008 Edgewall Software # Copyright (C) 2003-2004 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Jonas Borgström try: import os import pkg_resources if 'TRAC_ENV' not in os.environ and \ 'TRAC_ENV_PARENT_DIR' not in os.environ: os.environ['TRAC_ENV'] = '${env.path}' if 'PYTHON_EGG_CACHE' not in os.environ: if 'TRAC_ENV' in os.environ: egg_cache = os.path.join(os.environ['TRAC_ENV'], '.egg-cache') elif 'TRAC_ENV_PARENT_DIR' in os.environ: egg_cache = os.path.join(os.environ['TRAC_ENV_PARENT_DIR'], '.egg-cache') pkg_resources.set_extraction_path(egg_cache) from trac.web import fcgi_frontend fcgi_frontend.run() except SystemExit: raise except Exception, e: print 'Content-Type: text/plain\r\n\r\n', print 'Oops...' print print 'Trac detected an internal error:' print print e print import traceback import StringIO tb = StringIO.StringIO() traceback.print_exc(file=tb) print tb.getvalue() PKkh9%trac/admin/templates/deploy_trac.wsgi#!${executable} # -*- coding: utf-8 -*- # # Copyright (C) 2008 Edgewall Software # Copyright (C) 2008 Noah Kantrowitz # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Noah Kantrowitz import os def application(environ, start_request): if not 'trac.env_path_parent_dir' in environ: environ.setdefault('trac.env_path', '${env.path}') if 'PYTHON_EGG_CACHE' in environ: os.environ['PYTHON_EGG_CACHE'] = environ['PYTHON_EGG_CACHE'] elif 'trac.env_path' in environ: os.environ['PYTHON_EGG_CACHE'] = os.path.join(environ['trac.env_path'], '.egg-cache') elif 'trac.env_path_parent_dir' in environ: os.environ['PYTHON_EGG_CACHE'] = os.path.join(environ['trac.env_path_parent_dir'], '.egg-cache') from trac.web.main import dispatch_request return dispatch_request(environ, start_request) PKkh9e77trac/db/__init__.pyfrom trac.db.api import * from trac.db.schema import * PKmh9&( trac/db/__init__.pyc; Ic@sdkTdkTdS((s*N(s trac.db.apistrac.db.schema(((s5build/bdist.darwin-9.5.0-i386/egg/trac/db/__init__.pys?sPKkh9q|trac/db/api.py# -*- coding: utf-8 -*- # # Copyright (C) 2005 Edgewall Software # Copyright (C) 2005 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Christopher Lenz import os import urllib from trac.config import Option, IntOption from trac.core import * from trac.db.pool import ConnectionPool from trac.util.text import unicode_passwd def get_column_names(cursor): return cursor.description and \ [(isinstance(d[0], str) and [unicode(d[0], 'utf-8')] or [d[0]])[0] for d in cursor.description] or [] class IDatabaseConnector(Interface): """Extension point interface for components that support the connection to relational databases.""" def get_supported_schemes(): """Return the connection URL schemes supported by the connector, and their relative priorities as an iterable of `(scheme, priority)` tuples. """ def get_connection(**kwargs): """Create a new connection to the database.""" def init_db(**kwargs): """Initialize the database.""" def to_sql(table): """Return the DDL statements necessary to create the specified table, including indices.""" class DatabaseManager(Component): connectors = ExtensionPoint(IDatabaseConnector) connection_uri = Option('trac', 'database', 'sqlite:db/trac.db', """Database connection [wiki:TracEnvironment#DatabaseConnectionStrings string] for this project""") timeout = IntOption('trac', 'timeout', '20', """Timeout value for database connection, in seconds. Use '0' to specify ''no timeout''. ''(Since 0.11)''""") def __init__(self): self._cnx_pool = None def init_db(self): connector, args = self._get_connector() connector.init_db(**args) def get_connection(self): if not self._cnx_pool: connector, args = self._get_connector() self._cnx_pool = ConnectionPool(5, connector, **args) return self._cnx_pool.get_cnx(self.timeout or None) def shutdown(self, tid=None): if self._cnx_pool: self._cnx_pool.shutdown(tid) if not tid: self._cnx_pool = None def _get_connector(self): ### FIXME: Make it public? scheme, args = _parse_db_str(self.connection_uri) candidates = {} for connector in self.connectors: for scheme_, priority in connector.get_supported_schemes(): if scheme_ != scheme: continue highest = candidates.get(scheme_, (None, 0))[1] if priority > highest: candidates[scheme] = (connector, priority) connector = candidates.get(scheme, [None])[0] if not connector: raise TracError('Unsupported database type "%s"' % scheme) if scheme == 'sqlite': # Special case for SQLite to support a path relative to the # environment directory if args['path'] != ':memory:' and \ not args['path'].startswith('/'): args['path'] = os.path.join(self.env.path, args['path'].lstrip('/')) return connector, args def _parse_db_str(db_str): scheme, rest = db_str.split(':', 1) if not rest.startswith('/'): if scheme == 'sqlite': # Support for relative and in-memory SQLite connection strings host = None path = rest else: raise TracError('Database connection string must start with ' 'scheme:/') else: if not rest.startswith('//'): host = None rest = rest[1:] elif rest.startswith('///'): host = None rest = rest[3:] else: rest = rest[2:] if '/' not in rest: host = rest rest = '' else: host, rest = rest.split('/', 1) path = None if host and '@' in host: user, host = host.split('@', 1) if ':' in user: user, password = user.split(':', 1) else: password = None if user: user = urllib.unquote(user) if password: password = unicode_passwd(urllib.unquote(password)) else: user = password = None if host and ':' in host: host, port = host.split(':') port = int(port) else: port = None if not path: path = '/' + rest if os.name == 'nt': # Support local paths containing drive letters on Win32 if len(rest) > 1 and rest[1] == '|': path = "%s:%s" % (rest[0], rest[2:]) params = {} if '?' in path: path, qs = path.split('?', 1) qs = qs.split('&') for param in qs: name, value = param.split('=', 1) value = urllib.unquote(value) params[name] = value args = zip(('user', 'password', 'host', 'port', 'path', 'params'), (user, password, host, port, path, params)) return scheme, dict([(key, value) for key, value in args if value]) PKmh9(a.VVtrac/db/api.pyc; Ic@sdkZdkZdklZlZdkTdklZdkl Z dZ de fdYZ de fd YZd ZdS( N(sOptions IntOption(s*(sConnectionPool(sunicode_passwdcCss|io^gi}|iD]F}|t|dtot|ddgp |dgdq~pgSdS(Nisutf-8(scursors descriptionsappends_[1]sds isinstancesstrsunicode(scursors_[1]sd((s0build/bdist.darwin-9.5.0-i386/egg/trac/db/api.pysget_column_namesssIDatabaseConnectorcBs2tZdZdZdZdZdZRS(saExtension point interface for components that support the connection to relational databases.cCsdS(sReturn the connection URL schemes supported by the connector, and their relative priorities as an iterable of `(scheme, priority)` tuples. N((((s0build/bdist.darwin-9.5.0-i386/egg/trac/db/api.pysget_supported_schemes$scKsdS(s(Create a new connection to the database.N((skwargs((s0build/bdist.darwin-9.5.0-i386/egg/trac/db/api.pysget_connection)scKsdS(sInitialize the database.N((skwargs((s0build/bdist.darwin-9.5.0-i386/egg/trac/db/api.pysinit_db,scCsdS(s]Return the DDL statements necessary to create the specified table, including indices.N((stable((s0build/bdist.darwin-9.5.0-i386/egg/trac/db/api.pysto_sql/s(s__name__s __module__s__doc__sget_supported_schemessget_connectionsinit_dbsto_sql(((s0build/bdist.darwin-9.5.0-i386/egg/trac/db/api.pysIDatabaseConnector s    sDatabaseManagercBsntZeeZeddddZeddddZdZ d Z d Z e d Z d ZRS( Nstracsdatabasessqlite:db/trac.dbslDatabase connection [wiki:TracEnvironment#DatabaseConnectionStrings string] for this projectstimeouts20snTimeout value for database connection, in seconds. Use '0' to specify ''no timeout''. ''(Since 0.11)''cCs t|_dS(N(sNonesselfs _cnx_pool(sself((s0build/bdist.darwin-9.5.0-i386/egg/trac/db/api.pys__init__AscCs#|i\}}|i|dS(N(sselfs_get_connectors connectorsargssinit_db(sselfs connectorsargs((s0build/bdist.darwin-9.5.0-i386/egg/trac/db/api.pysinit_dbDscCsT|i o+|i\}}td|||_n|ii|iptSdS(Ni( sselfs _cnx_pools_get_connectors connectorsargssConnectionPoolsget_cnxstimeoutsNone(sselfs connectorsargs((s0build/bdist.darwin-9.5.0-i386/egg/trac/db/api.pysget_connectionHs cCs7|io)|ii|| o t|_q3ndS(N(sselfs _cnx_poolsshutdownstidsNone(sselfstid((s0build/bdist.darwin-9.5.0-i386/egg/trac/db/api.pysshutdownNs cCsGt|i\}}h}x|iD]t}xk|iD]]\}}||joq8n|i |t dfd}||jo||f|| # Copyright (C) 2005 Jeff Weiss # Copyright (C) 2006 Andres Salomon # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. import re from trac.core import * from trac.db.api import IDatabaseConnector from trac.db.util import ConnectionWrapper from trac.util import get_pkginfo _like_escape_re = re.compile(r'([/_%])') try: import MySQLdb import MySQLdb.cursors has_mysqldb = True class MySQLUnicodeCursor(MySQLdb.cursors.Cursor): def _convert_row(self, row): return tuple([(isinstance(v, str) and [v.decode('utf-8')] or [v])[0] for v in row]) def fetchone(self): row = super(MySQLUnicodeCursor, self).fetchone() return row and self._convert_row(row) or None def fetchmany(self, num): rows = super(MySQLUnicodeCursor, self).fetchmany(num) return rows != None and [self._convert_row(row) for row in rows] or [] def fetchall(self): rows = super(MySQLUnicodeCursor, self).fetchall() return rows != None and [self._convert_row(row) for row in rows] or [] except ImportError: has_mysqldb = False class MySQLConnector(Component): """MySQL database support for version 4.1 and greater. Database urls should be of the form: mysql://user[:password]@host[:port]/database """ implements(IDatabaseConnector) def __init__(self): self._version = None def get_supported_schemes(self): global has_mysqldb if has_mysqldb: return [('mysql', 1)] else: return [] def get_connection(self, path, user=None, password=None, host=None, port=None, params={}): cnx = MySQLConnection(path, user, password, host, port, params) if not self._version: self._version = get_pkginfo(MySQLdb).get('version', MySQLdb.__version__) mysql_info = 'server: "%s", client: "%s", thread-safe: %s' % \ (cnx.cnx.get_server_info(), MySQLdb.get_client_info(), MySQLdb.thread_safe()) self.env.systeminfo.extend([('MySQL', mysql_info), ('MySQLdb', self._version)]) return cnx def init_db(self, path, user=None, password=None, host=None, port=None, params={}): cnx = self.get_connection(path, user, password, host, port, params) cursor = cnx.cursor() from trac.db_default import schema for table in schema: for stmt in self.to_sql(table): self.env.log.debug(stmt) cursor.execute(stmt) cnx.commit() def _collist(self, table, columns): """Take a list of columns and impose limits on each so that indexing works properly. Some Versions of MySQL limit each index prefix to 500 bytes total, with a max of 255 bytes per column. """ cols = [] limit = 333 / len(columns) if limit > 255: limit = 255 for c in columns: name = '`%s`' % c table_col = filter((lambda x: x.name == c), table.columns) if len(table_col) == 1 and table_col[0].type.lower() == 'text': if name == '`rev`': name += '(20)' elif name == '`path`': name += '(255)' elif name == '`change_type`': name += '(2)' else: name += '(%s)' % limit # For non-text columns, we simply throw away the extra bytes. # That could certainly be optimized better, but for now let's KISS. cols.append(name) return ','.join(cols) def to_sql(self, table): sql = ['CREATE TABLE %s (' % table.name] coldefs = [] for column in table.columns: ctype = column.type if column.auto_increment: ctype = 'INT UNSIGNED NOT NULL AUTO_INCREMENT' # Override the column type, as a text field cannot # use auto_increment. column.type = 'int' coldefs.append(' `%s` %s' % (column.name, ctype)) if len(table.key) > 0: coldefs.append(' PRIMARY KEY (%s)' % self._collist(table, table.key)) sql.append(',\n'.join(coldefs) + '\n)') yield '\n'.join(sql) for index in table.indices: unique = index.unique and 'UNIQUE' or '' yield 'CREATE %s INDEX %s_%s_idx ON %s (%s);' % (unique, table.name, '_'.join(index.columns), table.name, self._collist(table, index.columns)) class MySQLConnection(ConnectionWrapper): """Connection wrapper for MySQL.""" poolable = True def __init__(self, path, user=None, password=None, host=None, port=None, params={}): if path.startswith('/'): path = path[1:] if password == None: password = '' if port == None: port = 3306 cnx = MySQLdb.connect(db=path, user=user, passwd=password, host=host, port=port, charset='utf8') ConnectionWrapper.__init__(self, cnx) self._is_closed = False def cast(self, column, type): if type == 'int': type = 'signed' elif type == 'text': type = 'char' return 'CAST(%s AS %s)' % (column, type) def concat(self, *args): return 'concat(%s)' % ', '.join(args) def like(self): return "LIKE %s ESCAPE '/'" def like_escape(self, text): return _like_escape_re.sub(r'/\1', text) def get_last_id(self, cursor, table, column='id'): return self.cnx.insert_id() def rollback(self): self.cnx.ping() try: self.cnx.rollback() except MySQLdb.ProgrammingError: self._is_closed = True def close(self): if not self._is_closed: try: self.cnx.close() except MySQLdb.ProgrammingError: pass # this error would mean it's already closed. So, ignore self._is_closed = True def cursor(self): return MySQLUnicodeCursor(self.cnx) PKmh9S((trac/db/mysql_backend.pyc; Ic@sdkZdkTdklZdklZdklZeidZ y8dk Z dk Z e a de iifdYZWnej o ea nXdefd YZd efd YZdS( N(s*(sIDatabaseConnector(sConnectionWrapper(s get_pkginfos([/_%])sMySQLUnicodeCursorcBs,tZdZdZdZdZRS(NcCsYtgi}|D]:}|t|to|idgp|gdq~SdS(Nsutf-8i(stuplesappends_[1]srowsvs isinstancesstrsdecode(sselfsrows_[1]sv((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pys _convert_row scCs4tt|i}|o |i|ptSdS(N(ssupersMySQLUnicodeCursorsselfsfetchonesrows _convert_rowsNone(sselfsrow((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysfetchone#scCs^tt|i|}|tjo.gi}|D]}||i |q3~pgSdS(N( ssupersMySQLUnicodeCursorsselfs fetchmanysnumsrowssNonesappends_[1]srows _convert_row(sselfsnumsrowss_[1]srow((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pys fetchmany&scCs[tt|i}|tjo.gi}|D]}||i |q0~pgSdS(N( ssupersMySQLUnicodeCursorsselfsfetchallsrowssNonesappends_[1]srows _convert_row(sselfsrowss_[1]srow((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysfetchall*s(s__name__s __module__s _convert_rowsfetchones fetchmanysfetchall(((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysMySQLUnicodeCursors   sMySQLConnectorcBsltZdZeedZdZeeeehdZeeeehdZ dZ dZ RS(sMySQL database support for version 4.1 and greater. Database urls should be of the form: mysql://user[:password]@host[:port]/database cCs t|_dS(N(sNonesselfs_version(sself((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pys__init__:scCs toddfgSngSdS(Nsmysqli(s has_mysqldb(sself((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysget_supported_schemes=sc Cst||||||}|i ort t i dt i |_ d|iit it if}|iiid|fd|i fgn|SdS(Nsversions+server: "%s", client: "%s", thread-safe: %ssMySQLsMySQLdb(sMySQLConnectionspathsuserspasswordshostsportsparamsscnxsselfs_versions get_pkginfosMySQLdbsgets __version__sget_server_infosget_client_infos thread_safes mysql_infosenvs systeminfosextend( sselfspathsuserspasswordshostsportsparamsscnxs mysql_info((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysget_connectionDs (,c Cs|i||||||}|i } dk l } xH| D]@} x7|i | D]&}|iii|| i|qTWq>W|idS(N(sschema(sselfsget_connectionspathsuserspasswordshostsportsparamsscnxscursorstrac.db_defaultsschemastablesto_sqlsstmtsenvslogsdebugsexecutescommit( sselfspathsuserspasswordshostsportsparamssstmtscnxscursorstablesschema((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysinit_dbRs  csg}dt|}|djo d}nx|D]d}td|i}t|djo|di i djoc|djo|d 7}q|d jo|d 7}q|d jo|d 7}q|d|7}n|i |q4Wdi |SdS(sTake a list of columns and impose limits on each so that indexing works properly. Some Versions of MySQL limit each index prefix to 500 bytes total, with a max of 255 bytes per column. iMis`%s`cs |ijS(N(sxsnamesc(sx(sc(s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysjsiistexts`rev`s(20)s`path`s(255)s `change_type`s(2)s(%s)s,N( scolsslenscolumnsslimitscsnamesfilterstables table_colstypeslowersappendsjoin(sselfstablescolumnsscsnamescolsslimits table_col((scs:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pys_collist]s&   -   c cs2d|ig}g}xQ|iD]F}|i}|iod}d|_n|i d|i|fq Wt |i djo$|i d|i ||i n|i di|dd i|Vx`|iD]U}|iod pd }d ||id i|i|i|i ||ifVqWdS(NsCREATE TABLE %s (s$INT UNSIGNED NOT NULL AUTO_INCREMENTsints `%s` %sis PRIMARY KEY (%s)s, s )s sUNIQUEss%CREATE %s INDEX %s_%s_idx ON %s (%s);s_(stablesnamessqlscoldefsscolumnsscolumnstypesctypesauto_incrementsappendslenskeysselfs_collistsjoinsindicessindexsunique(sselfstablesindexscoldefsscolumnsctypessqlsunique((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysto_sqlys"    $  ( s__name__s __module__s__doc__s implementssIDatabaseConnectors__init__sget_supported_schemessNonesget_connectionsinit_dbs_colliststo_sql(((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysMySQLConnector1s     sMySQLConnectioncBswtZdZeZeeeehdZdZdZdZ dZ ddZ dZ d Z d ZRS( sConnection wrapper for MySQL.c Cs|ido|d}n|tjo d}n|tjo d}ntid|d|d|d|d |d d }t i ||t |_dS( Ns/isi sdbsuserspasswdshostsportscharsetsutf8(spaths startswithspasswordsNonesportsMySQLdbsconnectsusershostscnxsConnectionWrappers__init__sselfsFalses _is_closed(sselfspathsuserspasswordshostsportsparamsscnx((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pys__init__s    cCs@|djo d}n|djo d}nd||fSdS(NsintssignedstextscharsCAST(%s AS %s)(stypescolumn(sselfscolumnstype((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pyscasts     cGsddi|SdS(Ns concat(%s)s, (sjoinsargs(sselfsargs((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysconcatscCsdSdS(NsLIKE %s ESCAPE '/'((sself((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pyslikescCstid|SdS(Ns/\1(s_like_escape_ressubstext(sselfstext((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pys like_escapessidcCs|iiSdS(N(sselfscnxs insert_id(sselfscursorstablescolumn((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pys get_last_idscCsD|iiy|iiWntij ot|_nXdS(N(sselfscnxspingsrollbacksMySQLdbsProgrammingErrorsTrues _is_closed(sself((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysrollbacks  cCsF|i o7y|iiWntij onXt|_ndS(N(sselfs _is_closedscnxsclosesMySQLdbsProgrammingErrorsTrue(sself((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pyscloses  cCst|iSdS(N(sMySQLUnicodeCursorsselfscnx(sself((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pyscursors(s__name__s __module__s__doc__sTruespoolablesNones__init__scastsconcatslikes like_escapes get_last_idsrollbacksclosescursor(((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pysMySQLConnections        (sres trac.cores trac.db.apisIDatabaseConnectors trac.db.utilsConnectionWrappers trac.utils get_pkginfoscompiles_like_escape_resMySQLdbsMySQLdb.cursorssTrues has_mysqldbscursorssCursorsMySQLUnicodeCursors ImportErrorsFalses ComponentsMySQLConnectorsMySQLConnection( sConnectionWrappersMySQLUnicodeCursors get_pkginfosres_like_escape_resMySQLdbsMySQLConnectionsIDatabaseConnectorsMySQLConnector((s:build/bdist.darwin-9.5.0-i386/egg/trac/db/mysql_backend.pys?s        `PKkh9@trac/db/pool.py# -*- coding: utf-8 -*- # # Copyright (C) 2005 Edgewall Software # Copyright (C) 2005 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Christopher Lenz try: import threading except ImportError: import dummy_threading as threading threading._get_ident = lambda: 0 import os import time from trac.db.util import ConnectionWrapper from trac.util.translation import _ class TimeoutError(Exception): """Exception raised by the connection pool when no connection has become available after a given timeout.""" class PooledConnection(ConnectionWrapper): """A database connection that can be pooled. When closed, it gets returned to the pool. """ def __init__(self, pool, cnx, key, tid): ConnectionWrapper.__init__(self, cnx) self._pool = pool self._key = key self._tid = tid def close(self): if self.cnx: self._pool._return_cnx(self.cnx, self._key, self._tid) self.cnx = None def __del__(self): self.close() def try_rollback(cnx): """Resets the Connection in a safe way, returning True when it succeeds. The rollback we do for safety on a Connection can fail at critical times because of a timeout on the Connection. """ try: cnx.rollback() # resets the connection return True except Exception: cnx.close() return False class ConnectionPoolBackend(object): """A process-wide LRU-based connection pool. """ def __init__(self, maxsize): self._available = threading.Condition(threading.RLock()) self._maxsize = maxsize self._active = {} self._pool = [] self._pool_key = [] self._pool_time = [] def get_cnx(self, connector, kwargs, timeout=None): num = 1 cnx = None key = unicode(kwargs) start = time.time() tid = threading._get_ident() self._available.acquire() try: while True: # First choice: Return the same cnx already used by the thread if (tid, key) in self._active: cnx, num = self._active[(tid, key)] num += 1 # Second best option: Reuse a live pooled connection elif key in self._pool_key: idx = self._pool_key.index(key) self._pool_key.pop(idx) self._pool_time.pop(idx) cnx = self._pool.pop(idx) # If possible, verify that the pooled connection is # still available and working. if hasattr(cnx, 'ping'): try: cnx.ping() except: continue # Third best option: Create a new connection elif len(self._active) + len(self._pool) < self._maxsize: cnx = connector.get_connection(**kwargs) # Forth best option: Replace a pooled connection with a new one elif len(self._active) < self._maxsize: # Remove the LRU connection in the pool self._pool.pop(0).close() self._pool_key.pop(0) self._pool_time.pop(0) cnx = connector.get_connection(**kwargs) if cnx: self._active[(tid, key)] = (cnx, num) return PooledConnection(self, cnx, key, tid) # Worst option: wait until a connection pool slot is available if timeout and (time.time() - start) > timeout: raise TimeoutError(_('Unable to get database ' 'connection within %(time)d ' 'seconds', time=timeout)) elif timeout: self._available.wait(timeout) else: self._available.wait() finally: self._available.release() def _return_cnx(self, cnx, key, tid): self._available.acquire() try: assert (tid, key) in self._active cnx, num = self._active[(tid, key)] if num == 1: del self._active[(tid, key)] self._available.notify() if cnx.poolable and try_rollback(cnx): self._pool.append(cnx) self._pool_key.append(key) self._pool_time.append(time.time()) else: self._active[(tid, key)] = (cnx, num - 1) finally: self._available.release() def shutdown(self, tid=None): """Close pooled connections not used in a while""" delay = 120 if tid is None: delay = 0 when = time.time() - delay self._available.acquire() try: while self._pool_time and self._pool_time[0] <= when: self._pool.pop(0) self._pool_key.pop(0) self._pool_time.pop(0) finally: self._available.release() _pool_size = int(os.environ.get('TRAC_DB_POOL_SIZE', 10)) _backend = ConnectionPoolBackend(_pool_size) class ConnectionPool(object): def __init__(self, maxsize, connector, **kwargs): # maxsize not used right now but kept for api compatibility self._connector = connector self._kwargs = kwargs def get_cnx(self, timeout=None): return _backend.get_cnx(self._connector, self._kwargs, timeout) def shutdown(self, tid=None): _backend.shutdown(tid) PKmh91trac/db/pool.pyc; Ic@sy dkZWn(ej odkZde_nXdkZdkZdklZdkl Z de fdYZ defdYZ dZ d efd YZeeiid d ZeeZd efdYZdS(NcCsdS(Ni((((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/pool.pyss(sConnectionWrapper(s_s TimeoutErrorcBstZdZRS(sjException raised by the connection pool when no connection has become available after a given timeout.(s__name__s __module__s__doc__(((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/pool.pys TimeoutErrors sPooledConnectioncBs)tZdZdZdZdZRS(s]A database connection that can be pooled. When closed, it gets returned to the pool. cCs/ti||||_||_||_ dS(N( sConnectionWrappers__init__sselfscnxspools_poolskeys_keystids_tid(sselfspoolscnxskeystid((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/pool.pys__init__'s  cCs:|io,|ii|i|i|it|_ndS(N(sselfscnxs_pools _return_cnxs_keys_tidsNone(sself((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/pool.pysclose-s cCs|idS(N(sselfsclose(sself((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/pool.pys__del__2s(s__name__s __module__s__doc__s__init__scloses__del__(((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/pool.pysPooledConnection"s   cCs:y|itSWn!tj o|itSnXdS(sResets the Connection in a safe way, returning True when it succeeds. The rollback we do for safety on a Connection can fail at critical times because of a timeout on the Connection. N(scnxsrollbacksTrues ExceptionsclosesFalse(scnx((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/pool.pys try_rollback6s  sConnectionPoolBackendcBs8tZdZdZedZdZedZRS(s.A process-wide LRU-based connection pool. cCsItiti|_||_h|_g|_g|_ g|_ dS(N( s threadings ConditionsRLocksselfs _availablesmaxsizes_maxsizes_actives_pools _pool_keys _pool_time(sselfsmaxsize((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/pool.pys__init__Gs     c Cs]d}t}t|}ti}ti } |i i z xto| |f|ijo'|i| |f\}}|d7}n#||ijow|ii|}|ii||ii||ii|}t|doy|iWq qCq Xqnt|it|i|ijo|i|}nct|i|ijoI|iidi|iid|iid|i|}n|o0||f|i| |f # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Christopher Lenz import re from trac.core import * from trac.db.api import IDatabaseConnector from trac.db.util import ConnectionWrapper from trac.util import get_pkginfo psycopg = None PgSQL = None PGSchemaError = None _like_escape_re = re.compile(r'([/_%])') class PostgreSQLConnector(Component): """PostgreSQL database support.""" implements(IDatabaseConnector) def __init__(self): self._version = None def get_supported_schemes(self): return [('postgres', 1)] def get_connection(self, path, user=None, password=None, host=None, port=None, params={}): global psycopg global PgSQL cnx = PostgreSQLConnection(path, user, password, host, port, params) if not self._version: if psycopg: self._version = get_pkginfo(psycopg).get('version', psycopg.__version__) name = 'psycopg2' elif PgSQL: import pyPgSQL self._version = get_pkginfo(pyPgSQL).get('version', pyPgSQL.__version__) name = 'pyPgSQL' else: name = 'unknown postgreSQL driver' self._version = '?' self.env.systeminfo.append((name, self._version)) return cnx def init_db(self, path, user=None, password=None, host=None, port=None, params={}): cnx = self.get_connection(path, user, password, host, port, params) cursor = cnx.cursor() if cnx.schema: cursor.execute('CREATE SCHEMA "%s"' % cnx.schema) cursor.execute('SET search_path TO %s', (cnx.schema,)) from trac.db_default import schema for table in schema: for stmt in self.to_sql(table): cursor.execute(stmt) cnx.commit() def to_sql(self, table): sql = ["CREATE TABLE %s (" % table.name] coldefs = [] for column in table.columns: ctype = column.type if column.auto_increment: ctype = "SERIAL" if len(table.key) == 1 and column.name in table.key: ctype += " PRIMARY KEY" coldefs.append(" %s %s" % (column.name, ctype)) if len(table.key) > 1: coldefs.append(" CONSTRAINT %s_pk PRIMARY KEY (%s)" % (table.name, ','.join(table.key))) sql.append(',\n'.join(coldefs) + '\n)') yield '\n'.join(sql) for index in table.indices: unique = index.unique and 'UNIQUE' or '' yield "CREATE %s INDEX %s_%s_idx ON %s (%s)" % (unique, table.name, '_'.join(index.columns), table.name, ','.join(index.columns)) class PostgreSQLConnection(ConnectionWrapper): """Connection wrapper for PostgreSQL.""" poolable = True def __init__(self, path, user=None, password=None, host=None, port=None, params={}): if path.startswith('/'): path = path[1:] # We support both psycopg and PgSQL but prefer psycopg global psycopg global PgSQL global PGSchemaError if not psycopg and not PgSQL: try: import psycopg2 as psycopg import psycopg2.extensions from psycopg2 import ProgrammingError as PGSchemaError psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) except ImportError: from pyPgSQL import PgSQL from pyPgSQL.libpq import OperationalError as PGSchemaError if 'host' in params: host = params['host'] if psycopg: dsn = [] if path: dsn.append('dbname=' + path) if user: dsn.append('user=' + user) if password: dsn.append('password=' + password) if host: dsn.append('host=' + host) if port: dsn.append('port=' + str(port)) cnx = psycopg.connect(' '.join(dsn)) cnx.set_client_encoding('UNICODE') else: # Don't use chatty, inefficient server-side cursors. # http://pypgsql.sourceforge.net/pypgsql-faq.html#id2787367 PgSQL.fetchReturnsList = 1 PgSQL.noPostgresCursor = 1 cnx = PgSQL.connect('', user, password, host, path, port, client_encoding='utf-8', unicode_results=True) try: self.schema = None if 'schema' in params: self.schema = params['schema'] cnx.cursor().execute('SET search_path TO %s', (self.schema,)) except PGSchemaError: cnx.rollback() ConnectionWrapper.__init__(self, cnx) def cast(self, column, type): # Temporary hack needed for the union of selects in the search module return 'CAST(%s AS %s)' % (column, type) def concat(self, *args): return '||'.join(args) def like(self): # Temporary hack needed for the case-insensitive string matching in the # search module return "ILIKE %s ESCAPE '/'" def like_escape(self, text): return _like_escape_re.sub(r'/\1', text) def get_last_id(self, cursor, table, column='id'): cursor.execute("SELECT CURRVAL('%s_%s_seq')" % (table, column)) return cursor.fetchone()[0] def rollback(self): self.cnx.rollback() if self.schema: try: self.cnx.cursor().execute("SET search_path TO %s", (self.schema,)) except PGSchemaError: self.cnx.rollback() PKmh9ggtrac/db/postgres_backend.pyc; Ic@sdkZdkTdklZdklZdklZea ea ea ei dZ defdYZdefd YZdS( N(s*(sIDatabaseConnector(sConnectionWrapper(s get_pkginfos([/_%])sPostgreSQLConnectorcBsctZdZeedZdZeeeehdZeeeehdZ dZ RS(sPostgreSQL database support.cCs t|_dS(N(sNonesselfs_version(sself((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pys__init__$scCsddfgSdS(Nspostgresi((sself((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pysget_supported_schemes'sc Cst||||||}|i ot o(t t i dt i |_ d}nHto1dk} t | i d| i |_ d}nd}d|_ |iii||i fn|SdS(Nsversionspsycopg2spyPgSQLsunknown postgreSQL drivers?(sPostgreSQLConnectionspathsuserspasswordshostsportsparamsscnxsselfs_versionspsycopgs get_pkginfosgets __version__snamesPgSQLspyPgSQLsenvs systeminfosappend( sselfspathsuserspasswordshostsportsparamssnamescnxspyPgSQL((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pysget_connection*s"      c Cs|i||||||}|i } |i o.| i d|i | i d|i fndk l } x5| D]-} x$|i| D]}| i |qWqvW|idS(NsCREATE SCHEMA "%s"sSET search_path TO %s(sschema(sselfsget_connectionspathsuserspasswordshostsportsparamsscnxscursorsschemasexecutestrac.db_defaultstablesto_sqlsstmtscommit( sselfspathsuserspasswordshostsportsparamssstmtscnxscursorstablesschema((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pysinit_db?s   ccscd|ig}g}x|iD]t}|i}|io d}nt |i djo|i|i jo|d7}n|i d|i|fq Wt |i djo*|i d|idi |i fn|i di |d d i |Vx]|i D]R}|iod pd }d ||idi |i|idi |ifVq WdS(NsCREATE TABLE %s (sSERIALis PRIMARY KEYs %s %ss% CONSTRAINT %s_pk PRIMARY KEY (%s)s,s, s )s sUNIQUEss$CREATE %s INDEX %s_%s_idx ON %s (%s)s_(stablesnamessqlscoldefsscolumnsscolumnstypesctypesauto_incrementslenskeysappendsjoinsindicessindexsunique(sselfstablesindexscoldefsscolumnsctypessqlsunique((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pysto_sqlLs$    )*  ( s__name__s __module__s__doc__s implementssIDatabaseConnectors__init__sget_supported_schemessNonesget_connectionsinit_dbsto_sql(((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pysPostgreSQLConnectors     sPostgreSQLConnectioncBsetZdZeZeeeehdZdZdZdZ dZ ddZ dZ RS( s"Connection wrapper for PostgreSQL.c Cs5|ido|d}nt ot omy9dkadk}dkla|ii |ii Wqt j o!dk ladk laqXnd|jo|d}ntog}|o|id|n|o|id|n|o|id |n|o|id |n|o|id t|ntid i|} | id n=dt_dt_tid|||||dddt} yGt|_ d|jo-|d|_ | i!i"d|i fnWntj o| i#nXt$i%|| dS(Ns/i(sProgrammingError(sPgSQL(sOperationalErrorshostsdbname=suser=s password=shost=sport=s sUNICODEssclient_encodingsutf-8sunicode_resultssschemasSET search_path TO %s(&spaths startswithspsycopgsPgSQLspsycopg2spsycopg2.extensionssProgrammingErrors PGSchemaErrors extensionss register_typesUNICODEs ImportErrorspyPgSQLs pyPgSQL.libpqsOperationalErrorsparamsshostsdsnsappendsuserspasswordsportsstrsconnectsjoinscnxsset_client_encodingsfetchReturnsListsnoPostgresCursorsTruesNonesselfsschemascursorsexecutesrollbacksConnectionWrappers__init__( sselfspathsuserspasswordshostsportsparamssdsnspsycopg2scnx((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pys__init__fsT          $cCsd||fSdS(NsCAST(%s AS %s)(scolumnstype(sselfscolumnstype((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pyscastscGsdi|SdS(Ns||(sjoinsargs(sselfsargs((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pysconcatscCsdSdS(NsILIKE %s ESCAPE '/'((sself((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pyslikescCstid|SdS(Ns/\1(s_like_escape_ressubstext(sselfstext((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pys like_escapessidcCs)|id||f|idSdS(NsSELECT CURRVAL('%s_%s_seq')i(scursorsexecutestablescolumnsfetchone(sselfscursorstablescolumn((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pys get_last_idscCse|ii|ioJy#|iiid|ifWqatj o|iiqaXndS(NsSET search_path TO %s(sselfscnxsrollbacksschemascursorsexecutes PGSchemaError(sself((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pysrollbacks   #( s__name__s __module__s__doc__sTruespoolablesNones__init__scastsconcatslikes like_escapes get_last_idsrollback(((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pysPostgreSQLConnectionas 2     (sres trac.cores trac.db.apisIDatabaseConnectors trac.db.utilsConnectionWrappers trac.utils get_pkginfosNonespsycopgsPgSQLs PGSchemaErrorscompiles_like_escape_res ComponentsPostgreSQLConnectorsPostgreSQLConnection(sConnectionWrappers get_pkginfosres_like_escape_resPostgreSQLConnectorsIDatabaseConnectorsPostgreSQLConnection((s=build/bdist.darwin-9.5.0-i386/egg/trac/db/postgres_backend.pys?s    BPKkh9P#trac/db/schema.py# -*- coding: utf-8 -*- # # Copyright (C) 2005 Edgewall Software # Copyright (C) 2005 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Christopher Lenz class Table(object): """Declare a table in a database schema.""" def __init__(self, name, key=[]): self.name = name self.columns = [] self.indices = [] self.key = key if isinstance(key, basestring): self.key = [key] def __getitem__(self, objs): self.columns = [o for o in objs if isinstance(o, Column)] self.indices = [o for o in objs if isinstance(o, Index)] return self class Column(object): """Declare a table column in a database schema.""" def __init__(self, name, type='text', size=None, unique=False, auto_increment=False): self.name = name self.type = type self.size = size self.auto_increment = auto_increment class Index(object): """Declare an index for a database schema.""" def __init__(self, columns, unique=False): self.columns = columns self.unique = unique PKmh99 9 trac/db/schema.pyc; Ic@sFdefdYZdefdYZdefdYZdS(sTablecBs#tZdZgdZdZRS(s%Declare a table in a database schema.cCsH||_g|_g|_||_t|to|g|_ndS(N(snamesselfscolumnssindicesskeys isinstances basestring(sselfsnameskey((s3build/bdist.darwin-9.5.0-i386/egg/trac/db/schema.pys__init__s     cCsgi}|D]$}t|to||qq~|_gi}|D]$}t|to||qLqL~|_ |SdS(N( sappends_[1]sobjssos isinstancesColumnsselfscolumnssIndexsindices(sselfsobjssos_[1]((s3build/bdist.darwin-9.5.0-i386/egg/trac/db/schema.pys __getitem__s>>(s__name__s __module__s__doc__s__init__s __getitem__(((s3build/bdist.darwin-9.5.0-i386/egg/trac/db/schema.pysTables  sColumncBs#tZdZdeeedZRS(s,Declare a table column in a database schema.stextcCs(||_||_||_||_dS(N(snamesselfstypessizesauto_increment(sselfsnamestypessizesuniquesauto_increment((s3build/bdist.darwin-9.5.0-i386/egg/trac/db/schema.pys__init__&s   (s__name__s __module__s__doc__sNonesFalses__init__(((s3build/bdist.darwin-9.5.0-i386/egg/trac/db/schema.pysColumn#s sIndexcBstZdZedZRS(s'Declare an index for a database schema.cCs||_||_dS(N(scolumnssselfsunique(sselfscolumnssunique((s3build/bdist.darwin-9.5.0-i386/egg/trac/db/schema.pys__init__1s (s__name__s __module__s__doc__sFalses__init__(((s3build/bdist.darwin-9.5.0-i386/egg/trac/db/schema.pysIndex.s N(sobjectsTablesColumnsIndex(sColumnsTablesIndex((s3build/bdist.darwin-9.5.0-i386/egg/trac/db/schema.pys?s PKkh9a- - trac/db/sqlite_backend.py# -*- coding: utf-8 -*- # # Copyright (C) 2005 Edgewall Software # Copyright (C) 2005 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Christopher Lenz import os import re import weakref from trac.core import * from trac.db.api import IDatabaseConnector from trac.db.util import ConnectionWrapper from trac.util import get_pkginfo _like_escape_re = re.compile(r'([/_%])') try: import pysqlite2.dbapi2 as sqlite have_pysqlite = 2 except ImportError: try: import sqlite3 as sqlite have_pysqlite = 2 except ImportError: try: import sqlite have_pysqlite = 1 except ImportError: have_pysqlite = 0 if have_pysqlite == 2: _ver = sqlite.sqlite_version_info sqlite_version = _ver[0] * 10000 + _ver[1] * 100 + int(_ver[2]) sqlite_version_string = '%d.%d.%d' % (_ver[0], _ver[1], int(_ver[2])) class PyFormatCursor(sqlite.Cursor): def _rollback_on_error(self, function, *args, **kwargs): try: return function(self, *args, **kwargs) except sqlite.DatabaseError, e: self.cnx.rollback() raise def execute(self, sql, args=None): if args: sql = sql % (('?',) * len(args)) return self._rollback_on_error(sqlite.Cursor.execute, sql, args or []) def executemany(self, sql, args=None): if args: sql = sql % (('?',) * len(args[0])) return self._rollback_on_error(sqlite.Cursor.executemany, sql, args or []) elif have_pysqlite == 1: _ver = sqlite._sqlite.sqlite_version_info() sqlite_version = _ver[0] * 10000 + _ver[1] * 100 + _ver[2] sqlite_version_string = '%d.%d.%d' % _ver class SQLiteUnicodeCursor(sqlite.Cursor): def _convert_row(self, row): return tuple([(isinstance(v, str) and [v.decode('utf-8')] or [v])[0] for v in row]) def fetchone(self): row = sqlite.Cursor.fetchone(self) return row and self._convert_row(row) or None def fetchmany(self, num): rows = sqlite.Cursor.fetchmany(self, num) return rows != None and [self._convert_row(row) for row in rows] or [] def fetchall(self): rows = sqlite.Cursor.fetchall(self) return rows != None and [self._convert_row(row) for row in rows] or [] def _to_sql(table): sql = ["CREATE TABLE %s (" % table.name] coldefs = [] for column in table.columns: ctype = column.type.lower() if column.auto_increment: ctype = "integer PRIMARY KEY" elif len(table.key) == 1 and column.name in table.key: ctype += " PRIMARY KEY" elif ctype == "int": ctype = "integer" coldefs.append(" %s %s" % (column.name, ctype)) if len(table.key) > 1: coldefs.append(" UNIQUE (%s)" % ','.join(table.key)) sql.append(',\n'.join(coldefs) + '\n);') yield '\n'.join(sql) for index in table.indices: unique = index.unique and 'UNIQUE' or '' yield "CREATE %s INDEX %s_%s_idx ON %s (%s);" % (unique, table.name, '_'.join(index.columns), table.name, ','.join(index.columns)) class SQLiteConnector(Component): """SQLite database support.""" implements(IDatabaseConnector) def __init__(self): self._version = None def get_supported_schemes(self): return [('sqlite', 1)] def get_connection(self, path, params={}): if not self._version: global sqlite_version_string self._version = get_pkginfo(sqlite).get( 'version', '%d.%d.%s' % sqlite.version_info) self.env.systeminfo.extend([('SQLite', sqlite_version_string), ('pysqlite', self._version)]) return SQLiteConnection(path, params) def init_db(cls, path, params={}): if path != ':memory:': # make the directory to hold the database if os.path.exists(path): raise TracError('Database already exists at %s' % path) os.makedirs(os.path.split(path)[0]) if isinstance(path, unicode): # needed with 2.4.0 path = path.encode('utf-8') cnx = sqlite.connect(path, timeout=int(params.get('timeout', 10000))) cursor = cnx.cursor() from trac.db_default import schema for table in schema: for stmt in cls.to_sql(table): cursor.execute(stmt) cnx.commit() def to_sql(cls, table): return _to_sql(table) class SQLiteConnection(ConnectionWrapper): """Connection wrapper for SQLite.""" __slots__ = ['_active_cursors'] poolable = have_pysqlite and os.name == 'nt' and sqlite_version >= 30301 def __init__(self, path, params={}): assert have_pysqlite > 0 self.cnx = None if path != ':memory:': if not os.access(path, os.F_OK): raise TracError('Database "%s" not found.' % path) dbdir = os.path.dirname(path) if not os.access(path, os.R_OK + os.W_OK) or \ not os.access(dbdir, os.R_OK + os.W_OK): from getpass import getuser raise TracError('The user %s requires read _and_ write ' \ 'permission to the database file %s and the ' \ 'directory it is located in.' \ % (getuser(), path)) if have_pysqlite == 2: self._active_cursors = weakref.WeakKeyDictionary() timeout = int(params.get('timeout', 10.0)) if isinstance(path, unicode): # needed with 2.4.0 path = path.encode('utf-8') cnx = sqlite.connect(path, detect_types=sqlite.PARSE_DECLTYPES, check_same_thread=sqlite_version < 30301, timeout=timeout) else: timeout = int(params.get('timeout', 10000)) cnx = sqlite.connect(path, timeout=timeout, encoding='utf-8') ConnectionWrapper.__init__(self, cnx) if have_pysqlite == 2: def cursor(self): cursor = self.cnx.cursor(PyFormatCursor) self._active_cursors[cursor] = True cursor.cnx = self return cursor def rollback(self): for cursor in self._active_cursors.keys(): cursor.close() self.cnx.rollback() else: def cursor(self): self.cnx._checkNotClosed("cursor") return SQLiteUnicodeCursor(self.cnx, self.cnx.rowclass) def cast(self, column, type): if sqlite_version >= 30203: return 'CAST(%s AS %s)' % (column, type) elif type == 'int': # hack to force older SQLite versions to convert column to an int return '1*' + column else: return column def concat(self, *args): return '||'.join(args) def like(self): if sqlite_version >= 30100: return "LIKE %s ESCAPE '/'" else: return 'LIKE %s' def like_escape(self, text): if sqlite_version >= 30100: return _like_escape_re.sub(r'/\1', text) else: return text if have_pysqlite == 2: def get_last_id(self, cursor, table, column='id'): return cursor.lastrowid else: def get_last_id(self, cursor, table, column='id'): return self.cnx.db.sqlite_last_insert_rowid() PKmh9/G0G0trac/db/sqlite_backend.pyc; Ic@sdkZdkZdkZdkTdklZdklZdkl Z ei dZ ydk i ZdZWnkej o_ydkZdZWqej o6ydkZdZWqej o dZqXqXnXedjoseiZedd edd eedZd ededeedfad eifd YZnfedjoXeiiZedd edd edZd eadeifdYZndZdefdYZdefdYZdS(N(s*(sIDatabaseConnector(sConnectionWrapper(s get_pkginfos([/_%])iiii'ids%d.%d.%dsPyFormatCursorcBs)tZdZedZedZRS(NcOsCy||||SWn(tij o}|iinXdS(N( sfunctionsselfsargsskwargsssqlites DatabaseErrorsescnxsrollback(sselfsfunctionsargsskwargsse((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pys_rollback_on_error0s  cCsF|o|dft|}n|itii||pgSdS(Ns?(sargsssqlslensselfs_rollback_on_errorssqlitesCursorsexecute(sselfssqlsargs((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysexecute6scCsJ|o|dft|d}n|itii||pgSdS(Ns?i(sargsssqlslensselfs_rollback_on_errorssqlitesCursors executemany(sselfssqlsargs((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pys executemany;s(s__name__s __module__s_rollback_on_errorsNonesexecutes executemany(((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysPyFormatCursor/s  sSQLiteUnicodeCursorcBs,tZdZdZdZdZRS(NcCsYtgi}|D]:}|t|to|idgp|gdq~SdS(Nsutf-8i(stuplesappends_[1]srowsvs isinstancesstrsdecode(sselfsrows_[1]sv((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pys _convert_rowGscCs1tii|}|o |i|ptSdS(N(ssqlitesCursorsfetchonesselfsrows _convert_rowsNone(sselfsrow((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysfetchoneJscCs[tii||}|tjo.gi}|D]}||i |q0~pgSdS(N( ssqlitesCursors fetchmanysselfsnumsrowssNonesappends_[1]srows _convert_row(sselfsnumsrowss_[1]srow((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pys fetchmanyMscCsXtii|}|tjo.gi}|D]}||i |q-~pgSdS(N( ssqlitesCursorsfetchallsselfsrowssNonesappends_[1]srows _convert_row(sselfsrowss_[1]srow((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysfetchallQs(s__name__s __module__s _convert_rowsfetchones fetchmanysfetchall(((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysSQLiteUnicodeCursorFs   ccswd|ig}g}x|iD]}|ii}|i o d}nOt |i djo|i|i jo|d7}n|djo d}n|i d|i|fq Wt |i djo!|i dd i |i n|i d i |d d i |Vx]|iD]R}|iod pd}d||idi |i|id i |ifVqWdS(NsCREATE TABLE %s (sinteger PRIMARY KEYis PRIMARY KEYsintsintegers %s %ss UNIQUE (%s)s,s, s );s sUNIQUEss%CREATE %s INDEX %s_%s_idx ON %s (%s);s_(stablesnamessqlscoldefsscolumnsscolumnstypeslowersctypesauto_incrementslenskeysappendsjoinsindicessindexsunique(stablesindexscoldefsscolumnsctypessqlsunique((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pys_to_sqlWs(   )  !  sSQLiteConnectorcBsKtZdZeedZdZhdZhdZdZ RS(sSQLite database support.cCs t|_dS(N(sNonesselfs_version(sself((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pys__init__qscCsddfgSdS(Nssqlitei((sself((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysget_supported_schemestscCsj|i oNttiddti|_|iiidt fd|ifgnt ||SdS(Nsversions%d.%d.%ssSQLitespysqlite( sselfs_versions get_pkginfossqlitesgets version_infosenvs systeminfosextendssqlite_version_stringsSQLiteConnectionspathsparams(sselfspathsparams((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysget_connectionws  ",cCs|djoHtii|otd|ntitii|dnt|to|id}nt i |dt |i dd}|i}dkl}x5|D]-}x$|i|D]}|i|qWqW|idS(Ns:memory:sDatabase already exists at %sisutf-8stimeouti'(sschema(spathsossexistss TracErrorsmakedirsssplits isinstancesunicodesencodessqlitesconnectsintsparamssgetscnxscursorstrac.db_defaultsschemastablesclssto_sqlsstmtsexecutescommit(sclsspathsparamssschemascnxscursorsstmtstable((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysinit_dbs !'  cCst|SdS(N(s_to_sqlstable(sclsstable((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysto_sqls( s__name__s __module__s__doc__s implementssIDatabaseConnectors__init__sget_supported_schemessget_connectionsinit_dbsto_sql(((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysSQLiteConnectorms     sSQLiteConnectioncBstZdZdgZeoeidjo edjZhdZ edjodZ dZ n dZ d Z d Z d Zd Zedjod dZn d dZRS(sConnection wrapper for SQLite.s_active_cursorssnti]vcCstdjptt|_|djoti|ti ot d|ntii |}ti|ti ti  pti|ti ti  o*dkl}t d||fqntdjoxti|_t|idd}t|to|id }nti|d tid td jd|}n4t|idd }ti|d|dd }ti||dS(Nis:memory:sDatabase "%s" not found.(sgetuserslThe user %s requires read _and_ write permission to the database file %s and the directory it is located in.istimeoutf10.0sutf-8s detect_typesscheck_same_threadi]vi'sencoding( s have_pysqlitesAssertionErrorsNonesselfscnxspathsossaccesssF_OKs TracErrorsdirnamesdbdirsR_OKsW_OKsgetpasssgetusersweakrefsWeakKeyDictionarys_active_cursorssintsparamssgetstimeouts isinstancesunicodesencodessqlitesconnectsPARSE_DECLTYPESssqlite_versionsConnectionWrappers__init__(sselfspathsparamsscnxsgetuserstimeoutsdbdir((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pys__init__s(  < !  icCs0|iit}t|i|<||_|SdS(N(sselfscnxscursorsPyFormatCursorsTrues_active_cursors(sselfscursor((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pyscursors  cCs5x!|iiD]}|iqW|iidS(N(sselfs_active_cursorsskeysscursorsclosescnxsrollback(sselfscursor((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysrollbackscCs*|iidt|i|iiSdS(Nscursor(sselfscnxs_checkNotClosedsSQLiteUnicodeCursorsrowclass(sself((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pyscursorscCs@tdjod||fSn|djo d|Sn|SdS(NiusCAST(%s AS %s)sints1*(ssqlite_versionscolumnstype(sselfscolumnstype((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pyscasts    cGsdi|SdS(Ns||(sjoinsargs(sselfsargs((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysconcatscCstdjodSndSdS(NiusLIKE %s ESCAPE '/'sLIKE %s(ssqlite_version(sself((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pyslikes cCs)tdjotid|Sn|SdS(Nius/\1(ssqlite_versions_like_escape_ressubstext(sselfstext((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pys like_escapes sidcCs |iSdS(N(scursors lastrowid(sselfscursorstablescolumn((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pys get_last_idscCs|iiiSdS(N(sselfscnxsdbssqlite_last_insert_rowid(sselfscursorstablescolumn((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pys get_last_ids(s__name__s __module__s__doc__s __slots__s have_pysqlitesossnamessqlite_versionspoolables__init__scursorsrollbackscastsconcatslikes like_escapes get_last_id(((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pysSQLiteConnections  #         (sossresweakrefs trac.cores trac.db.apisIDatabaseConnectors trac.db.utilsConnectionWrappers trac.utils get_pkginfoscompiles_like_escape_respysqlite2.dbapi2sdbapi2ssqlites have_pysqlites ImportErrorssqlite3ssqlite_version_infos_versintssqlite_versionssqlite_version_stringsCursorsPyFormatCursors_sqlitesSQLiteUnicodeCursors_to_sqls ComponentsSQLiteConnectorsSQLiteConnection(sConnectionWrapperssqlitesSQLiteConnectors have_pysqlitesPyFormatCursors get_pkginfossqlite_versionsweakrefsres_like_escape_resSQLiteConnections_to_sqlsIDatabaseConnectors_versossSQLiteUnicodeCursor((s;build/bdist.darwin-9.5.0-i386/egg/trac/db/sqlite_backend.pys?sB              (% "  'PKkh9 [7 trac/db/util.py# -*- coding: utf-8 -*- # # Copyright (C) 2005-2008 Edgewall Software # Copyright (C) 2005 Christopher Lenz # Copyright (C) 2006 Matthew Good # 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.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. # # Author: Christopher Lenz def sql_escape_percent(sql): import re return re.sub("'((?:[^']|(?:''))*)'", lambda m: m.group(0).replace('%', '%%'), sql) class IterableCursor(object): """Wrapper for DB-API cursor objects that makes the cursor iterable and escapes all "%"s used inside literal strings with parameterized queries. Iteration will generate the rows of a SELECT query one by one. """ __slots__ = ['cursor'] def __init__(self, cursor): self.cursor = cursor def __getattr__(self, name): return getattr(self.cursor, name) def __iter__(self): while True: row = self.cursor.fetchone() if not row: return yield row def execute(self, sql, args=None): # -- In case of SQL errors, uncomment the following 'print' statements # print 'execute', repr(sql) if args: # print repr(args) return self.cursor.execute(sql_escape_percent(sql), args) return self.cursor.execute(sql) def executemany(self, sql, args=None): # print 'executemany', repr(sql) if args: # print repr(args) return self.cursor.executemany(sql_escape_percent(sql), args) return self.cursor.executemany(sql) class ConnectionWrapper(object): """Generic wrapper around connection objects. This wrapper makes cursors produced by the connection iterable using `IterableCursor`. """ __slots__ = ['cnx'] def __init__(self, cnx): self.cnx = cnx def __getattr__(self, name): if hasattr(self, 'cnx'): return getattr(self.cnx, name) return object.__getattr__(self, name) def cursor(self): return IterableCursor(self.cnx.cursor()) PKmh9Fvtrac/db/util.pyc; Ic@s9dZdefdYZdefdYZdS(cCs#dk}|idd|SdS(Ns'((?:[^']|(?:''))*)'cCs|ididdS(Nis%s%%(smsgroupsreplace(sm((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pyss(sressubssql(ssqlsre((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pyssql_escape_percents sIterableCursorcBsJtZdZdgZdZdZdZedZedZ RS(sWrapper for DB-API cursor objects that makes the cursor iterable and escapes all "%"s used inside literal strings with parameterized queries. Iteration will generate the rows of a SELECT query one by one. scursorcCs ||_dS(N(scursorsself(sselfscursor((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pys__init__ scCst|i|SdS(N(sgetattrsselfscursorsname(sselfsname((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pys __getattr__#sccs6x/to'|ii}| odSn|VqWdS(N(sTruesselfscursorsfetchonesrow(sselfsrow((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pys__iter__&s cCs8|o|iit||Sn|ii|SdS(N(sargssselfscursorsexecutessql_escape_percentssql(sselfssqlsargs((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pysexecute-scCs8|o|iit||Sn|ii|SdS(N(sargssselfscursors executemanyssql_escape_percentssql(sselfssqlsargs((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pys executemany5s( s__name__s __module__s__doc__s __slots__s__init__s __getattr__s__iter__sNonesexecutes executemany(((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pysIterableCursors      sConnectionWrappercBs2tZdZdgZdZdZdZRS(sGeneric wrapper around connection objects. This wrapper makes cursors produced by the connection iterable using `IterableCursor`. scnxcCs ||_dS(N(scnxsself(sselfscnx((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pys__init__EscCs8t|dot|i|Snti||SdS(Nscnx(shasattrsselfsgetattrscnxsnamesobjects __getattr__(sselfsname((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pys __getattr__HscCst|iiSdS(N(sIterableCursorsselfscnxscursor(sself((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pyscursorMs(s__name__s __module__s__doc__s __slots__s__init__s __getattr__scursor(((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pysConnectionWrapper=s    N(ssql_escape_percentsobjectsIterableCursorsConnectionWrapper(sConnectionWrappersIterableCursorssql_escape_percent((s1build/bdist.darwin-9.5.0-i386/egg/trac/db/util.pys?s &PKkh9trac/htdocs/asc.pngPNG  IHDRwmsBITOPLTE̿ՑtRNS&R pHYs B4!tEXtSoftwareMacromedia Fireworks 3.0i1(IDATxc``R```q& .. .. *..N J@3;N.IENDB`PKkh9-trac/htdocs/attachment.pngPNG  IHDR agAMA7tEXtSoftwareAdobe ImageReadyqe<0PLTELLLRRRXXX]]]lllvvv;[tRNS#]RIDATxL@FQroq~L`cTni鐼a4-2̽"ͿB_%\gT)}0wP ?XHIENDB`PKkh9ش&&trac/htdocs/changeset.pngPNG  IHDR [AsBITO-PLTEʪ}}}eee]]]MMM555111$utRNS卞 pHYs B4!tEXtSoftwareMacromedia Fireworks 4.0&'uHIDATxc``SHM*:'(*xt9,' (lT9eJn6/~ Y<IENDB`PKkh9b?))trac/htdocs/closedticket.pngPNG  IHDR [AsBITOPLTEfyˠCĊ)0tRNS-T% pHYs  ~!tEXtSoftwareMacromedia Fireworks 4.0&'utEXtCreation Time11/17/049ZEIDATxcpc0`0/s%"ua8D3&0&1&؍MM\\\Y\\)IENDB`PKkh9Qtrac/htdocs/collapsed.pngPNG  IHDR rgAMAOX2tEXtSoftwareAdobe ImageReadyqe<PLTEsssЅOtRNSރY4IDATxb`gfvFF68 lf``"P0|]sIENDB`PKkh9F)Ltrac/htdocs/desc.pngPNG  IHDRwmsBITOPLTE̿ՑtRNS&R pHYs B4!tEXtSoftwareMacromedia Fireworks 3.0i1(IDATxcP'fFgG MMIENDB`PKkh9/22trac/htdocs/dots.gifGIF89aLLLLLL! , `{;PKkh9}_YYtrac/htdocs/draft.pngPNG  IHDR#$SgAMA7tEXtSoftwareAdobe ImageReadyqe<PLTE.tRNSsUIDATxb@1A@aDh4F@a@##8) @#"(&@0b`ağ0"H@ D@1 $a@ #&$b9ADv  "r u P#hx56@ 2*@6A( 7%@553 OQ\$@ 6tP (%a  D聤 #QM aDJJ$@ 90I[  aDD DT $bAD@ DT$bADy " F@QH0@ D@ 0D@ 0bfҡQ 0k4 $&aL@F l4 $aXhH0e,hI@ \8mƓaF 0$a(F "aHF H W"@"1$k/ !D聄֑P r\" @ C= c#@y|6Ð"@R\GL ݔ @ C?!"H0"` 1QD0i@"9 fq  ?#,iDx@ 0RL 𢙓Xy3:w  T2p1hhG@"sD !F ŪI*d.@ 00S @ C&nbV$7 @ C'I?Qh}0K qC}03H.v#a1@1 $R=,LlbZAHC%@ C,ItDzP "@$(>rA !DhOR!OI05DH̙9L `I-f1nR Dȁ$HbA$N@1 5#a,ЏE ` #fA@B?)KnFDL@_D*aġC| A  kq爔 $xgnF 9".&RF {y@ AH4>hx P@!1w4@Ccr` \@ 5H4d ` Y5p@Chۀ@ ~H4CP Р#f.3'U@ "ysR'uxr"4 ABT $F^r a2LRT $" a q3U $b4A'Xx? Q+$*{ UY( $Og "`EQ Q [%YN"aQ%H0bhbAe#1f@1 yPL#h mo_,B  #N H *F%f&:.@ THb Oc}Ѐk DhD4p#.nbb .@ d_G+}h>-'#ѡāXCq1PT,1!thjt/0t"]1V!%\n^ω Ѐ:$0 @cbe3h@HZKr_,Iۙ4yM{Xyp\i .: 6xṇS +(:ah@ p ނ-uIhxC@ tyZ c#h~> YIhx0Jɍ4alC,7t6$ b,thIlx1th"Bg 1aNMM A֏3!CNy.@ 0šXM`XWD4(gs_2 "FUle7݃H E1^V "lMJ.@  D t tVri %tQ"tv2H,LD@nw@"2P" Y)D|4t ̌0A @c>yG hH Hˆ EO/H  ڄ#W‰Fb  Z8V2k@mr )Fk  دC|/e@Qjf<, $$̄)*9pA @T#+fKH ALhyЅ@15o:6@17tdt %$^FD0R&829HBbla@NGxÈ98 hP1 Jm!b %[D=W0IN]< zDȠ#b^ BPv:zGc Ɉ$FE 'JgqpTq@ "& $H@ ""ZAAD08pspK`Iƒ/tAD(@e0TyNj  @&#IȆID@ H|BhL5̐|A @ "<Ąe&:v  F Z,8W@)yaݏ3 2i.@1 |JđN\$-  ZZ : :L,ݱ@ B $Basg@X[m9Xݜơ;8@1(IHg@Fe$M6AFDvaHĆ #"7XۉH"ӓ #"7,L,Dha,Pba@d56^NRx0ADDA"c`$v#XY  $DD?uuІ@QsD@bbA @T@RH\7t:lJIpP_@Tri02"@beGnzs  %r AU@Qρ(7hVZwb$HC5p $ja0L |[ɰ $"-幸) $V!FD|I r@WrJHH|,C(0\M-#MF p qYl #fFg2H4 #V>'2HC(pI?o@bBA @ÈБV<$k0B8+i4ԚI?[ LJ $@p IW 93?! ]!(A@]6Fs2JQ'd pT= ]@b]\yP a&HCtl hh N19ToNR oqٱZb."-1{뱆?@1h:HXV4*  Bk ːܐ"]b !)0)qV"]b KB q  bz$C?t }?/ʡ"}Ls.@19Ft q;@ Ď:H/ ":_ADl 0 X3tMT I 0 X3OL 0 X P)a^ x:ݯE84  /lb4x_ 0lCe9 Xc7_90u0  @bb#I $idaAR q p$> ]@XRI!C_H9'>d!›; $脵 "]_HXiR@RaPPMDT "B:"X[HC}N -Iu.@#; @2@Dma@p. aDDlOg  G3p"]"~k  FŰ҈"]"e#FFIh4BH HF DD(=R  Rg2XF\ɳ=#.t0bqA @ "]"189F^a$:H H # DDjy/5‚H tG!@aDh4F@a@aDh4F@a@aDh4F@a@aDh4F@a@aDh4F@m-p IENDB`PKkh9q trac/htdocs/edit_toolbar.pngPNG  IHDR7gAMA a pHYs  tEXtSoftwarePolybytes PolyImagePro +tEXtCommentsSoftware: Polybytes PolyImagePro 7IDATH WkcG6kBD tR .)Ҥù? WsȅRp6ȸ5F$٧of[:pz;%%VbD~yw>b`bj{+SNJdDS #T.JX6gmkʄ2@\H&.[$"@eya>v)Y'8[2H8j?\NIXERzWơbDLsoN3=+@~`N{]iԀNzlw`Gtկ]/ {vx8vcCa9y2c/HkJXҲQ%BKL#CKr I¥2̸$\JGE 5t*[mnW7ic_)ޫV9ifk4(6"-= aJm99e+ U[:Lc'IyNNy"r?h;Fb%i>&eXh%EȻ+R5DH5z\KgJz5IW 8 S&gJ{HyȰ2%=UV(AOlhɆ{T&wCV[౳/J^/b6>L"zs'aü:vOjox$ixWZpEC 1",xGNqKyҬ}z~r6|w&ZbsP[W[ꙅ|ӈDID ðB|499sCKh˖IG@fԎXyu9][hI;h{thvJı*ɋXcsj=uU^߯:~ k6"` ;'V*D=+X Ϝ|d;ώ[d@=G)\/O/w->7Cy ;e=]J#_1`>$f΄MJ_,m|SņccCM &IENDB`PKkh9SKtrac/htdocs/editedticket.pngPNG  IHDR [AtEXtCreation Time11/17/049ZtIME 0 Y pHYs B4gAMA aPLTEĊyfˠCMtRNS@f0IDATxc`A0`R4$G8DyAH[ rIENDB`PKkh9Wtrac/htdocs/expander_normal.pngPNG  IHDR \RbKGD pHYs&/[tIME#~IDATuϱ C! ЃI(C30 P0WpQ(_yOuf!ؔzRn$"sjQ`7ȅ_m)9GZ wZ3? 9gb^jv=IENDB`PKkh9e%trac/htdocs/expander_normal_hover.pngPNG  IHDR \RbKGD pHYs&/[tIME hZ߼aIDATu n(` IT$N\-Bєce@)Hs;Rblx冀}Ü3t8!}~R,MIENDB`PKkh9./htrac/htdocs/expander_open.pngPNG  IHDR 2dbKGD pHYs&&LtIME4@Xg"lIDATӅλ 0 siZh60."DFT=UcfI{HFDt$y^轿 I03ʞ09Z u'@DIENDB`PKkh9AA#trac/htdocs/expander_open_hover.pngPNG  IHDR 2dbKGD pHYs&&LtIME Z_^IDATӅ C!CP$ޏKPT{c14Ia e& @RT%d&UhN  s_?EJWIENDB`PKkh9mZZtrac/htdocs/extlink.gifGIF89a ׿!, 8,C@"& H4hdyTYH(LdS;PKkh9M~~trac/htdocs/feed.pngPNG  IHDR(sBITOPLTEYƽȉKܳ5djuֱ|Kt(|nQЛS 빌*"a˘gj&jdyэ:a浀ߚBt{Fzds#{|/滃vvFΙC x!dэIH֜R鵉{vj pHYs  ~!tEXtSoftwareMacromedia Fireworks 4.0&'utEXtCreation Time11/14/06/aIDATx5r`+y$o-|>f=X⌉z`jk{$V GF5uu1=L'?N$$ 3;଒*5aG2Q](핖"݆{ J!IENDB`PKkh9U)NHtrac/htdocs/file.pngPNG  IHDRR*PLTExxxsss{[,tRNS@f pHYs B4tIME%*yIDATxc`P26663```{f C7 @P #-KLX]HZ TDHM+\L,3 ˋl EAA%c0PR6qq̙3MBsw-]|1IENDB`PKkh9~Q6xeetrac/htdocs/folder.pngPNG  IHDR(-ScPLTEa^&1сQ:fzgzQG "xYd`ekRҦ{&͞$6Kڦ%>O$9=}# 6n#B0'[v6ancLM[F32DI!K3&c[g}6NlY"1Q;IMMLm\^ENR4 <9K:5V)nψsOl f3bp=ٲD㥙|‘ M1t @0 _N"=R~Cׯ4 +6ɽі6ƏNI^7|QK髡[wrM-&F'-K4U>Yj59 X3? . %R>?ۜ$+rLՉΜ9p8&l*aozp~qX*=x}ln'Q6nQ%e,M;>-5WG(#鈦-E*`x1?If@0jÊYiMFX]ٮEM^l6ĉK.- v"JNNv^#==ӿcǔ1K,1,X2k׮ vޝ(HgyyN'.+O>.667˗/eG$+!!}}uT*jp[ڳgONFǻ-Z5oش:U"BS?gҿ4#Sk^xa{@O"h4rbbb|2lTvYf-˩^x.yiXرHN$hooe"1i3;$tdeA[Ejg|'-cnKRZ*F\W+`\>4>Q\L&h9F$LHu._zNEyf4nIwmD7\HMMuی nEaKTT֭[+KKK>--S\\ܑjn>K&F@^ҙ%tPk@2A^yp%5( {ވe#A*{aߊ9,jdDoq@R ]9]|jDPp8_(\w3N_n>iD$Dߴ!X' 7-.]aѴA#1"af\,&cN4πlTKLC|51O~TލHG*`}[`I|L,9=c ~0_-2 NH W Qbw9g&v:u1"=ڈ u>SyMtjPΙjLr~}\ȏSH|g8fM{5MZSk xt&g.*~`}MEK r@4V|M(39qaӕ jϟXwS~ju" NMw IS8$o*eՍm¤X9g{aF0_|V; V7ѠW{ R-|F39Ӓb ?ocsU.(.㲃GV)?<|FMp&}H޶3רLp>̛?=Йsa_/1ZGj< Q85Ez)Svjw< 6c7{%"onZ|gϾ7>9ar%"bn5!FNI:e^bI/8wds#( `sXɋWe 7%Ǝ\6;M^\{G)W F"^b/Ɏ%-R=]/s•D`z wB~רVϽvVN}GV6lV4˷~~bیݜMo}bgR//J㵉-K X?bӕ貲:|8zFvmB~FYNK;ൿ/I?ٶJf swLZ9e[Sb>}ӣue8__Qb 69#?}^ݳSz%=\׬lxHoϾKDsLyŒXWaFltEfhALDH_霔8٪FW̰h/m??b'*n]32>|yJ:-#i49/7D!N#=yrLNJ3DX03~FUyi6 pxHbzVc0Y`3jO{i=žc RCMSLe,O&O@jc4z0 1UV\k32bZ%|K1x\v4 un;_WKȲO/ֶskiP3ܤE4%20 ؜nf+(( :=$YW+_@ODħ,\uu0,V !VeiDY,V.4 75^K(|21:UqUZmw^cY!0Mۢ!>PTD@FIԇX@9Ӂ@n{|9о &d}[/7@سKFAFmS%$uz%2nV$y-T̰=%eZ[4vvN-bxAM++L>N%|4GJ ՍC<> s sKKB qKij\渽cn;Q׬j<+x ɋux|ȴR~?xYr}w X7c;dZEsr@^‘v:DT нiVGj=*s>&#%EwJ%AWLHfN8!?zUl^ o*e 3jķ頋1,(gÇdgeCpq۹pW~<ۛ{A&PrRlh; 8 ktk~v/1ELLjU7IȊ{[^6V'̵Eb}\5Mr=M7[{aڧ~SfqAW=zZawĦutd Ɉwduq|[3H]ONSRmeu{?|l|CswI:UܼnUgT2F)8y᪜&O~QǣE*;LVs5,&nQ47{n^S,NvYJЊPN(: Vp^ll]&yGW-0=p`ZG[)^Ȁ[t$, %9@E^p` mAѯk=DR,tC1qqa ¼&жxj [yɃ[GM/ -\ȷ麍!C<܈d PVJ;owV6ny&m v[IENDB`PKkh9k/"  trac/htdocs/READMEThis directory contains files used by Trac's default clearsilver templates. Local modifications to these files might be lost during the installation of a new Trac version. This can be avoided by making a copy of this entire directory before beginning modifications. PKkh94dž^^trac/htdocs/topbar_gradient.pngPNG  IHDR U@r pHYs  ~tIME!:.m{ IDATxA E?*{Qpնeq俆DZK)9Lg> J{TyF%iPAhv*(І hj*(k];*tA݂ 00~Mx ض bp!Q]"Z9u91OP:zu]*eYPA oh. 9_RJfv~c"1+Ώ[MZ\Y*}+Ykl{ w~Y2GIENDB`PKkh9TVV trac/htdocs/topbar_gradient2.pngPNG  IHDR sRGB0PLTE "$!&(%02/231342685?@>DFCJLIOPNWYV^`]ghfnpmxzwbKGDH pHYs  tIMEIDATXc  ?+{  `LëU Fk퀟30X,m`e G ,`86Љ@g[]n/X0ysA@   N)]x@;d ia@gÁvĀJIENDB`PKkh9x"66trac/htdocs/trac.icoh& ( @ZZ88ppKK((BB{{hiaa22QQ!!          ?!??( @[[99rrKK((ffBBzz11!!bbQQlmwx//tt#      ""         #  "! ! $    #  !             !   ! $  ? ??? <   <???PKkh9,Dqqtrac/htdocs/trac_banner.pngPNG  IHDRI :{HPLTE %'$.0-675>?=EGDNPMWXV22jkiYYჂ顡؇IDATx훋*@HhP!&3ZugvLV=5F`?{zW`oKIO\؁`wA[e{=F?6 uC`Ǻ.`bf{v1{ ~#l =~oa6FYXRdUtjzlWTb;F\od=M66y"tAatq*. %D5 f1v;@t[}jqi! sܚ쐨`M"tOR[D:4 ۮ'<h9zg캸-4K<ڰ?]YɬǔWt6 E6{H@oo[w]jX_;v薰ѧaf$GA2e6;,[Q_ǧρ[gcI5/aM> a9a}.kMBEruuֿl\z 8Daٯ#1ؘ6+>Ig CKPS.SpjTbdg- k|xxzᙺ5~kw&[ԙ`vY2):([,03`ȓkTnFjI`W919c'mjle?",{1Zӯkaܧ^yjm߄5`3Bwsư=>&I+6Eu]TbU϶n/ڦGU㮷~ v;OBviڰ{߷NѶK0kX1Ixv$7=dBXksXyG,fiTiAXcY*o hH Lk;`g#A6ِxXRbz$e (;M bQ۰Z6, lA0⪦Ec3ٳٮc)KF`DEXk1nZ. ?bq?#aN@_poHN58Ex t5Tw[QίƯw;}5(k+ËjQصwn}WP aK+e]UIPBi0\JgrVT(D}K\GAQ]Εh TeKD g%nQ q.qh 6WrJVe,[Y9(p?=%$]_]ddԌܤ丷 0IDATxڵ E4TE hgZf[7r̴|w[R eB,IUF̲Rg]IjZYR ` ~**#}7*.fyTS"X8 Grx9Ю&xBdX!` YFFĜFoH-P,vw㍀,S3 D)=ST x=6˰CPNIӲGćy xx<|CS_PG:zfas@ڔkVBuCYA7kRi J L[f{ʳ1<1=]fNl,)RC>L*l(+3XScB|nF vGv*[Tf%^\lxd-B:Yy)~sL׈1mW