Trac-1.2/0000755000175500017550000000000013007224103011767 5ustar debacledebacleTrac-1.2/UPGRADE.rst0000644000175500017550000005504713007223745013636 0ustar debacledebacle.. charset=utf-8 Upgrade Instructions ==================== #. Instructions #. 1. Bring your server off-line #. 2. Update the Trac Code #. 3. Upgrade the Trac Environment #. 4. Update the Trac Documentation #. 5. Refresh static resources #. 6. Steps specific to a given Trac version #. Upgrading from Trac 1.0 to 1.2 #. Upgrading from Trac 0.12 to Trac 1.0 #. Upgrading from Trac 0.11 to Trac 0.12 #. Upgrading from Trac 0.10 to Trac 0.11 #. 7. Restart the Web Server #. Known Issues #. Customized Templates #. ZipImportError #. Wiki Upgrade #. Trac database upgrade #. Parent dir #. Related topics #. Upgrading Python #. Windows and Python 2.6 #. Changing Database Backend #. Upgrading from older versions of Trac Instructions ------------ Typically, there are seven steps involved in upgrading to a newer version of Trac: 1. Bring your server off-line ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is not a good idea to update a running server: the server processes may have parts of the current packages cached in memory, and updating the code will likely trigger `internal errors`_. Although a database backup will be implicitly created by default when upgrading the environment, it is always a good idea to perform a full backup of the environment using the `hotcopy`_ command before beginning. 2. Update the Trac Code ~~~~~~~~~~~~~~~~~~~~~~~ Get the new version as described in `TracInstall`_, or through your operating system package manager. If you already an earlier version of Trac installed via `easy_install`, it might be easiest to also use `easy_install` to upgrade your Trac installation: :: easy_install --upgrade Trac==1.2 You may also want to remove the pre-existing Trac code by deleting the `trac` directory from the Python `lib/site-packages` directory, or remove Trac `.egg` files from former versions. The location of the site-packages directory depends on the operating system and the location in which Python was installed. However, the following locations are typical: + on Linux: `/usr/lib/python2.X/site-packages` + on Windows: `C:\Python2.X\lib\site-packages` + on MacOSX: `/Library/Python/2.X/site-packages` You may also want to remove the directory in which your static resources are `deployed`_. The exact location depends on your platform. This cleanup is not mandatory, but makes it easier to troubleshoot issues later on, as your installation is uncluttered by code or templates from a previous release that is not used anymore. As usual, make a backup before actually removing things. 3. Upgrade the Trac Environment ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Environment upgrades are not necessary for minor version releases unless otherwise noted. After restarting, Trac should show the instances which need a manual upgrade via the automated upgrade scripts to ease the pain. These scripts are run via `trac-admin`_: :: trac-admin /path/to/projenv upgrade This command will not have any effect if the environment is already up-to-date. Note that a backup of your database will be performed automatically prior to the upgrade. This feature is relatively new for PostgreSQL or MySQL databases, so if it fails, you will have to backup the database manually. Then, to perform the actual upgrade: :: trac-admin /path/to/projenv upgrade --no-backup 4. Update the Trac Documentation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default, every `Trac environment`_ includes a copy of the Trac documentation for the installed version. However, to keep the included documentation in sync with the installed version of Trac, use the following `trac-admin`_ command to upgrade the documentation: :: trac-admin /path/to/projenv wiki upgrade Note that this procedure will leave your `WikiStart` page intact. 5. Refresh static resources ~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you have set up a web server to give out static resources directly (accessed using the `/chrome/` URL) then you will need to refresh them using the same command: :: trac-admin /path/to/env deploy /deploy/path This will extract static resources and CGI scripts ( `trac.wsgi`, etc) from new Trac version and its plugins into `/deploy/path`. Some web browsers (IE, Opera) cache CSS and Javascript files aggressively, so you may need to instruct your users to manually erase the contents of their browser's cache, a forced refreshed ( ``) should be enough. 6. Steps specific to a given Trac version ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Upgrading from Trac 1.0 to 1.2 `````````````````````````````` Python 2.5 no longer supported ++++++++++++++++++++++++++++++ Upgrade Python to at least 2.6 or 2.7, but not 3.0 or greater. Obsolete Plugins ++++++++++++++++ Trac has added functionality equivalent to the following plugins: + `AdminEnumListPlugin`_ + `DateFieldPlugin`_: see the time `custom field type`_ + `GroupBasedRedirectionPlugin`_: the default handler can set as a user preference. + `LinenoMacro`_: see `WikiProcessors#AvailableProcessors`_ + `NeverNotifyUpdaterPlugin`_: see `notification subscribers`_ + `QueryUiAssistPlugin`_: see `TracQuery#Filters`_. + `TicketCreationStatusPlugin`_: see `#NewWorkflowActions`_ The plugins should be removed when upgrading Trac to 1.2. New workflow actions ++++++++++++++++++++ The ticket creation step is controlled with a workflow action. The default workflow has `create` and `create_and_assign` actions. The `create` action will always be added when upgrading the database. The `create_and_assign` action will be added if the workflow has an *assigned* state. You may want to edit your workflow after upgrading the database to customize the actions available on the *New Ticket* page. New permissions policy for read-only wiki pages +++++++++++++++++++++++++++++++++++++++++++++++ Since 1.1.2 the read-only attribute of wiki pages is enabled and enforced only when `ReadonlyWikiPolicy` is in the list of active permission policies. If `[trac] permission_policy` has the default value `DefaultPermissionPolicy, LegacyAttachmentPolicy`, then `ReadonlyWikiPolicy` should be automatically appended to the list when upgrading the environment: :: [trac] permission_policies = ReadonlyWikiPolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy If other permission policies are enabled, `trac.ini` will need to have `ReadonlyWikiPolicy` appended to the list of active `permission_policies`. See `TracFineGrainedPermissions#ReadonlyWikiPolicy`_ for additional details on the proper ordering. Upgrading from Trac 0.12 to Trac 1.0 ```````````````````````````````````` Python 2.4 no longer supported ++++++++++++++++++++++++++++++ Upgrade Python to at least 2.5, but not 3.0. Obsolete Plugins ++++++++++++++++ Trac has added functionality equivalent to the following plugins: + `BatchModifyPlugin`_ + ​`GitPlugin`_ + `OverrideEditPlugin`_ The plugins should be removed when upgrading Trac to 1.0. Subversion components not enabled by default for new installations ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ The Trac components for Subversion support are no longer enabled by default. To enable the svn support, you need to make sure the `tracopt.versioncontrol.svn` components are enabled, for example by setting the following in the `TracIni`_: :: [components] tracopt.versioncontrol.svn.* = enabled The upgrade procedure should take care of this and change the `TracIni`_ appropriately, unless you already had the svn components explicitly disabled. Attachments migrated to new location ++++++++++++++++++++++++++++++++++++ Another step in the automatic upgrade will change the way the attachments are stored. Create a backup of the `attachments` directory before upgrading. In case the `attachments` directory contains some files which are *not* attachments, the last step of the migration to the new layout will fail: the deletion of the now unused `attachments` directory can't be done if there are still files and folders in it. You may ignore this error, but better to move them elsewhere and remove the `attachments` directory manually. The attachments themselves are now all located in your environment below the `files/attachments` directory. Behavior of `[ticket] default_owner` changed ++++++++++++++++++++++++++++++++++++++++++++ Prior to 1.0, the owner field of new tickets always defaulted to `[ticket] default_owner` when the value was not empty. If the value was empty, the owner field defaulted to to the Component's owner. In 1.0 and later, the `default_owner` must be set to `< default >` to make new tickets default to the Component's owner. This change allows the `default_owner` to be set to an empty value if no default owner is desired. Upgrading from Trac 0.11 to Trac 0.12 ````````````````````````````````````` Python 2.3 no longer supported ++++++++++++++++++++++++++++++ The minimum supported version of Python is now 2.4. SQLite v3.x required ++++++++++++++++++++ SQLite v2.x is no longer supported. If you still use a Trac database of this format, you'll need to convert it to SQLite v3.x first. See `PySqlite#UpgradingSQLitefrom2.xto3.x`_ for details. `PySqlite`_ 2 required ++++++++++++++++++++++ `PySqlite`_ 1.1.x is no longer supported. Please install 2.5.5 or later if possible, see `Trac database upgrade`_ below. Obsolete Plugins ++++++++++++++++ Trac has added functionality equivalent to the following plugins: + `AutoQueryPlugin`_ + ​`AdminConsoleProviderPatch`_ + `AnchorMacro`_: see `WikiFormatting#SettingAnchors`_ + `TicketChangePlugin`_: see `TICKET_EDIT_COMMENT permission`_ + `TicketDeletePlugin`_: see `tracopt.ticket.deleter` + `SubversionLocationPlugin`_: see `TracRepositoryAdmin#Repositories`_ + `WikiCreoleRendererPlugin`_: see `WikiCreole`_ + `RepoRevisionSyntaxPlugin`_ (added in 0.12.1) The plugins should be removed when upgrading Trac to 0.12. Multiple Repository Support +++++++++++++++++++++++++++ The latest version includes support for multiple repositories. If you plan to add more repositories to your Trac instance, please refer to `TracRepositoryAdmin#Migration`_. This may be of interest to users with only one repository, since there is now a way to avoid the potentially costly resync check at every request. Resynchronize the Trac Environment Against the Source Code Repository +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Each `Trac environment`_ must be resynchronized against the source code repository in order to avoid errors such as "`No changeset ??? in the repository`_" while browsing the source through the Trac interface: :: trac-admin /path/to/projenv repository resync '*' Improved repository synchronization +++++++++++++++++++++++++++++++++++ In addition to supporting multiple repositories, there is now a more efficient method for synchronizing Trac and your repositories. While you can keep the same synchronization as in 0.11 adding the post-commit hook as outlined in `TracRepositoryAdmin#Synchronization`_ and `TracRepositoryAdmin#ExplicitSync`_ will allow more efficient synchronization and is more or less required for multiple repositories. Note that if you were using the `trac-post-commit-hook`, *you're strongly advised to upgrade it* to the new hook documented in the above references and `here`_, as the old hook will not work with anything else than the default repository and even for this case, it won't trigger the appropriate notifications. Authz permission checking +++++++++++++++++++++++++ The authz permission checking has been migrated to a fine-grained permission policy. If you use authz permissions (aka `[trac] authz_file` and `authz_module_name`), you must add `AuthzSourcePolicy` in front of your permission policies in `[trac] permission_policies`. You must also remove `BROWSER_VIEW`, `CHANGESET_VIEW`, `FILE_VIEW` and `LOG_VIEW` from your global permissions with `trac-admin $ENV permission remove` or the "Permissions" admin panel. Microsecond timestamps ++++++++++++++++++++++ All timestamps in database tables, except the `session` table, have been changed from "seconds since epoch" to "microseconds since epoch" values. This change should be transparent to most users, except for custom reports. If any of your reports use date/time columns in calculations (e.g. to pass them to `datetime()`), you must divide the values retrieved from the database by 1'000'000. Similarly, if a report provides a calculated value to be displayed as a date/time (i.e. with a column named "time", "datetime", "changetime", "date", "created" or "modified"), you must provide a microsecond timestamp, that is, multiply your previous calculation with 1'000'000. Upgrading from Trac 0.10 to Trac 0.11 ````````````````````````````````````` Site Templates and Styles +++++++++++++++++++++++++ The templating engine has changed in 0.11 to Genshi, please look at `TracInterfaceCustomization`_ for more information. If you are using custom CSS or modified templates in the `templates` directory of the `TracEnvironment`_, you will need to convert them to the Genshi way of doing things. To continue to use your style sheet, follow the instructions at `TracInterfaceCustomization#SiteAppearance`_. Trac Macros, Plugins ++++++++++++++++++++ The Trac macros will need to be adapted, as the old-style wiki-macros are not supported anymore due to the drop of `ClearSilver`_ and the HDF. They need to be converted to the new-style macros, see `WikiMacros`_. When they are converted to the new style, they need to be placed into the plugins directory instead and not wiki-macros, which is no longer scanned for macros or plugins. For FCGI/WSGI/CGI users +++++++++++++++++++++++ For those who run Trac under the CGI environment, run this command in order to obtain the trac.*gi file: :: trac-admin /path/to/env deploy /deploy/directory/path This will create a deploy directory with the following two subdirectories: `cgi-bin` and `htdocs`. Then update your Apache configuration file `httpd.conf` with this new `trac.cgi` location and `htdocs` location. Web Admin plugin integrated +++++++++++++++++++++++++++ If you had the `WebAdmin`_ plugin installed, you can uninstall it as it is part of the Trac code base since 0.11. New Default Configurable Workflow +++++++++++++++++++++++++++++++++ When you run `trac-admin upgrade`, your `trac.ini` will be modified to include a `[ticket-workflow]` section. The workflow configured in this case is the original workflow, so that ticket actions will behave like they did in 0.10: Enable JavaScript to display the workflow graph. There are some significant caveats in this, such as accepting a ticket sets it to 'assigned' state, and assigning a ticket sets it to 'new' state. So you will probably want to migrate to "basic" workflow; `contrib/workflow/migrate_original_to_basic.py`_ may be helpful. See `TracWorkflow`_ for a detailed description of the new basic workflow. 7. Restart the Web Server ~~~~~~~~~~~~~~~~~~~~~~~~~ If you are not running `CGI`_, reload the new Trac code by restarting your web server. Known Issues ------------ Customized Templates ~~~~~~~~~~~~~~~~~~~~ Trac supports customization of its Genshi templates by placing copies of the templates in the `/templates` folder of your `environment`_ or in a common location specified in the ` [inherit] templates_dir`_ configuration setting. If you choose to do so, be aware that you will need to repeat your changes manually on a copy of the new templates when you upgrade to a new release of Trac (even a minor one), as the templates will likely evolve. So keep a diff around. The preferred way to perform `TracInterfaceCustomization`_ is to write a custom plugin doing an appropriate `ITemplateStreamFilter` transformation, as this is more robust in case of changes: we usually won't modify element `id`s or change CSS `class`es, and if we have to do so, this will be documented in the `TracDev/ApiChanges`_ pages. ZipImportError ~~~~~~~~~~~~~~ Due to internal caching of zipped packages, whenever the content of the packages change on disk, the in-memory zip index will no longer match and you'll get irrecoverable ZipImportError errors. Better anticipate and bring your server down for maintenance before upgrading. See `#7014`_ for details. Wiki Upgrade ~~~~~~~~~~~~ `trac-admin` will not delete or remove default wiki pages that were present in a previous version but are no longer in the new version. Trac database upgrade ~~~~~~~~~~~~~~~~~~~~~ A known issue in some versions of `PySqlite`_ (2.5.2-2.5.4) prevents the trac-admin upgrade script from successfully upgrading the database format. It is advised to use either a newer or older version of the sqlite python bindings to avoid this error. For more details see ticket `#9434`_. Parent dir ~~~~~~~~~~ If you use a Trac parent env configuration and one of the plugins in one child does not work, none of the children will work. Related topics -------------- Upgrading Python ~~~~~~~~~~~~~~~~ Upgrading Python to a newer version will require reinstallation of Python packages: Trac itself of course, but also `easy_install`_, if you've been using that. If you are using Subversion, you'll also need to upgrade the Python bindings for svn. Windows and Python 2.6 `````````````````````` If you've been using CollabNet's Subversion package, you may need to uninstall that in favor of `Alagazam`_, which has the Python bindings readily available, see `TracSubversion`_. That package works without tweaking. Changing Database Backend ~~~~~~~~~~~~~~~~~~~~~~~~~ The `TracMigratePlugin`_ on `trac-hacks.org`_ has been written to assist in migrating between SQLite, MySQL and PostgreSQL databases. Upgrading from older versions of Trac ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For upgrades from versions older than Trac 0.10, refer first to `wiki:0.10/TracUpgrade#SpecificVersions`_. See also: `TracGuide`_, `TracInstall`_ .. _ [inherit] templates_dir: http://trac.edgewall.org/wiki/TracIni#GlobalConfiguration .. _#7014: http://trac.edgewall.org/intertrac/%237014 .. _#9434: http://trac.edgewall.org/intertrac/%239434 .. _#NewWorkflowActions: http://trac.edgewall.org/wiki/TracUpgrade#NewWorkflowActions .. _AdminConsoleProviderPatch: https://trac-hacks.org/wiki/AdminConsoleProviderPatch .. _AdminEnumListPlugin: https://trac-hacks.org/wiki/AdminEnumListPlugin .. _Alagazam: http://alagazam.net/ .. _AnchorMacro: https://trac-hacks.org/wiki/AnchorMacro .. _AutoQueryPlugin: https://trac-hacks.org/wiki/AutoQueryPlugin .. _BatchModifyPlugin: https://trac-hacks.org/wiki/BatchModifyPlugin .. _CGI: http://trac.edgewall.org/wiki/TracCgi .. _ClearSilver: http://trac.edgewall.org/intertrac/ClearSilver .. _contrib/workflow/migrate_original_to_basic.py: http://trac.edgewall.org/intertrac/source%3Atrunk/contrib/workflow/migrate_original_to_basic.py .. _custom field type: http://trac.edgewall.org/wiki/TracTicketsCustomFields#AvailableFieldTypesandOptions .. _DateFieldPlugin: https://trac-hacks.org/wiki/DateFieldPlugin .. _deployed: http://trac.edgewall.org/wiki/TracInstall#cgi-bin .. _easy_install: http://pypi.python.org/pypi/setuptools .. _environment: http://trac.edgewall.org/wiki/TracEnvironment .. _GitPlugin: https://trac-hacks.org/wiki/GitPlugin .. _GroupBasedRedirectionPlugin: https://trac-hacks.org/wiki/GroupBasedRedirectionPlugin .. _here: http://trac.edgewall.org/wiki/TracWorkflow#Howtocombinethetracopt.ticket.commit_updaterwiththetestingworkflow .. _hotcopy: http://trac.edgewall.org/wiki/TracBackup .. _internal errors: http://trac.edgewall.org/wiki/TracUpgrade#ZipImportError .. _LinenoMacro: https://trac-hacks.org/wiki/LinenoMacro .. _NeverNotifyUpdaterPlugin: https://trac-hacks.org/wiki/NeverNotifyUpdaterPlugin .. _No changeset ??? in the repository: http://trac.edgewall.org/intertrac/%236120 .. _notification subscribers: http://trac.edgewall.org/wiki/TracNotification#notification-subscriber-section .. _OverrideEditPlugin: https://trac-hacks.org/wiki/OverrideEditPlugin .. _PySqlite#UpgradingSQLitefrom2.xto3.x: http://trac.edgewall.org/intertrac/PySqlite%23UpgradingSQLitefrom2.xto3.x .. _PySqlite: http://trac.edgewall.org/intertrac/PySqlite .. _QueryUiAssistPlugin: https://trac-hacks.org/wiki/QueryUiAssistPlugin .. _RepoRevisionSyntaxPlugin: https://trac-hacks.org/wiki/RepoRevisionSyntaxPlugin .. _SubversionLocationPlugin: https://trac-hacks.org/wiki/SubversionLocationPlugin .. _TICKET_EDIT_COMMENT permission: http://trac.edgewall.org/wiki/TracPermissions#TicketSystem .. _TicketChangePlugin: https://trac-hacks.org/wiki/TicketChangePlugin .. _TicketCreationStatusPlugin: https://trac-hacks.org/wiki/TicketCreationStatusPlugin .. _TicketDeletePlugin: https://trac-hacks.org/wiki/TicketDeletePlugin .. _Trac database upgrade: http://trac.edgewall.org/wiki/TracUpgrade#Tracdatabaseupgrade .. _Trac environment: http://trac.edgewall.org/wiki/TracEnvironment .. _trac-admin: http://trac.edgewall.org/wiki/TracAdmin .. _trac-hacks.org: https://trac-hacks.org .. _TracDev/ApiChanges: http://trac.edgewall.org/intertrac/TracDev/ApiChanges .. _TracEnvironment: http://trac.edgewall.org/wiki/TracEnvironment .. _TracFineGrainedPermissions#ReadonlyWikiPolicy: http://trac.edgewall.org/wiki/TracFineGrainedPermissions#ReadonlyWikiPolicy .. _TracGuide: http://trac.edgewall.org/wiki/TracGuide .. _TracIni: http://trac.edgewall.org/wiki/TracIni .. _TracInstall: http://trac.edgewall.org/wiki/TracInstall .. _TracInterfaceCustomization#SiteAppearance: http://trac.edgewall.org/wiki/TracInterfaceCustomization#SiteAppearance .. _TracInterfaceCustomization: http://trac.edgewall.org/wiki/TracInterfaceCustomization .. _TracMigratePlugin: https://trac-hacks.org/wiki/TracMigratePlugin .. _TracQuery#Filters: http://trac.edgewall.org/wiki/TracQuery#Filters .. _TracRepositoryAdmin#ExplicitSync: http://trac.edgewall.org/wiki/TracRepositoryAdmin#ExplicitSync .. _TracRepositoryAdmin#Migration: http://trac.edgewall.org/wiki/TracRepositoryAdmin#Migration .. _TracRepositoryAdmin#Repositories: http://trac.edgewall.org/wiki/TracRepositoryAdmin#Repositories .. _TracRepositoryAdmin#Synchronization: http://trac.edgewall.org/wiki/TracRepositoryAdmin#Synchronization .. _TracSubversion: http://trac.edgewall.org/intertrac/TracSubversion .. _TracWorkflow: http://trac.edgewall.org/wiki/TracWorkflow .. _WebAdmin: http://trac.edgewall.org/intertrac/WebAdmin .. _wiki:0.10/TracUpgrade#SpecificVersions: http://trac.edgewall.org/intertrac/wiki%3A0.10/TracUpgrade%23SpecificVersions .. _WikiCreole: http://trac.edgewall.org/intertrac/WikiCreole .. _WikiCreoleRendererPlugin: https://trac-hacks.org/wiki/WikiCreoleRendererPlugin .. _WikiFormatting#SettingAnchors: http://trac.edgewall.org/wiki/WikiFormatting#SettingAnchors .. _WikiMacros: http://trac.edgewall.org/wiki/WikiMacros .. _WikiProcessors#AvailableProcessors: http://trac.edgewall.org/wiki/WikiProcessors#AvailableProcessors Trac-1.2/INSTALL.rst0000644000175500017550000007013513007223746013651 0ustar debacledebacle.. charset=utf-8 Trac Installation Guide for 1.1 =============================== Trac is written in the Python programming language and needs a database, `SQLite`_, `PostgreSQL`_, or `MySQL`_. For HTML rendering, Trac uses the `Genshi`_ templating system. Trac can also be localized, and there is probably a translation available in your language. If you want to use the Trac interface in other languages, then make sure you have installed the optional package `Babel`_. Pay attention to the extra steps for localization support in the `Installing Trac`_ section below. Lacking Babel, you will only get the default English version. If you're interested in contributing new translations for other languages or enhancing the existing translations, then please have a look at `TracL10N`_. What follows are generic instructions for installing and setting up Trac. While you may find instructions for installing Trac on specific systems at `TracInstallPlatforms`_, please first read through these general instructions to get a good understanding of the tasks involved. Installation Steps `````````````````` #. Dependencies #. Mandatory Dependencies #. Optional Dependencies #. Installing Trac #. Using `easy_install` #. Using `pip` #. From source #. Using installer #. Using package manager #. Advanced `easy_install` Options #. Creating a Project Environment #. Deploying Trac #. Running the Standalone Server #. Running Trac on a Web Server #. Configuring Authentication #. Granting admin rights to the admin user #. Configuring Trac #. Using Trac Dependencies ------------ Mandatory Dependencies ~~~~~~~~~~~~~~~~~~~~~~ To install Trac, the following software packages must be installed: + `Python`_, version >= 2.6 and < 3.0 (note that we dropped the support for Python 2.5 in this release) + `setuptools`_, version >= 0.6 + `Genshi*`_, version >= 0.6 You also need a database system and the corresponding python bindings. The database can be either SQLite, PostgreSQL or MySQL. For the SQLite database ``````````````````````` As you must be using Python 2.6 or 2.7, you already have the SQLite database bindings bundled with the standard distribution of Python (the `sqlite3` module). Optionally, you may install a newer version of `pysqlite`_ than the one provided by the Python distribution. See `PySqlite*`_ for details. For the PostgreSQL database ``````````````````````````` You need to install the database and its Python bindings: + `PostgreSQL`_, version 8.0 or later + `psycopg2`_, version 2.0 or later See `DatabaseBackend`_ for details. For the MySQL database `````````````````````` Trac works well with MySQL, provided you follow the guidelines: + `MySQL`_, version 5.0 or later + `MySQLdb`_, version 1.2.2 or later Given the caveats and known issues surrounding MySQL, read carefully the `MySqlDb*`_ page before creating the database. Optional Dependencies ~~~~~~~~~~~~~~~~~~~~~ Subversion `````````` `Subversion`_, 1.6.x or later and the *corresponding* Python bindings. There are `pre-compiled SWIG bindings`_ available for various platforms. (Good luck finding precompiled SWIG bindings for any Windows package at that listing. `TracSubversion`_ points you to `Alagazam`_, which works for me under Python 2.6.) For troubleshooting information, see the `TracSubversion*`_ page. Note: + Trac doesn't use `PySVN`_, nor does it work yet with the newer `ctype`-style bindings. + If using Subversion, Trac must be installed on the same machine . Remote repositories are currently `not supported`_. Git ``` `Git`_ 1.5.6 or later is supported. More information is available on the `TracGit`_ page. Other Version Control Systems ````````````````````````````` Support for other version control systems is provided via third-party plugins. See `PluginList#VersionControlSystems`_ and `VersionControlSystem`_. Web Server `````````` A web server is optional because Trac is shipped with a server included, see the `Running the Standalone Server`_ section below. Alternatively you can configure Trac to run in any of the following environments: + `Apache`_ with + `mod_wsgi`_, see `TracModWSGI`_ and `ModWSGI IntegrationWithTrac`_. + `mod_python 3.5.0`_, see `TracModPython`_ + a `FastCGI`_-capable web server (see `TracFastCgi`_) + an `AJP`_-capable web server (see `TracOnWindowsIisAjp`_) + Microsoft IIS with FastCGI and a FastCGI-to-WSGI gateway (see `IIS with FastCGI`_) + a CGI-capable web server (see `TracCgi`_), but usage of Trac as a cgi script is highly discouraged , better use one of the previous options. Other Python Packages ````````````````````` + `Babel*`_, version 0.9.6 or >= 1.3, needed for localization support + `docutils`_, version >= 0.3.9 for `WikiRestructuredText`_. + `Pygments`_ for `syntax highlighting`_. + `pytz`_ to get a complete list of time zones, otherwise Trac will fall back on a shorter list from an internal time zone implementation. Attention : The available versions of these dependencies are not necessarily interchangeable, so please pay attention to the version numbers. If you are having trouble getting Trac to work, please double-check all the dependencies before asking for help on the `MailingList`_ or `IrcChannel`_. Please refer to the documentation of these packages to find out how they are best installed. In addition, most of the `platform-specific instructions`_ also describe the installation of the dependencies. Keep in mind however that the information there *probably concern older versions of Trac than the one you're installing*. Installing Trac --------------- The `trac-admin`_ command-line tool, used to create and maintain `project environments`_, as well as the `tracd`_ standalone server are installed along with Trac. There are several methods for installing Trac. It is assumed throughout this guide that you have elevated permissions as the `root` user or by prefixing commands with `sudo`. The umask `0002` should be used for a typical installation on a Unix-based platform. Using `easy_install` ~~~~~~~~~~~~~~~~~~~~ Trac can be installed from PyPI or the Subversion repository using `setuptools`_. A few examples: + Install the latest stable version of Trac: :: $ easy_install Trac + Install latest development version: :: $ easy_install http://download.edgewall.org/trac/Trac-latest-dev.tar.gz Note that in this case you won't have the possibility to run a localized version of Trac; either use a released version or install from source More information can be found on the `setuptools*`_ page. Setuptools Warning: If the version of your setuptools is in the range 5.4 through 5.6, the environment variable `PKG_RESOURCES_CACHE_ZIP_MANIFESTS` must be set in order to avoid significant performance degradation. More information may be found in `Deploying Trac`_. Using `pip` ~~~~~~~~~~~ 'pip' is an easy_install replacement that is very useful to quickly install python packages. To get a Trac installation up and running in less than 5 minutes: Assuming you want to have your entire pip installation in `/opt/user/trac` + :: pip install trac psycopg2 or + :: pip install trac mysql-python Make sure your OS specific headers are available for pip to automatically build PostgreSQL ( `libpq-dev`) or MySQL ( `libmysqlclient-dev`) bindings. pip will automatically resolve all dependencies (like Genshi, pygments, etc.), download the latest packages from pypi.python.org and create a self contained installation in `/opt/user/trac`. All commands ( `tracd`, `trac-admin`) are available in `/opt/user/trac/bin`. This can also be leveraged for `mod_python` (using `PythonHandler` directive) and `mod_wsgi` (using `WSGIDaemonProcess` directive) Additionally, you can install several Trac plugins (listed `here`_) through pip. From source ~~~~~~~~~~~ Using the python-typical setup at the top of the source directory also works. You can obtain the source for a .tar.gz or .zip file corresponding to a release (e.g. `Trac-1.0.tar.gz`) from the `TracDownload`_ page, or you can get the source directly from the repository. See `TracRepositories`_ for details. :: $ python ./setup.py install *You will need root permissions or equivalent for this step.* This will byte-compile the Python source code and install it as an .egg file or folder in the `site-packages` directory of your Python installation. The .egg will also contain all other resources needed by standard Trac, such as `htdocs` and `templates`. If you install from source and want to make Trac available in other languages, make sure Babel is installed. Only then, perform the `install` (or simply redo the `install` once again afterwards if you realize Babel was not yet installed): :: $ python ./setup.py install Alternatively, you can run `bdist_egg` and copy the .egg from `dist/` to the place of your choice, or you can create a Windows installer ( `bdist_wininst`). Using installer ~~~~~~~~~~~~~~~ On Windows, Trac can be installed using the exe installers available on the `TracDownload`_ page. Installers are available for the 32-bit and 64-bit versions of Python. Make sure to use the installer that matches the architecture of your Python installation. Using package manager ~~~~~~~~~~~~~~~~~~~~~ Trac may be available in your platform's package repository. Note however, that the version provided by your package manager may not be the latest release. Advanced `easy_install` Options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To install Trac to a custom location, or find out about other advanced installation options, run: :: easy_install --help Also see `Installing Python Modules`_ for detailed information. Specifically, you might be interested in: :: easy_install --prefix=/path/to/installdir or, if installing Trac on a Mac OS X system: :: easy_install --prefix=/usr/local --install-dir=/Library/Python/2.6/site-packages Mac OS X Note: On Mac OS X 10.6, running `easy_install trac` will install into `/usr/local` and `/Library/Python/2.6/site-packages` by default. The `tracd` and `trac-admin` commands will be placed in `/usr/local/bin` and will install the Trac libraries and dependencies into `/Library/Python/2.6/site-packages`, which is Apple's preferred location for third-party Python application installations. Creating a Project Environment ------------------------------ A `Trac environment`_ is the backend where Trac stores information like wiki pages, tickets, reports, settings, etc. An environment is a directory that contains a human-readable `configuration file`_, and other files and directories. A new environment is created using `trac-admin`_: :: $ trac-admin /path/to/myproject initenv `trac-admin`_ will prompt you for the information it needs to create the environment: the name of the project and the `database connection string`_. If you're not sure what to specify for any of these options, just press `` to use the default value. Using the default database connection string will always work as long as you have SQLite installed. For the other `database backends`_ you should plan ahead and already have a database ready to use at this point. Also note that the values you specify here can be changed later using `TracAdmin`_ or directly editing the `conf/trac.ini`_ configuration file. Filesystem Warning: When selecting the location of your environment, make sure that the filesystem on which the environment directory resides supports sub-second timestamps (i.e. not `ext2` or `ext3` on Linux, or HFS+ on OSX), as the modification time of the `conf/trac.ini` file will be monitored to decide whether an environment restart is needed or not. A too coarse-grained timestamp resolution may result in inconsistencies in Trac < 1.0.2. The best advice is to opt for a platform with sub-second timestamp resolution, regardless of the Trac version. Finally, make sure the user account under which the web front-end runs will have write permissions to the environment directory and all the files inside. This will be the case if you run `trac-admin ... initenv` as this user. If not, you should set the correct user afterwards. For example on Linux, with the web server running as user `apache` and group `apache`, enter: :: $ chown -R apache:apache /path/to/myproject The actual username and groupname of the apache server may not be exactly `apache`, and are specified in the Apache configuration file by the directives `User` and `Group` (if Apache `httpd` is what you use). Warning: Please only use ASCII-characters for account name and project path, unicode characters are not supported there. Deploying Trac -------------- Setuptools Warning: If the version of your setuptools is in the range 5.4 through 5.6, the environment variable `PKG_RESOURCES_CACHE_ZIP_MANIFESTS` must be set in order to avoid significant performance degradation. If running `tracd`, the environment variable can be set system-wide or for just the user that runs the `tracd` process. There are several ways to accomplish this in addition to what is discussed here, and depending on the distribution of your OS. To be effective system-wide a shell script with the `export` statement may be added to `/etc/profile.d`. To be effective for a user session the `export` statement may be added to `~/.profile`. :: export PKG_RESOURCES_CACHE_ZIP_MANIFESTS=1 Alternatively, the variable can be set in the shell before executing `tracd`: :: $ PKG_RESOURCES_CACHE_ZIP_MANIFESTS=1 tracd --port 8000 /path/to/myproject If running the Apache web server, Ubuntu/Debian users should add the `export` statement to `/etc/apache2/envvars`. RedHat/CentOS/Fedora should can add the `export` statement to `/etc/sysconfig/httpd`. Running the Standalone Server ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After having created a Trac environment, you can easily try the web interface by running the standalone server `tracd`_: :: $ tracd --port 8000 /path/to/myproject Then, fire up a browser and visit `http://localhost:8000/`. You should get a simple listing of all environments that `tracd` knows about. Follow the link to the environment you just created, and you should see Trac in action. If you only plan on managing a single project with Trac you can have the standalone server skip the environment list by starting it like this: :: $ tracd -s --port 8000 /path/to/myproject Running Trac on a Web Server ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Trac provides various options for connecting to a "real" web server: + `FastCGI*`_ + `Apache with mod_wsgi`_ + `Apache with mod_python`_ + *`CGI`_ (should not be used, as the performance is far from optimal)* Trac also supports `AJP*`_ which may be your choice if you want to connect to IIS. Other deployment scenarios are possible: `nginx`_, `uwsgi`_, `Isapi-wsgi`_ etc. Generating the Trac cgi-bin directory ````````````````````````````````````` In order for Trac to function properly with FastCGI you need to have a `trac.fcgi` file and for mod_wsgi a `trac.wsgi` file. These are Python scripts which load the appropriate Python code. They can be generated using the `deploy` option of `trac-admin`_. There is, however, a bit of a chicken-and-egg problem. The `trac- admin`_ command requires an existing environment to function, but complains if the deploy directory already exists. This is a problem, because environments are often stored in a subdirectory of the deploy. The solution is to do something like this: :: mkdir -p /usr/share/trac/projects/my-project trac-admin /usr/share/trac/projects/my-project initenv trac-admin /usr/share/trac/projects/my-project deploy /tmp/deploy mv /tmp/deploy/* /usr/share/trac Don't forget to check that the web server has the execution right on scripts in the `/usr/share/trac/cgi-bin` directory. Mapping Static Resources ```````````````````````` Without additional configuration, Trac will handle requests for static resources such as stylesheets and images. For anything other than a `TracStandalone`_ deployment, this is not optimal as the web server can be set up to directly serve the static resources. For CGI setup, this is highly undesirable as it causes abysmal performance. Web servers such as `Apache`_ allow you to create *Aliases* to resources, giving them a virtual URL that doesn't necessarily reflect their location on the file system. We can map requests for static resources directly to directories on the file system, to avoid Trac processing the requests. There are two primary URL paths for static resources: `/chrome/common` and `/chrome/site`. Plugins can add their own resources, usually accessible at the `/chrome/` path. A single `/chrome` alias can used if the static resources are extracted for all plugins. This means that the `deploy` command must be executed after installing or updating a plugin that provides static resources, or after modifying resources in the `$env/htdocs` directory. This is probably appropriate for most installations but may not be what you want if, for example, you wish to upload plugins through the *Plugins* administration page. The resources are extracted using the `trac-admin`_ ` deploy` command: :: deploy Extract static resources from Trac and all plugins The target `` will contain an `htdocs` directory with: + `common/` - the static resources of Trac + `site/` - a copy of the environment's `htdocs/` directory + `shared` - the static resources shared by multiple Trac environments, with a location defined by the `[inherit]` `htdocs_dir` option + `/` - one directory for each resource directory provided by the plugins enabled for this environment The example that follows will create a single `/chrome` alias. If that isn't the correct approach for your installation you simply need to create more specific aliases: :: Alias /trac/chrome/common /path/to/trac/htdocs/common Alias /trac/chrome/site /path/to/trac/htdocs/site Alias /trac/chrome/shared /path/to/trac/htdocs/shared Alias /trac/chrome/ /path/to/trac/htdocs/ Example: Apache and `ScriptAlias` +++++++++++++++++++++++++++++++++ Assuming the deployment has been done this way: :: $ trac-admin /var/trac/env deploy /path/to/shared/trac Add the following snippet to Apache configuration, changing paths to match your deployment. The snippet must be placed *before* the `ScriptAlias` or `WSGIScriptAlias` directive, because those directives map all requests to the Trac application: :: Alias /trac/chrome /path/to/trac/htdocs # For Apache 2.2 Order allow,deny Allow from all # For Apache 2.4 Require all granted If using mod_python, add this too, otherwise the alias will be ignored: :: SetHandler None Alternatively, if you wish to serve static resources directly from your project's `htdocs` directory rather than the location to which the files are extracted with the `deploy` command, you can configure Apache to serve those resources. Again, put this *before* the `ScriptAlias` or `WSGIScriptAlias` for the .*cgi scripts, and adjust names and locations to match your installation: :: Alias /trac/chrome/site /path/to/projectenv/htdocs # For Apache 2.2 Order allow,deny Allow from all # For Apache 2.4 Require all granted Another alternative to aliasing `/trac/chrome/common` is having Trac generate direct links for those static resources (and only those), using the ` [trac] htdocs_location`_ configuration setting: :: [trac] htdocs_location = http://static.example.org/trac-common/ Note that this makes it easy to have a dedicated domain serve those static resources, preferentially cookie-less. Of course, you still need to make the Trac `htdocs/common` directory available through the web server at the specified URL, for example by copying (or linking) the directory into the document root of the web server: :: $ ln -s /path/to/trac/htdocs/common /var/www/static.example.org/trac-common Setting up the Plugin Cache ``````````````````````````` Some Python plugins need to be extracted to a cache directory. By default the cache resides in the home directory of the current user. When running Trac on a Web Server as a dedicated user (which is highly recommended) who has no home directory, this might prevent the plugins from starting. To override the cache location you can set the `PYTHON_EGG_CACHE` environment variable. Refer to your server documentation for detailed instructions on how to set environment variables. Configuring Authentication -------------------------- Trac uses HTTP authentication. You'll need to configure your webserver to request authentication when the `.../login` URL is hit (the virtual path of the "login" button). Trac will automatically pick the `REMOTE_USER` variable up after you provide your credentials. Therefore, all user management goes through your web server configuration. Please consult the documentation of your web server for more info. The process of adding, removing, and configuring user accounts for authentication depends on the specific way you run Trac. Please refer to one of the following sections: + `TracStandalone#UsingAuthentication`_ if you use the standalone server, `tracd`. + `TracModWSGI#ConfiguringAuthentication`_ if you use the Apache web server, with any of its front end: `mod_wsgi`, `mod_python`, `mod_fcgi` or `mod_fastcgi`. + `TracFastCgi`_ if you're using another web server with FCGI support (Cherokee, Lighttpd, LiteSpeed, nginx) `TracAuthenticationIntroduction`_ also contains some useful information for beginners. Granting admin rights to the admin user --------------------------------------- Grant admin rights to user admin: :: $ trac-admin /path/to/myproject permission add admin TRAC_ADMIN This user will have an *Admin* navigation item that directs to pages for administering your Trac project. Configuring Trac ---------------- `TracRepositoryAdmin`_ provides information on configuring version control repositories for your project. Using Trac ---------- Once you have your Trac site up and running, you should be able to create tickets, view the timeline, browse your version control repository if configured, etc. Keep in mind that *anonymous* (not logged in) users can by default access only a few of the features, in particular they will have a read-only access to the resources. You will need to configure authentication and grant additional `permissions`_ to authenticated users to see the full set of features. *Enjoy!* `The Trac Team`_ See also: `TracInstallPlatforms`_, `TracGuide`_, `TracUpgrade`_, `TracPermissions`_ .. _ [trac] htdocs_location: http://trac.edgewall.org/wiki/TracIni#trac-section .. _AJP*: http://trac.edgewall.org/intertrac/TracOnWindowsIisAjp .. _AJP: http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html .. _Alagazam: http://alagazam.net .. _Apache with mod_python: http://trac.edgewall.org/wiki/TracModPython .. _Apache with mod_wsgi: http://trac.edgewall.org/wiki/TracModWSGI .. _Apache: http://httpd.apache.org/ .. _Babel*: http://babel.edgewall.org .. _Babel: http://trac.edgewall.org/wiki/TracInstall#OtherPythonPackages .. _CGI: http://trac.edgewall.org/wiki/TracCgi .. _conf/trac.ini: http://trac.edgewall.org/wiki/TracIni .. _configuration file: http://trac.edgewall.org/wiki/TracIni .. _database backends: http://trac.edgewall.org/intertrac/DatabaseBackend .. _database connection string: http://trac.edgewall.org/wiki/TracEnvironment#DatabaseConnectionStrings .. _DatabaseBackend: http://trac.edgewall.org/intertrac/DatabaseBackend%23Postgresql .. _Deploying Trac: http://trac.edgewall.org/wiki/TracInstall#DeployingTrac .. _docutils: http://docutils.sourceforge.net/ .. _FastCGI*: http://trac.edgewall.org/wiki/TracFastCgi .. _FastCGI: http://www.fastcgi.com/ .. _Genshi*: http://genshi.edgewall.org/wiki/Download .. _Genshi: http://genshi.edgewall.org .. _Git: http://git-scm.com/ .. _here: https://pypi.python.org/pypi?:action=browse&show=all&c=516 .. _IIS with FastCGI: http://trac.edgewall.org/intertrac/CookBook/Installation/TracOnWindowsIisWfastcgi .. _Installing Python Modules: http://docs.python.org/2/install/index.html .. _Installing Trac: http://trac.edgewall.org/wiki/TracInstall#InstallingTrac .. _IrcChannel: http://trac.edgewall.org/intertrac/IrcChannel .. _Isapi-wsgi: http://trac.edgewall.org/intertrac/TracOnWindowsIisIsapi .. _MailingList: http://trac.edgewall.org/intertrac/MailingList .. _mod_python 3.5.0: http://modpython.org/ .. _mod_wsgi: https://github.com/GrahamDumpleton/mod_wsgi .. _ModWSGI IntegrationWithTrac: http://code.google.com/p/modwsgi/wiki/IntegrationWithTrac .. _MySQL: http://mysql.com/ .. _MySqlDb*: http://trac.edgewall.org/intertrac/MySqlDb .. _MySQLdb: http://sf.net/projects/mysql-python .. _nginx: http://trac.edgewall.org/intertrac/TracNginxRecipe .. _not supported: http://trac.edgewall.org/intertrac/ticket%3A493 .. _permissions: http://trac.edgewall.org/wiki/TracPermissions .. _platform-specific instructions: http://trac.edgewall.org/intertrac/TracInstallPlatforms .. _PluginList#VersionControlSystems: http://trac.edgewall.org/intertrac/PluginList%23VersionControlSystems .. _PostgreSQL: http://www.postgresql.org/ .. _pre-compiled SWIG bindings: http://subversion.apache.org/packages.html .. _project environments: http://trac.edgewall.org/wiki/TracEnvironment .. _psycopg2: http://pypi.python.org/pypi/psycopg2 .. _Pygments: http://pygments.org .. _PySqlite*: http://trac.edgewall.org/intertrac/PySqlite%23ThePysqlite2bindings .. _pysqlite: http://pypi.python.org/pypi/pysqlite .. _PySVN: http://pysvn.tigris.org/ .. _Python: http://www.python.org/ .. _pytz: http://pytz.sf.net .. _Running the Standalone Server: http://trac.edgewall.org/wiki/TracInstall#RunningtheStandaloneServer .. _setuptools*: http://trac.edgewall.org/intertrac/wiki%3Asetuptools .. _setuptools: http://pypi.python.org/pypi/setuptools .. _SQLite: http://sqlite.org/ .. _Subversion: http://subversion.apache.org/ .. _syntax highlighting: http://trac.edgewall.org/wiki/TracSyntaxColoring .. _The Trac Team: http://trac.edgewall.org/intertrac/TracTeam .. _Trac environment: http://trac.edgewall.org/wiki/TracEnvironment .. _trac-admin: http://trac.edgewall.org/wiki/TracAdmin .. _TracAdmin: http://trac.edgewall.org/wiki/TracAdmin .. _TracAuthenticationIntroduction: http://trac.edgewall.org/intertrac/TracAuthenticationIntroduction .. _TracCgi: http://trac.edgewall.org/wiki/TracCgi .. _tracd: http://trac.edgewall.org/wiki/TracStandalone .. _TracDownload: http://trac.edgewall.org/intertrac/TracDownload .. _TracFastCgi: http://trac.edgewall.org/wiki/TracFastCgi .. _TracGit: http://trac.edgewall.org/intertrac/TracGit .. _TracGuide: http://trac.edgewall.org/wiki/TracGuide .. _TracInstallPlatforms: http://trac.edgewall.org/intertrac/TracInstallPlatforms .. _TracL10N: http://trac.edgewall.org/intertrac/wiki%3ATracL10N .. _TracModPython: http://trac.edgewall.org/wiki/TracModPython .. _TracModWSGI#ConfiguringAuthentication: http://trac.edgewall.org/wiki/TracModWSGI#ConfiguringAuthentication .. _TracModWSGI: http://trac.edgewall.org/wiki/TracModWSGI .. _TracOnWindowsIisAjp: http://trac.edgewall.org/intertrac/TracOnWindowsIisAjp .. _TracPermissions: http://trac.edgewall.org/wiki/TracPermissions .. _TracRepositories: http://trac.edgewall.org/intertrac/TracRepositories%23OfficialSubversionrepository .. _TracRepositoryAdmin: http://trac.edgewall.org/wiki/TracRepositoryAdmin .. _TracStandalone#UsingAuthentication: http://trac.edgewall.org/wiki/TracStandalone#UsingAuthentication .. _TracStandalone: http://trac.edgewall.org/wiki/TracStandalone .. _TracSubversion*: http://trac.edgewall.org/intertrac/TracSubversion%23Troubleshooting .. _TracSubversion: http://trac.edgewall.org/intertrac/TracSubversion .. _TracUpgrade: http://trac.edgewall.org/wiki/TracUpgrade .. _uwsgi: http://projects.unbit.it/uwsgi/wiki/Example#Traconapacheinasub-uri .. _VersionControlSystem: http://trac.edgewall.org/intertrac/VersionControlSystem .. _WikiRestructuredText: http://trac.edgewall.org/wiki/WikiRestructuredText Trac-1.2/sample-plugins/0000755000175500017550000000000013007224103014727 5ustar debacledebacleTrac-1.2/sample-plugins/HelloWorld.py0000644000175500017550000000376413007223745017401 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2007-2014 Edgewall Software # 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. """Example macro.""" revision = "$Rev: 12605 $" url = "$URL: https://svn.edgewall.org/repos/trac/branches/1.2-stable/sample-plugins/HelloWorld.py $" from trac.util.translation import cleandoc_ from trac.wiki.macros import WikiMacroBase class HelloWorldMacro(WikiMacroBase): _description = cleandoc_( """Simple HelloWorld macro. Note that the name of the class is meaningful: - it must end with "Macro" - what comes before "Macro" ends up being the macro name The documentation of the class (i.e. what you're reading) will become the documentation of the macro, as shown by the !MacroList macro (usually used in the WikiMacros page). """) def expand_macro(self, formatter, name, content, args=None): """Return some output that will be displayed in the Wiki content. `name` is the actual name of the macro (no surprise, here it'll be `'HelloWorld'`), `content` is the text enclosed in parenthesis at the call of the macro. Note that if there are ''no'' parenthesis (like in, e.g. [[HelloWorld]]), then `content` is `None`. `args` will contain a dictionary of arguments when called using the Wiki processor syntax and will be `None` if called using the macro syntax. """ return 'Hello World, content = ' + unicode(content) # Note that there's no need to HTML escape the returned data, # as the template engine (Genshi) will do it for us. Trac-1.2/sample-plugins/permissions/0000755000175500017550000000000013007224103017302 5ustar debacledebacleTrac-1.2/sample-plugins/permissions/debug_perm.py0000644000175500017550000000275013007223745022004 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2007-2013 Edgewall Software # 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. from trac.core import * from trac.perm import IPermissionPolicy, PermissionCache from trac.resource import Resource revision = "$Rev$" url = "$URL$" class DebugPolicy(Component): """Verify the well-formedness of the permission checks. **This plugin is only useful for Trac Development.** Once this plugin is enabled, you'll have to insert it at the appropriate place in your list of permission policies, e.g. {{{ [trac] permission_policies = DebugPolicy, SecurityTicketsPolicy, AuthzPolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy }}} """ implements(IPermissionPolicy) # IPermissionPolicy methods def check_permission(self, action, username, resource, perm): if resource: assert resource is None or isinstance(resource, Resource) assert isinstance(perm, PermissionCache) self.log.info("does '%s' have %s on %r?", username, action, resource) Trac-1.2/sample-plugins/permissions/public_wiki_policy.py0000644000175500017550000000535113007223745023553 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2007-2013 Edgewall Software # 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. from fnmatch import fnmatchcase from trac.config import Option from trac.core import * from trac.perm import IPermissionPolicy revision = "$Rev: 12501 $" url = "$URL: https://svn.edgewall.org/repos/trac/branches/1.2-stable/sample-plugins/permissions/public_wiki_policy.py $" class PublicWikiPolicy(Component): """Allow public access to some wiki pages. This is a sample permission policy plugin illustrating how to check permission on realms. Don't forget to integrate that plugin in the appropriate place in the list of permission policies: {{{ [trac] permission_policies = PublicWikiPolicy, DefaultPermissionPolicy }}} Then you can configure which pages you want to make public: {{{ [public_wiki] view = Public* modify = PublicSandbox/* }}} """ implements(IPermissionPolicy) view = Option('public_wiki', 'view', 'Public*', """Case-sensitive glob pattern used for granting view permission on all Wiki pages matching it.""") modify = Option('public_wiki', 'modify', 'Public*', """Case-sensitive glob pattern used for granting modify permissions on all Wiki pages matching it.""") def check_permission(self, action, username, resource, perm): if resource: # fine-grained permission check if resource.realm == 'wiki': # wiki realm or resource if resource.id: # ... it's a resource if action == 'WIKI_VIEW': # (think 'VIEW' here) pattern = self.view else: pattern = self.modify if fnmatchcase(resource.id, pattern): return True else: # ... it's a realm return True # this policy ''may'' grant permissions on some wiki pages else: # coarse-grained permission check # # support for the legacy permission checks: no resource specified # and realm information in the action name itself. # if action.startswith('WIKI_'): return True # this policy ''may'' grant permissions on some wiki pages Trac-1.2/sample-plugins/permissions/vulnerability_tickets.py0000644000175500017550000000477213007223745024320 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2007-2013 Edgewall Software # Copyright (C) 2007 Alec Thomas # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. from trac.core import * from trac.perm import IPermissionPolicy, IPermissionRequestor revision = "$Rev: 12166 $" url = "$URL: https://svn.edgewall.org/repos/trac/branches/1.2-stable/sample-plugins/permissions/vulnerability_tickets.py $" class SecurityTicketsPolicy(Component): """Prevent public access to security sensitive tickets. Add the VULNERABILITY_VIEW permission as a pre-requisite for any other permission check done on tickets that have the words "security" or "vulnerability" in the summary or keywords fields. Once this plugin is enabled, you'll have to insert it at the appropriate place in your list of permission policies, e.g. {{{ [trac] permission_policies = SecurityTicketsPolicy, AuthzPolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy }}} """ implements(IPermissionPolicy, IPermissionRequestor) # IPermissionPolicy methods def check_permission(self, action, username, resource, perm): # We add the 'VULNERABILITY_VIEW' pre-requisite for any action # other than 'VULNERABILITY_VIEW' itself, as this would lead # to recursion. if action == 'VULNERABILITY_VIEW': return # Check whether we're dealing with a ticket resource while resource: if resource.realm == 'ticket': break resource = resource.parent if resource and resource.realm == 'ticket' and resource.id is not None: for keywords, summary in self.env.db_query( "SELECT keywords, summary FROM ticket WHERE id=%s", (resource.id,)): fields = ''.join(f for f in (keywords, summary) if f).lower() if 'security' in fields or 'vulnerability' in fields: if 'VULNERABILITY_VIEW' not in perm: return False # IPermissionRequestor methods def get_permission_actions(self): yield 'VULNERABILITY_VIEW' Trac-1.2/sample-plugins/Timestamp.py0000644000175500017550000000222713007223745017262 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2007-2014 Edgewall Software # 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. """Inserts the current time (in seconds) into the wiki page.""" revision = "$Rev: 14481 $" url = "$URL: https://svn.edgewall.org/repos/trac/branches/1.2-stable/sample-plugins/Timestamp.py $" from datetime import datetime from genshi.builder import tag from trac.util.datefmt import datetime_now, format_datetime, utc from trac.wiki.macros import WikiMacroBase class TimestampMacro(WikiMacroBase): _description = "Inserts the current time (in seconds) into the wiki page." def expand_macro(self, formatter, name, content, args=None): t = datetime_now(utc) return tag.strong(format_datetime(t, '%c')) Trac-1.2/sample-plugins/milestone_to_version.py0000644000175500017550000000576413007223745021576 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2009-2013 Edgewall Software # Copyright (C) 2009 Remy Blank # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. import re from trac.config import Option from trac.core import * from trac.resource import ResourceNotFound from trac.ticket.api import IMilestoneChangeListener from trac.ticket.model import Version revision = "$Rev: 13232 $" url = "$URL: https://svn.edgewall.org/repos/trac/branches/1.2-stable/sample-plugins/milestone_to_version.py $" class MilestoneToVersion(Component): """Automatically create a version when a milestone is completed. Sample plugin demonstrating the IMilestoneChangeListener interface. Creates a version from a just-completed milestone based on whether the milestone's name matches a specified pattern. """ implements(IMilestoneChangeListener) pattern = Option('milestone_to_version', 'pattern', r'(?i)(?:v(?:er)?\.?|version)?\s*(?P\d.*)', """A regular expression to match the names of milestones that should be made into versions when they are completed. The pattern must include one named group called 'version' that matches the version number itself.""") def milestone_created(self, milestone): pass def milestone_changed(self, milestone, old_values): if not milestone.is_completed or 'completed' not in old_values \ or old_values['completed'] is not None: return m = re.match(self.pattern, milestone.name) if not m: return version_name = m.groupdict().get('version') if not version_name: return try: version = Version(self.env, version_name) if not version.time: version.time = milestone.completed version.update() self.log.info('Existing version "%s" updated with completion ' 'time from milestone "%s"', version.name, milestone.name) else: self.log.info('Version "%s" already exists. No new version ' 'created from milestone "%s"', version.name, milestone.name) except ResourceNotFound: version = Version(self.env) version.name = version_name version.time = milestone.completed version.insert() self.log.info('New version "%s" created from completed milestone ' '"%s".', version.name, milestone.name) def milestone_deleted(self, milestone): pass Trac-1.2/sample-plugins/workflow/0000755000175500017550000000000013007224103016601 5ustar debacledebacleTrac-1.2/sample-plugins/workflow/VoteOperation.py0000644000175500017550000000570113007223745021767 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2007-2013 Edgewall Software # Copyright (C) 2007 Eli Carter # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. from genshi.builder import tag from trac.core import implements,Component from trac.ticket.api import ITicketActionController from trac.ticket.default_workflow import ConfigurableTicketWorkflow from trac.ticket.model import Priority, Ticket #from trac.perm import IPermissionRequestor # (TODO) revision = "$Rev: 12166 $" url = "$URL: https://svn.edgewall.org/repos/trac/branches/1.2-stable/sample-plugins/workflow/VoteOperation.py $" class VoteOperation(Component): """Provides a simplistic vote feature. This is a sample action controller illustrating how to create additional ''operations''. Don't forget to add `VoteOperation` to the workflow option in the `[ticket]` section in TracIni. If there is no other workflow option, the line will look like this: {{{ workflow = ConfigurableTicketWorkflow,VoteOperation }}} """ implements(ITicketActionController) def get_ticket_actions(self, req, ticket): controller = ConfigurableTicketWorkflow(self.env) return controller.get_actions_by_operation_for_req(req, ticket, 'vote') def get_all_status(self): return [] def render_ticket_action_control(self, req, ticket, action): id = 'vote_%s_result' % (action, ) selected_value = req.args.get(id, 'for') options = ['for', 'against'] return ("vote", tag.select([tag.option(x, selected=(x == selected_value or None)) for x in options], name=id, id=id), "Vote on the issue, raising or lowering its priority") def get_ticket_changes(self, req, ticket, action): id = 'vote_%s_result' % (action, ) selected = req.args.get(id, 'for') priorities = list(Priority.select(self.env)) orig_ticket = Ticket(self.env, ticket.id) current_priority = int(Priority(self.env, name= orig_ticket['priority']).value) if selected == 'for': # priorities are 1-based, not 0-based new_value = max(1, current_priority - 1) else: maxval = max([int(p.value) for p in priorities]) new_value = min(maxval, current_priority + 1) return {'priority': [p.name for p in priorities if int(p.value) == new_value][0]} def apply_action_side_effects(self, req, ticket, action): pass Trac-1.2/sample-plugins/workflow/enterprise-review-workflow.ini0000644000175500017550000000450713007223745024652 0ustar debacledebacle; enterprise-review-workflow.ini ; Add CodeReviewActionController to the workflow option in [ticket]. If there ; is no workflow option, the line will look like this: ; workflow = ConfigurableTicketWorkflow,CodeReviewActionController [ticket-workflow] ; assign, reassign, unassign actions assign = new -> assigned assign.operations = set_owner assign.permissions = TICKET_MODIFY reassign = assigned,in_work -> assigned reassign.operations = set_owner reassign.permissions = TICKET_MODIFY reassign_closed = closed -> closed reassign_closed.label = reassign reassign_closed.operations = set_owner reassign_closed.permissions = TICKET_MODIFY unassign = assigned,in_work -> new unassign.operations = del_owner unassign.permissions = TICKET_MODIFY ; leave actions leave = * -> * leave.operations = leave_status leave.default = 1 ; resolve actions resolve = in_QA -> closed resolve.operations = set_resolution resolve.permissions = TICKET_MODIFY fail = in_QA -> assigned fail.permissions = TICKET_MODIFY ; start/stop actions start = assigned -> in_work start.operations = set_owner_to_self start.permissions = TICKET_MODIFY stop = in_work -> assigned stop.permissions = TICKET_MODIFY ; reopen actions reopen = closed -> new reopen.operations = del_resolution reopen.permissions = TICKET_CREATE ; needinfo actions ; For tickets with an owner, they go back to assigned needinfo = assigned,in_work,in_QA -> needinfo needinfo.permissions = TICKET_MODIFY infoprovided = needinfo -> assigned infoprovided.permissions = TICKET_MODIFY infoprovided.default = 2 ; But tickets without an owner go back to new. needinfo_new = new -> needinfo_new needinfo_new.label = needinfo needinfo_new.permissions = TICKET_MODIFY infoprovided_new = needinfo_new -> new infoprovided_new.permissions = TICKET_MODIFY infoprovided_new.default = 2 ; review actions request_review = in_work -> in_review review = in_review -> * review.operations = code_review,hidden ; A reviewer can approve the work as-is, approve the work with a few things ; that need touched-up, but that won't need further review, or request changes ; significant enough that another review will be needed. review.code_review = approve -> in_QA, approve as noted -> post_review, request changes -> in_work review.permissions = TICKET_REVIEW submit_to_test = post_review -> in_QA Trac-1.2/sample-plugins/workflow/DeleteTicket.py0000644000175500017550000000422213007223745021534 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2007-2013 Edgewall Software # Copyright (C) 2007 Eli Carter # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. from trac.core import implements,Component from trac.ticket.api import ITicketActionController from trac.perm import IPermissionRequestor revision = "$Rev: 13452 $" url = "$URL: https://svn.edgewall.org/repos/trac/branches/1.2-stable/sample-plugins/workflow/DeleteTicket.py $" class DeleteTicketActionController(Component): """Provides the admin with a way to delete a ticket. Illustrates how to create an action controller with side-effects. Don't forget to add `DeleteTicketActionController` to the workflow option in the `[ticket]` section in TracIni. If there is no other workflow option, the line will look like this: {{{ workflow = ConfigurableTicketWorkflow,DeleteTicketActionController }}} """ implements(ITicketActionController, IPermissionRequestor) # IPermissionRequestor methods def get_permission_actions(self): return ['TICKET_DELETE'] # ITicketActionController methods def get_ticket_actions(self, req, ticket): actions = [] if 'TICKET_DELETE' in req.perm(ticket.resource) \ and ticket.exists: actions.append((0,'delete')) return actions def get_all_status(self): return [] def render_ticket_action_control(self, req, ticket, action): return ("delete ticket", '', "This ticket will be deleted.") def get_ticket_changes(self, req, ticket, action): return {} def apply_action_side_effects(self, req, ticket, action): # Be paranoid here, as this should only be called when # action is delete... if action == 'delete': ticket.delete() Trac-1.2/sample-plugins/workflow/CodeReview.py0000644000175500017550000001211113007223745021216 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2007-2013 Edgewall Software # Copyright (C) 2007 Eli Carter # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. from genshi.builder import tag from trac.core import Component, implements from trac.perm import IPermissionRequestor from trac.ticket.api import ITicketActionController from trac.ticket.default_workflow import ConfigurableTicketWorkflow revision = "$Rev: 13432 $" url = "$URL: https://svn.edgewall.org/repos/trac/branches/1.2-stable/sample-plugins/workflow/CodeReview.py $" class CodeReviewActionController(Component): """Support for simple code reviews. The action that supports the `code_review` operation will present an extra choice for the review decision. Depending on that decision, a specific state will be selected. Example (from the enterprise-review-workflow.ini): {{{ review = in_review -> * review.label = review as review.operations = code_review review.code_review = approve -> in_QA, approve as noted -> post_review, request changes -> in_work }}} Don't forget to add the `CodeReviewActionController` to the workflow option in the `[ticket]` section in TracIni. If there is no other workflow option, the line will look like this: {{{ workflow = ConfigurableTicketWorkflow,CodeReviewActionController }}} """ implements(ITicketActionController, IPermissionRequestor) # IPermissionRequestor methods def get_permission_actions(self): return ['TICKET_REVIEW'] # ITicketActionController methods def get_ticket_actions(self, req, ticket): # The review action is available in those status where it has been # configured, for those users who have the TICKET_REVIEW permission, as # long as they are not the owner of the ticket (you can't review your # own work!). actions_we_handle = [] if req.authname != ticket['owner'] and \ 'TICKET_REVIEW' in req.perm(ticket.resource): controller = ConfigurableTicketWorkflow(self.env) actions_we_handle = controller.get_actions_by_operation_for_req( req, ticket, 'code_review') self.log.debug('code review handles actions: %r', actions_we_handle) return actions_we_handle def get_all_status(self): all_status = set() controller = ConfigurableTicketWorkflow(self.env) ouractions = controller.get_actions_by_operation('code_review') for weight, action in ouractions: status = [status for option, status in self._get_review_options(action)] all_status.update(status) return all_status def render_ticket_action_control(self, req, ticket, action): id, grade = self._get_grade(req, action) review_options = self._get_review_options(action) actions = ConfigurableTicketWorkflow(self.env).actions selected_value = grade or review_options[0][0] label = actions[action]['label'] control = tag(["as: ", tag.select([tag.option(option, selected= (option == selected_value or None)) for option, status in review_options], name=id, id=id)]) if grade: new_status = self._get_new_status(req, ticket, action, review_options) hint = "Next status will be '%s'" % new_status else: hint = "Next status will be one of " + \ ', '.join(["'%s'" % status for option, status in review_options]) return (label, control, hint) def get_ticket_changes(self, req, ticket, action): new_status = self._get_new_status(req, ticket, action) return {'status': new_status or 'new'} def apply_action_side_effects(self, req, ticket, action): pass # Internal methods def _get_grade(self, req, action): id = action + '_code_review_result' return id, req.args.get(id) def _get_review_options(self, action): return [[x.strip() for x in raw_option.split('->')] for raw_option in self.config.getlist('ticket-workflow', action + '.code_review')] def _get_new_status(self, req, ticket, action, review_options=None): id, grade = self._get_grade(req, action) if not review_options: review_options = self._get_review_options(action) for option, status in review_options: if grade == option: return status Trac-1.2/sample-plugins/workflow/MilestoneOperation.py0000644000175500017550000001236513007223745023015 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2012-2015 Edgewall Software # Copyright (C) 2012 Franz Mayer # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. from trac.core import Component, implements from trac.resource import ResourceNotFound from trac.ticket.api import ITicketActionController from trac.ticket.default_workflow import ConfigurableTicketWorkflow from trac.ticket.model import Milestone from trac.util.translation import _ from trac.web.chrome import add_warning revision = "$Rev$" url = "$URL$" class MilestoneOperation(Component): """Sets milestone for specific status. === Example === {{{ [ticket-workflow] resolve.operations = set_resolution,set_milestone resolve.milestone = invalid,wontfix,duplicate,worksforme -> rejected }}} When setting status to `duplicate` the milestone will automatically change to `rejected`. '''Note:''' if user has changed milestone manually, this workflow operation has ''no effect''! === Configuration === Don't forget to add `MilestoneOperation` to the workflow option in `[ticket]` section. If there is no workflow option, the line will look like this: {{{ [ticket] workflow = ConfigurableTicketWorkflow,MilestoneOperation }}} """ implements(ITicketActionController) def get_ticket_actions(self, req, ticket): actions_we_handle = [] if req.authname != 'anonymous' and \ 'TICKET_MODIFY' in req.perm(ticket.resource): controller = ConfigurableTicketWorkflow(self.env) actions_we_handle = controller.get_actions_by_operation_for_req( req, ticket, 'set_milestone') self.log.debug('set_milestone handles actions: %r', actions_we_handle) return actions_we_handle def get_all_status(self): return [] def render_ticket_action_control(self, req, ticket, action): actions = ConfigurableTicketWorkflow(self.env).actions label = actions[action]['label'] res_ms = self.__get_resolution_milestone_dict(ticket, action) resolutions = '' milestone = None for i, resolution in enumerate(res_ms): if i > 0: resolutions = "%s, '%s'" % (resolutions, resolution) else: resolutions = "'%s'" % resolution milestone = res_ms[resolution] hint = None if res_ms: try: Milestone(self.env, milestone) except ResourceNotFound: pass else: hint = _("For resolution %(resolutions)s the milestone will " "be set to '%(milestone)s'.", resolutions=resolutions, milestone=milestone) return label, None, hint def get_ticket_changes(self, req, ticket, action): if action == 'resolve' and \ req.args and 'action_resolve_resolve_resolution' in req.args: old_milestone = ticket._old.get('milestone') or None user_milestone = ticket['milestone'] or None # If there's no user defined milestone, we try to set it # using the defined resolution -> milestone mapping. if old_milestone is None: new_status = req.args['action_resolve_resolve_resolution'] new_milestone = self.__get_new_milestone(ticket, action, new_status) # ... but we don't reset it to None unless it was None if new_milestone is not None or user_milestone is None: try: Milestone(self.env, new_milestone) except ResourceNotFound: add_warning(req, _("Milestone %(name)s does " "not exist.", name=new_milestone)) else: self.log.info('changed milestone from %s to %s', old_milestone, new_milestone) return {'milestone': new_milestone} return {} def apply_action_side_effects(self, req, ticket, action): pass def __get_new_milestone(self, ticket, action, new_status): """Determines the new status""" if new_status: res_ms = self.__get_resolution_milestone_dict(ticket, action) return res_ms.get(new_status) def __get_resolution_milestone_dict(self, ticket, action): transitions = self.config.get('ticket-workflow', action + '.milestone').strip() transition = [x.strip() for x in transitions.split('->')] res_milestone = {} if len(transition) == 2: resolutions = [y.strip() for y in transition[0].split(',')] for res in resolutions: res_milestone[res] = transition[1] return res_milestone Trac-1.2/sample-plugins/workflow/StatusFixer.py0000644000175500017550000000574113007223745021456 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2007-2013 Edgewall Software # Copyright (C) 2007 Eli Carter # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. from genshi.builder import tag from trac.core import Component, implements from trac.ticket.api import ITicketActionController, TicketSystem from trac.perm import IPermissionRequestor revision = "$Rev: 14533 $" url = "$URL: https://svn.edgewall.org/repos/trac/branches/1.2-stable/sample-plugins/workflow/StatusFixer.py $" class StatusFixerActionController(Component): """Provides the admin with a way to correct a ticket's status. This plugin is especially useful when you made changes to your workflow, and some ticket status are no longer valid. The tickets that are in those status can then be set to some valid state. Don't forget to add `StatusFixerActionController` to the workflow option in the `[ticket]` section in TracIni. If there is no other workflow option, the line will look like this: {{{ workflow = ConfigurableTicketWorkflow,StatusFixerActionController }}} """ implements(ITicketActionController, IPermissionRequestor) # IPermissionRequestor methods def get_permission_actions(self): return ['TICKET_STATUSFIX'] # ITicketActionController methods def get_ticket_actions(self, req, ticket): actions = [] if ticket.exists and 'TICKET_STATUSFIX' in req.perm(ticket.resource): actions.append((0, 'force_status')) return actions def get_all_status(self): """Return all the status that are present in the database, so that queries for status no longer in use can be made. """ return [status for status, in self.env.db_query("SELECT DISTINCT status FROM ticket")] def render_ticket_action_control(self, req, ticket, action): # Need to use the list of all status so you can't manually set # something to an invalid state. selected_value = req.args.get('force_status_value', 'new') all_status = TicketSystem(self.env).get_all_status() render_control = tag.select( [tag.option(x, selected=(x == selected_value and 'selected' or None)) for x in all_status], id='force_status_value', name='force_status_value') return ("force status to", render_control, "The next status will be the selected one") def get_ticket_changes(self, req, ticket, action): return {'status': req.args.get('force_status_value')} def apply_action_side_effects(self, req, ticket, action): pass Trac-1.2/sample-plugins/revision_links.py0000644000175500017550000000522513007223745020356 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2007-2013 Edgewall Software # 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. """Sample Wiki syntax extension plugin.""" from genshi.builder import tag from trac.core import * from trac.util.text import shorten_line from trac.versioncontrol.api import NoSuchChangeset, RepositoryManager from trac.versioncontrol.web_ui import ChangesetModule from trac.wiki.api import IWikiSyntaxProvider revision = "$Rev: 12501 $" url = "$URL: https://svn.edgewall.org/repos/trac/branches/1.2-stable/sample-plugins/revision_links.py $" class RevisionLinks(Component): """Adds a few more ways to refer to changesets.""" implements(IWikiSyntaxProvider) KEYWORDS = ['[Rr]ev(?:ision)?', '[Cc]hangeset'] # IWikiSyntaxProvider methods def get_wiki_syntax(self): def revlink(f, match, fullmatch): elts = match.split() rev = elts[1] # ignore keyword reponame = '' if len(elts) > 2: # reponame specified reponame = elts[-1] return self._format_revision_link(f, 'revision', reponame, rev, rev, fullmatch) yield (r"!?(?:%s)\s+%s(?:\s+in\s+\w+)?" % ("|".join(self.KEYWORDS), ChangesetModule.CHANGESET_ID), revlink) def get_link_resolvers(self): def resolverev(f, ns, rev, label, fullmatch): return self._format_revision_link(f, ns, '', rev, label, fullmatch) yield ('revision', resolverev) def _format_revision_link(self, formatter, ns, reponame, rev, label, fullmatch=None): rev, params, fragment = formatter.split_link(rev) try: repos = RepositoryManager(self.env).get_repository(reponame) if repos: changeset = repos.get_changeset(rev) return tag.a(label, class_="changeset", title=shorten_line(changeset.message), href=(formatter.href.changeset(rev) + params + fragment)) except NoSuchChangeset: pass return tag.a(label, class_="missing changeset", rel="nofollow", href=formatter.href.changeset(rev)) Trac-1.2/.gitignore0000644000175500017550000000040413007223745013770 0ustar debacledebacle*~ .*.sw[op] .svn .hg build dist Trac.egg-info *.py[co] testenv testing.log functional-testing.log trac/test.db html htmlcov figleaf .coverage .figleaf* *.figleaf Makefile.cfg doc/.build *.mo trac/htdocs/js/messages/*.js .idea .project .pydevproject .settings Trac-1.2/tracini.cfg0000644000175500017550000000031213007223733014105 0ustar debacledebacle# mapping file for extracting messages of `*Option` and `ConfigSection` from # python files into trac/locale/tracini.pot (see setup.cfg) [python: **.py] [extractors] python = trac.dist:extract_python Trac-1.2/.appveyor.yml0000644000175500017550000000233713007223732014451 0ustar debacledebacleversion: '{branch}.{build}' clone_depth: 20 init: - ps: >- # Note 1: in 'Environment variables' groups above, use: # - SKIP_ENV=1 to completely skip a problematic environment # - SKIP_BUILD=1 to skip the Build step # - SKIP_TESTS=1 to skip the Tests step # Note 2: restoration of cached directories happens *after* this step environment: matrix: - SVN_BRANCH: 1.2-stable PYTHONHOME: C:\Python27 TRAC_TEST_DB_URI: - SVN_BRANCH: 1.2-stable PYTHONHOME: C:\Python27-x64 TRAC_TEST_DB_URI: sqlite:test.db - SVN_BRANCH: 1.2-stable PYTHONHOME: C:\Python26 TRAC_TEST_DB_URI: sqlite:test.db - SVN_BRANCH: 1.2-stable PYTHONHOME: C:\Python26-x64 TRAC_TEST_DB_URI: - SVN_BRANCH: 1.2-stable PYTHONHOME: C:\Miniconda TRAC_TEST_DB_URI: mysql://tracuser:password@localhost/trac - SVN_BRANCH: 1.2-stable PYTHONHOME: C:\Miniconda-x64 TRAC_TEST_DB_URI: postgres://tracuser:password@localhost/trac?schema=tractest services: - mysql - postgresql93 install: - ps: >- . .\contrib\appveyor.ps1 Trac-Install cache: C:\projects\dependencies build_script: - ps: >- . .\contrib\appveyor.ps1 Trac-Build test_script: - ps: >- . .\contrib\appveyor.ps1 Trac-Tests Trac-1.2/setup_wininst.bmp0000644000175500017550000011605613007223733015423 0ustar debacledebacleBM.6(  @@ "+B /$&$o*-+131&'c7:8./e>A?FHF<>c!!LNLGGiPRQUWV33\_]dgeHIknlknsutzvvx{y]\}~uu?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=0????????????????????????????????????????????????-=?????????????????????????????????????????????????????????????????????????????????????????????755:8?>;+75??=5=?:??:;??=557:?:?>;?75::>?:???>55;????;48?754:??87?==?:????:27??84=?;4:?:??;4:?>7:????>54>?=5;?:??=:?>=?84=?=5=?=4;??????????????????????*;=>/:2;*7=/?+>,>,??(>??;+==4=,??->/= =*?*???+8=+???=*>70>>2:75;-=/?0>??=+>/5:2>(?+>':->?+?/=-=75???+=:*?+>-;+??7'?>/>->+?*>-=,>,??????????????????????2:???*(?2:?,>+>>>2=?*????'??:558?,?-?2::'5???-=?*???75??(?'?>-28>=?+?87??=:?:/75?2;+?:278?*???+>>=???7??(?*>>>4:?472?+=-???+>>>?>*??????????????????????:0==?7$>:2?2;&55+:4?/>???(???-=-?->+?;4?'/???:0>,=??;/??0:+>?*:'5,8+>>-?????(>>,?:4/=?,=-?,>??/-5'?????28?&45+;0?0>/>,?+???$55+?/=???????????????????????&77?+=+> ;,=45;/? 755>??0:??-?"8,>->?,=-57???0/,*???(??845+;+?*;0;4,;-????/:??+;5777?,?$5:/;-=+;+????:-??70;-?$5,?=/58087754;->-;/??????????????????????*???:?=>;8;??87??;:7';??:4??;?;8:?=>?;==?:???+?>4???*>?=->:7>?>5:?=:7>???=-?;;?5:?>;?;?;;>:7>?;5=????*??8?:5>?;;:??:>=?78??87??:5>??????????????????????-:==????????????????8????+??=;???????????????45:>???:->07????????????????>*=+;???????????????????????,;7/????????????8??????????????????????????????????=778?????????????????????:??>;????????????????:7=????;5:??????????????????=5;?????????????????????????78?????????????:?????????????????????????????????????????????????????????????????????????;8458=??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>- '=?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????; >??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????> >?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????7;??5 $?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/:????, ???????????????????????????????????????????????????????????????????????????????????????????????????/$ &:???????>-"->??******8????????????:>????;(  />???????5******=??????-*****,??????=* +7??4*****;:******>:******>??????????????+ 7????7 2=4???=& "5?>5??:"?????? '?????;8????&= ;0=0=????????????? 4??4"7??/"?-?=>???7 ?????("???-=0=0=????????????5(&=> 8?0-? + ??? 7???????  =0=0=????????????????/'>??+8=*& -57???;??????84?? ???>=0=0=????????????5????;-----7>????8'*???>-*?????4----->?0>??$??????5=0=0=???????????:(----------5 ?????08 ?????4------------=? /?? =?*/5::=0=0=???????????0*?????88 ?????=-7=7?5"+??$=0=0=???????????5&(?????78 ?????>->+?;7/???2=0=0=???????????=/?????-8?????;-& ?+?,& ?>8????>5'  =0=0=???????????? 7????;8=????8"=????*-0?????:;?>&??"?;;;;;;????>?0=0=???????????? ????"?5:?> 85=??7-=4???=-$???:??0? 0???-?0=0=????????????=  7??8?-?*"? *???4???>4  &?0=0=?????????????8 ???: 8?= -??$75;???? (????=??0=0=??????????????>* 4?????= 788??>,:*???:=??????$;????2=>$>??0=0=????????????????>2*" *2?????????5*'4>?88????7*$/=??------7?????:/'&-8???0-----7?????=------0??????-----;???2'  '2?????0=0=??????????????????????????????????????????88??????????????????????????????????????????????????????????????????????????????????????0=0=??????????????????????????????????????????88??????????????????????????????????????????????????????????????????????????????????????0=0=??????????????????????????????????????????88??????????????????????????????????????????????????????????????????????????????????????0=0=??????????????????????????????????????????88??????????????????????????????????????????????????????????????????????????????????????0=0=??????????????????????????????????????????88??????????????????????????????????????????????????????????????????????????????????????0=0=??????????????????????????????????????????::??????????????????????????????????????????????????????????????????????????????????????5>5>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=4?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????5 =??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????$4???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????;>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/ :?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????> +???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????7 =????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????( 5?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????;"???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????2 :???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????> -?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????8 =??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????+ 7???????????????????????????????????????????????????????????????????????????????????????????????????????????????????=&?????????????????????????????????????????????????????????????????????????????????????????????????????????????????5;??????????????????????????????????????????????????????????????????????????????????????????????????????????????$ /???????????????????????????????????????????????????????????????????????????????????????????????????????????;>????????????????????????????????????????????????????????????????????????????????????????????????????????/  7?????????????????????????????????????????????????????????????????????????????????????????????????????>'???????????????????????????????????????????????????????????????????????????????????????????????????7 ;????????????????????????????????????????????????????????????????????????????????????????????????* 2??????????????????????????????????????????????????????????????????????????????????????????????> ??????????????????????????????????????????????????????????????????????????????????????????????4 ;????????????????????????????????????????????????????????????????????????????????????????????? /????????????????????????????????????????????????????????????????????????????????????????????? ????????????????????????????????????????????????????????????????????????????????????????????= ????????????????????????????????????????????????????????????????????????????????????????????2 ;??????????????????????????????????????????????????????????????????????????????????????????? /??????????????????????????????????????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????= ??????????????????????????????????????????????????????????????????????????????????????????0 ;????????????????????????????????????????????????????????????????????????????????????????? -????????????????????????????????????????????????????????????????????????????????????????? ????????????????????????????????????????????????????????????????????????????????????????= ????????????????????????????????????????????????????????????????????????????????????????/ !4;???????????????????????????????????????????????????????????????????????????????????????(>???; -???????????????????????????????????????????????????????????????????????????????????????  ,????????=" ??????????????????????????????????????????????????????????????????????????????????????; 5?????????????*??????????????????????????????????????????????????????????????????????????????????????/  :?????????????????0:?????????????????????????????????????????????????????????????????????????????????????!;?????????????????????7 ,????????????????????????????????????????????????????????????????????????????????????? !>?????????????????????????; ????????????????????????????????????????????????????????????????????????????????????;,??????????????????????????????>$ ????????????????????????????????????????????????????????????????????????????????????-  2???????????????????????????????????+:??????????????????????????????????????????????????????????????????????????????????? 7???????????????????????????????????????4,???????????????????????????????????????????????????????????????????????????????????;???????????????????????????????????????????8 ??????????????????????????????????????????????????????????????????????????????????:!=??????????????????????=5???????????????????????; ??????????????????????????????????????????????????????????????????????????????????, (>??????????????????????4 8??????????????????????>$ :????????????????????????????????????????????????????????????????????????????????? /??????????????????????:$  *=??????????????????????++????????????????????????????????????????????????????????????????????????????????? 5?????????????????????=+ 7???20??????????????????????4????????????????????????????????????????????????????????????????????????????????:$/:??????????????????40????????=+ 8?????????????????>7, ????????????????????????????????????????????????????????????????????????????????> +7>?????????:$ *=?????????????:"*=?????????=5( &???????????????????????????????????????????????????????????????????????????????????>7,  (4=?>+ 8???????????????????42??;0& $/:????????????????????????????????????????????????????????????????????????????????????????????:0& 2????????????????????????=+ (4=??????????????????????????????????????????????????????????????????????????????????????????????????????=5* *=?????????????????????????????:$ ,7>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????  8==4 ;=8&? +==: 7>; -==:&? ? ? ? ? 4=>8?:>4  -==:&?  7>;,==: > 7= ?" 2? ?$>?55 ;'? ? ?  ? ?8*'=?0 8055 ;'? ??$> :' ?? ? ->? ?"=? ? ?  ?  :? ?>?"=? ? -> ? ? ? $;?8? ? ? ? ? ? ? (5?====> ? ? ? ? ? ? $;?8 ? ? ? ?* ?  ? "= ? ? ? ?  ;'? ? ? > ? "= ? ? ?* ? ? ? ? &? ? 08 ;+ ?+ 0?- /= ?==?; 70 54 ?0 :/ 08 ;+ ?+ ? ? &? ? ? ? ;=;" =?== ,;=;* ?":>:&;>8 ? /7 4==4 ?$;>4 ,;=;* ?,=*=?== ;=;" 8+ ? ? ? ? ? > 7= 4 ? +; 2 7=>5      ?===8                +===5    ? 48    ? ? -=>: -=>: $;=8(? 4=>8 ? ? />;*? 8=;" ? += ? ? ? 8>7'? ?? &> ? &> ?& 4? 8+ *> ? ? ? /? ;, (>? ??? ? :4 :? ? 0> 0> ??? ? ? ?& ?? ?=* ?? ?? $?? ';?8 (;?8 ???====>? ?';>>?? ?85 ?? ?? ???,?-??? ??????48????$??? *?? *? ? ?8475??-? *>81*>?57??,2=:5:? ?&;=;'';=;' ?"?7>>5??/=-/=>;&8>=& ?87 ? ?(;>: 8>:-? ?? ?"? ?"?$?===>?==="?"= ?&?+ ??-=&?':=>72==;+??0>;,??';>52==;+ ?4>;,?8>= ?2==;+ ?"?-?4+?8 ??? ?'>$8=77=,???0??4:477=, ? ?0?? ?77=, ?"?87>74? ??=+ ?;-&?'?*= ???( ??'>?*= ??( ?? ??*= ? ?? => ;& ??87 ??$?"? ??*;>>? ?"? ?"? ?*;>>?? ? ?"? ?"?'>4/ >04 ? ?5:"?$??#*>"? ?"?#?(>?$*>$?"? ? ??"*>$?$?47&:-7&="?"?78#?&?5: =0$? ??&->$?5;57: =2&?0"?&->$?"?5: =2&?05>;-?8,? $?$?:8'? ?"4=>=0&?"?0>>=*&?':>8 4=>=1'?4=02>>=(>?>>&?4=>=0'?-;>;' "?$8-?":/&?;0 ?''?$?'?'? ->/>>0:'? *>*8= (?'? (? $7  77 $?7 (?*> (? *>" *:>>8& (? (?  (?  (>" """"""""""""""""""""" " " """"""""""" """"    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""#"""#""""""""""""##"#"#"#"#""""""""""##""""""""""""#"##"""# ""#"""#"""#"""""""""#"""#""#""#"""""##""#""""""#""""""""""$$##$#$$$$#$$$$$$$$$$#$#$$#$$$$$$###$##$$$$$$$$$$$$#$$#$$$#$##  "##$##$#$$$$$$#$#"$#$"$$$$$$##$"$#$$#$$"$$"#$"$$$$#$##$$$$$$$$$#$$$$$$$$$##$$$$$$$$$$#$#$$$$$$$#$$$#$$$"####$$$$$$$$$$"" #"#$$$$$$$$$$$$$#$$$#$$$$$$$$$$$$$$$""$$$$$$$$$##$$#$$$$#$"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"'8>>=0*;>>8$&?;8$? "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&$$>5 :4*>'":= &? ??$? "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*?"'?;/ *?+'?28:,"? $&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''&&$ /? ?$ """ '?>+-;"? $&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''&"'7?8'?  " '?'? ?"$? $&'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''((((''('('((''''''''(''''(''''''('''''''''(('''''''''''(&$(8??>4 (?  &?7582$?"#'''''''''''(''('''''(((''''''''''''''('''''''''''('''''''((((((((((((((((((((((((((((((((((((((((((((((((((((''''(((('''>=/& &?''??( -=&?"&'((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((*(((((((((((((((((((((((((((((((((((((((((((('''(((((',?&  "$;4 '?+(?0> "#?0? $'(((((((((((((((((((((((((((((((((((((((((((((((((((((((((*(*(**((*((*****(*(((***(****(*(*****((*(((*((*(*((('&&''(***((>5&#$5>&&->-"&:>#+?=5 "" 7;? &((*(**((((**((****(((**(**((*(****((*(**(*****(*****(*******************************************((((((((((((('&$$&'(****(+8>>>;-'''-;>>:*&,??*$" +?? "&'(***(((((((((((((((((***(((((****************************+++++*++*+*++*++++*+*++++*****((*****(('''''''''''''$" $&(****('''''''''''&&&&&&&&&&&$  $'(**(''''''''''''''''(((('''''(***+***+*++*****++++++*+**++++++++++++++++++++++++++++**('''****'&$$$$$$$$&&&&&=- $'***('&&&&''''&&$$$$$$$$$$$$4>>>8""$'(('&$""$$$$$&&&$$$$&''&$$$$&'(*+++++++++++++++++++++++++++++,+++++,+++++++++++++++++*(&&'(**(&$" "$$$2:$'***'&$""$&&&&$" """ ""(?+7: "&''&" # """" "$$$$" "&(*+++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,++*'$$$(**'$"  ""$?$"&***'$  $&$"   (? $&&$   "" #  $(*,+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,+(-?$$&((&0=>;0? '?'? *?4"&(**-? '? "$+? :>=$8>>;( ';>:1?&? "#&7>=0?:>= "8>>;( &:>;/?""'*+,,,,,,,,,,,,,,,,,,,,,,,,,-,,---,,--,------,--,,--,,,*-?" $''*?0"&8? '?'?  7;> &((*-? (? "+? '?";40>=7 ";?&? #,?*4?&?" ;40>;8 ";?#"(+,-------,,----,--,-,,,,,,---------------------------,*-?" "&&,?&$&,? '?'?"?*;/"&(**-? '? "*? '?&? &?' -?'? $*?/'?&?&? &?' -? $'+-------------------------////////////////-//-////-/--+-? "$&,?&&'-?$(?'?0=$4;&'(**-?"(? "*? (?*?>>>>?$+?$" *? (?""$&2=>>?'?*?>>>>?$+?$" *? $(+--//----///////-//////-/////////////////////////////-+/?" "$&-?((*/?&+? '? ;4'*?-(*++/?$*?$"$,?$*? '?($"-?&+?,$$/?$+?'$&''&$+?$*? '?($"-?&+?+$"/?"$(,-///////////////////////////////////////////////////-,/?>>>>-/?++,2?*,?"(?"*?+++:8+,,+/?&+?5'(8>',?$$';8**;:*+=:**=?(/?7(*-?0(5>',?$$';8**;:*+=:((;?$&*-/////////////////////////00000000000000000000000000//-0?&"$'(0?---4?,/?&+?&7:+--5?/--,0?'-?5=?=2+>?>>*-;>>:---0=>=7?-2?8>7,8>>>4+>?>>*-;>>:/--0=>=4?'(+-//00000000000000000000000101000000001110011000000001/-1?'&&'*+-/101/,1?(-?*(,-///011/-2?**,,-1//-4?-,-11////////////////////11//-4?-,,///11//////,0?*+-/1000000110000000000101000010010000001001000001001110014?+***+,/10001/4?,0?,,/10000000/4?-,/1000001;01/0100010000010000100010000002;0/110010100100/4?--/00011100010101000000110110222222222222222222222222222205?>>>>7/00222205?/4?0/00222222205?0/002222220002222222222222222222222222222200022222222222205?0/0022222222222222222222222222222222222222222222222222222220000000222222220000022222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222244444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222220000010010000000000000020100001000000002000002000000000000002002000020000200101000000000200100102010010000000000022000000000020000001000010100000001000000000000000000100010000000000000010000000000000000000000000000000000000000000001000000000000000000000000100000010000000110000000000110010000010000000000//////////////1////11////1//1//1//////////////////////////////1///////////////////1/////////////////1//1//1///////////////////////////////////////////////////////1///1///////////////////////////////////////////////////////////1///////////////1///1/1//////////////////////////////////////////////////////1////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--------------------------------------------------------------------------------------------------------------------------------------------------------??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<.).>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????6.>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????%9???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????%.???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<)????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????9))6???).?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<6???%????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????3???<9??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????39???66?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<.????9?????????????????????0' $/=?55555558????????????5* "-;????=5555557?????????:- $+27????????????????????????3)????))???????????????????>'-???????????2 0??(??????;&  ;?????????????????????%)????)%6????????????????????-??????????+=?????*$????????????????????.????.%9?????????????????????&-?????????8 ???????????????????????8& ??8"/70  ??????????????????))???????.)????6??????????????4:??????????????8??????8 ?? 8?????:4?????????????????3?????????.%????<3?????????????-5???????????????>???????( ?>0???????'"?????????????????<%)9??????????????????????????-=????????????????/ ?* ??????????????????????????????<)???93??????936?????)????????????-=???????:?????????=& ?/??????????????????????????????<???63?????)%????3????????????-=???????4????????????>;8740---- ?: ????????>5555555;??????????????%=======????????'?? 4???????,/??????????????>???<????9????????????-=???????55??????8-??$>??????:??????????????????).???>)????6????????? 4?5,>????=;??> 8???;???????????????????&'????/ 4???????????????????.????)6????>3.9???????????-?'&5?0:?????("????????????????????%>????)?????.6????????????->*? 5??-5???????0&?????????????????????))????<????3.???????????-;-??- 5???:& ;?????????; ";??????????????????????39????3????%???????????----+-----:=-------;????8,$:??????8*"  '0>?????????????>4&  $->?????????????????????????%?????3????)%???????????????-=??????????????????????????????????????????????????????????????????????????????????????????????69?????3????.6???????????????-=???????????????????????????????????????????????????????????????????????????????????????????????.9??????3)????9????????????????-=????????????????????????????????????????????????????????????????????????????????????????????????6%)?????????????????????????????????????????????????????????????????????????????????????????????.???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????36???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????6???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????9???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????)????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????.>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<.>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Trac-1.2/ChangeLog0000644000175500017550000000043413007223732013551 0ustar debacledebacleThe ChangeLog can be viewed: * On the web at http://trac.edgewall.org/wiki/TracChangeLog * In the source at trac/wiki/default-pages/TracChangeLog * By navigating to the TracChangeLog page after installing or upgrading Trac. You must upgrade the wiki to get the latest TracChangeLog. Trac-1.2/contrib/0000755000175500017550000000000013007224103013427 5ustar debacledebacleTrac-1.2/contrib/l10n_diff_index.py0000755000175500017550000000604213007223745016752 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2013 Edgewall Software # Copyright (C) 2013 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. """ L10N tool which prepares an index of "interesting" changes found in a .diff. Skipped changes are: - the changes for which the msgid has changed - removal only of msgstr content Example workflow 1), review changes to the 'fr' translation before committing: make diff-fr | less Example workflow 2), force a pull of all changes from Transifex:: make update updateopts=-N tx pull -f make update updateopts=-N svn diff > tx.diff python l10n_diff_index.py tx.diff svn revert -R . And then use either:: emacs tx.diff.index --eval '(grep-mode)' or:: vim -c :cbuffer -c :copen tx.diff.index This makes it easier to go through the potentially interesting changes only, and apply the corresponding chunks if needed. """ from __future__ import print_function from bisect import bisect_left import re interesting_changes_re = re.compile(r''' \n \s (?: msgid(?:_plural)?\s)? ".*" \n # ' ' msgid or "...", (?: [-\s] ".*" \n # ' ' or - "...", | - msgstr(?:\[\d+\])? \s ".*" \n # or the -msgstr )* (?: ( \+ msgstr(?:\[\d+\])? \s "[^"].*" ) \n # \1 is a non-empty +msgstr | [+\s] msgstr(?:\[\d+\])? \s ".*" \n # or after the msgstr, (?: [-\s] ".*" \n # optional ' ' or -"...", )* ( \+ "[^"].*" ) # \2 is a non-empty +"..." ) ''', re.MULTILINE | re.VERBOSE) def index_diffs(path, diffs): linenums = [] re.sub(r'\n', lambda m: linenums.append(m.start()), diffs) index = [] for m in interesting_changes_re.finditer(diffs): line = m.group(m.lastindex) if line.startswith(('+"Project-Id-Version:', '+"PO-Revision-Date:')): continue pos = m.start(m.lastindex) index.append((bisect_left(linenums, pos) + 1, line)) return index def write_index_for(path): with open(path, 'rb') as f: diffs = unicode(f.read(), 'utf-8') changes = index_diffs(path, diffs) if changes: index = path + '.index' with open(index, 'wb') as idx: for n, line in changes: print((u"%s:%s: %s" % (path, n, line)).encode('utf-8'), file=idx) print("%s: %d changes indexed in %s" % (path, len(changes), index)) else: print("%s: no interesting changes" % path) if __name__ == '__main__': import sys for path in sys.argv[1:]: write_index_for(path) Trac-1.2/contrib/htdigest.py0000755000175500017550000000616313007223745015640 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2006-2013 Edgewall Software # 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: Matthew Good from __future__ import print_function import errno import fileinput import sys from getpass import getpass from hashlib import md5 from optparse import OptionParser def ask_pass(): pass1 = getpass('New password: ') pass2 = getpass('Re-type new password: ') if pass1 != pass2: print("They don't match, sorry", file=sys.stderr) sys.exit(1) return pass1 def get_digest(userprefix, password=None): if password is None: password = ask_pass() return make_digest(userprefix, password) def make_digest(userprefix, password): return userprefix + md5(userprefix + password).hexdigest() usage = "%prog [-c] [-b] passwordfile realm username" parser = OptionParser(usage=usage) parser.add_option('-c', action='store_true', dest='create', default=False, help="Create a new file") parser.add_option('-b', action='store_true', dest='batch', default=False, help="Batch mode, password on the commandline.") if len(sys.argv) <= 1: parser.print_help() sys.exit(1) opts, args = parser.parse_args() try: if opts.batch: filename, realm, username, password = args else: filename, realm, username = args password = None except ValueError: parser.error("Wrong number of arguments") prefix = '%s:%s:' % (username, realm) if opts.create: try: f = open(filename, 'w') except EnvironmentError as e: if e.errno == errno.EACCES: print("Unable to update file", filename, file=sys.stderr) sys.exit(1) else: raise try: print(get_digest(prefix, password), file=f) finally: f.close() else: try: matched = False for line in fileinput.input(filename, inplace=True): if line.startswith(prefix): if not matched: print(get_digest(prefix, password)) matched = True else: print(line.rstrip()) if not matched: with open(filename, 'a') as f: print(get_digest(prefix, password), file=f) except EnvironmentError as e: if e.errno == errno.ENOENT: print("Could not open passwd file %s for reading." % filename, file=sys.stderr) print("Use -c option to create a new one.", file=sys.stderr) sys.exit(1) elif e.errno == errno.EACCES: print("Unable to update file", filename, file=sys.stderr) sys.exit(1) else: raise Trac-1.2/contrib/l10n_reset_en_GB.py0000755000175500017550000000421113007223745017023 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2013 Edgewall Software # Copyright (C) 2013 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. """ L10N tool which copies all msgid to the msgstr. This can be useful to verify the actual changes in the en_UK message catalogs. """ import re msgid_msgstr_re = re.compile(r''' ( # \1 "en_US" \r?\n msgid \s ".*" \r?\n (?: (?: msgid_plural \s )? ".*" \r?\n )* ) ( # \2 "en_GB" msgstr.* \s ".*" \r?\n (?: (?: msgstr.* \s )? ".*" \r?\n )* \r?\n ) ''', re.MULTILINE | re.VERBOSE) def reset_file(path): with open(path, 'rb+') as f: eol = '\r\n' content = f.read() if eol not in content: eol = '\n' def reset_msgstr(m): msgid, msgstr = m.groups() if '\nmsgid_plural' in msgid: msgstr = (msgid .replace(eol + 'msgid_plural', eol + 'msgstr[1]') .replace(eol + 'msgid', 'msgstr[0]')) else: msgstr = msgid.replace(eol + 'msgid', 'msgstr') return msgid + msgstr + eol sanitized, nsub = msgid_msgstr_re.subn(reset_msgstr, content) if nsub: print("reset %d messages to en_US in %s" % (nsub, path)) f.seek(0) f.write(sanitized) f.truncate() else: print("no messages found in %s" % (path,)) if __name__ == '__main__': import sys for path in sys.argv[1:]: reset_file(path) Trac-1.2/contrib/appveyor.ps10000644000175500017550000003057613007223745015747 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2016 Edgewall Software # Copyright (C) 2016 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. # ------------------------------------------------------------------ # This is a PowerShell script implementing the build steps used by # the AppVeyor Continuous Delivery service for Windows, Trac project. # # The builds results are published at: # # https://ci.appveyor.com/project/edgewall/trac # # or, for Git topic branches pushed on GitHub forks, at: # # https://ci.appveyor.com/project//trac # # ------------------------------------------------------------------ # ------------------------------------------------------------------ # Settings # ------------------------------------------------------------------ # Update the following variables to match the current build # environment on AppVeyor. # # See in particular: # - http://www.appveyor.com/docs/installed-software#python # - http://www.appveyor.com/docs/installed-software#mingw-msys-cygwin # - http://www.appveyor.com/docs/services-databases#mysql # - http://www.appveyor.com/docs/services-databases#postgresql $msysHome = 'C:\msys64\usr\bin' $deps = 'C:\projects\dependencies' $mysqlHome = 'C:\Program Files\MySql\MySQL Server 5.7' $mysqlPwd = 'Password12!' $pgHome = 'C:\Program Files\PostgreSQL\9.3' $pgUser = 'postgres' $pgPassword = 'Password12!' # External Python dependencies $pipCommonPackages = @( 'genshi', 'babel!=2.3.0,!=2.3.1', 'twill==0.9.1', 'configobj', 'docutils', 'pygments', 'pytz' ) $fcrypt = "$deps\fcrypt-1.3.1.tar.gz" $fcryptUrl = 'http://www.carey.geek.nz/code/python-fcrypt/fcrypt-1.3.1.tar.gz' $svnBase = "svn-win32-1.8.15" $svnBaseAp = "$svnBase-ap24" $svnUrlBase = "https://sourceforge.net/projects/win32svn/files/1.8.15/apache24" $pipPackages = @{ '1.0-stable' = @($fcrypt) '1.2-stable' = @('passlib') trunk = @('passlib') } $condaCommonPackages = @( 'lxml' ) # ------------------------------------------------------------------ # "Environment" environment variables # ------------------------------------------------------------------ # In the build matrix, we can set arbitrary environment variables # which together define the software configuration that will be # tested. # These variables are: # - SVN_BRANCH: the line of development (1.0-stable, ... trunk) # - PYTHONHOME: the version of python we are testing # - TRAC_TEST_DB_URI: the database backend we are testing # - SKIP_ENV: don't perform any step with this environment (optional) # - SKIP_BUILD: don't execute the Build step for this environment (optional) # - SKIP_TESTS: don't execute the Tests step for this environment (optional) # # Note that any combination should work, except for MySQL which can # only be installed conveniently from a Conda version of Python. # "Aliases" $pyHome = $env:PYTHONHOME $usingMysql = $env:TRAC_TEST_DB_URI -match '^mysql:' $usingPostgresql = $env:TRAC_TEST_DB_URI -match '^postgres:' $skipInstall = [bool]$env:SKIP_ENV $skipBuild = $env:SKIP_BUILD -or $env:SKIP_ENV $skipTests = $env:SKIP_TESTS -or $env:SKIP_ENV $svnBranch = $env:SVN_BRANCH # ------------------------------------------------------------------ # Utilities # ------------------------------------------------------------------ # Documentation for AppVeyor API (Add-AppveyorMessage, etc.) can be # found at: http://www.appveyor.com/docs/build-worker-api function Write-Step([string]$name, [bool]$skip) { if ($skip) { $message = "Skipping step $name" Write-Host $message Add-AppveyorMessage -Message $message } else { Write-Host @" ------------------------------------------------------------------ $name ------------------------------------------------------------------ "@ } } # Make it easier to run the tests locally, for debugging. # # Note that for this you may need to enable sourcing local scripts, # from your PowerShell console: # # Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process # # . .\contrib\appveyor.ps1 # # See http://trac.edgewall.org/wiki/AppVeyor for additional info. if (-not $env:APPVEYOR) { function Debug-Caller { $caller = (Get-Variable MyInvocation -Scope 1).Value.MyCommand.Name Write-Debug "$caller $args" } function Add-AppveyorMessage() { Debug-Caller @args } function Add-AppveyorTest() { Debug-Caller @args } function Update-AppveyorTest() { Debug-Caller @args } } # ------------------------------------------------------------------ # Prologue # ------------------------------------------------------------------ # Actions common to all steps (set up the PATH, determine Python version...) $env:Path = "$pyHome;$pyHome\Scripts;$msysHome;$($env:Path)" $pyV = [string](& python.exe -c 'import sys; print sys.version' 2>&1) $pyVersion = if ($pyV -match '^(\d\.\d)') { $Matches[1] } $py64 = ($pyV -match '64 bit') $pyIsConda = $pyV -match 'Continuum Analytics' # Subversion support if (-not $py64) { $env:Path = "$deps\$svnBase\bin;$($env:Path)" $env:PYTHONPATH = "$deps\$pyVersion\$svnBase\python;$($env:PYTHONPATH)" } # ------------------------------------------------------------------ # Steps # ------------------------------------------------------------------ function Trac-Install { Write-Step -Name INSTALL -Skip $skipInstall if ($skipInstall) { return } if (-not (Test-Path $deps)) { & mkdir $deps } # Download fcrypt if needed (only for 1.0-stable after #12239) if ($svnBranch -eq '1.0-stable') { if (-not (Test-Path $fcrypt)) { & curl.exe -sS $fcryptUrl -o $fcrypt } } # Subversion support via win32svn project, for Python 2.6 and 2.7 32-bits if (-not $py64) { $svnBinariesZip = "$deps\$svnBaseAp.zip" if (-not (Test-Path $svnBinariesZip)) { & curl.exe -Ss -L -o $svnBinariesZip ` "$svnUrlBase/$svnBaseAp.zip/download" & unzip.exe $svnBinariesZip -d $deps } $svnPython = "$($svnBaseAp)_py$($pyVersion -replace '\.', '')" $svnPythonZip = "$deps\$svnPython.zip" if (-not (Test-Path $svnPythonZip)) { & curl.exe -Ss -L -o $svnPythonZip ` "$svnUrlBase/$svnPython.zip/download" & mkdir "$deps\$pyVersion" & unzip $svnPythonZip -d "$deps\$pyVersion" } } # Install packages via pip # pip in Python 2.6 triggers the following warning: # https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning # use -W to avoid it if ($pyVersion -eq '2.6') { $ignoreWarnings = @( '-W', 'ignore:A true SSLContext object is not available' ) } function pip() { & python.exe $ignoreWarnings -m pip.__main__ @args # Note: -m pip works only in 2.7, -m pip.__main__ works in both } & pip --version & pip install $pipCommonPackages $pipPackages.$svnBranch if ($pyIsConda) { & conda.exe install -qy $condaCommonPackages } if ($usingMysql) { # # $TRAC_TEST_DB_URI="mysql://tracuser:password@localhost/trac" # # Conda provides MySQL-python support for Windows (x86 and x64) & conda.exe install -qy mysql-python Add-AppveyorMessage -Message "1.1. mysql-python package installed" ` -Category Information } elseif ($usingPostgresql) { # # $TRAC_TEST_DB_URI= # "postgres://tracuser:password@localhost/trac?schema=tractest" # & pip install psycopg2 Add-AppveyorMessage -Message "1.1. psycopg2 package installed" ` -Category Information } & pip list # Prepare local Makefile.cfg ".uri = $env:TRAC_TEST_DB_URI" | Out-File -Encoding ASCII 'Makefile.cfg' # Note 1: echo would create an UCS-2 file with a BOM, make.exe # doesn't appreciate... # Note 2: we can't do more at this stage, as the services # (MySQL/PostgreSQL) are not started yet. } function Trac-Build { Write-Step -Name BUILD -Skip $skipBuild if ($skipBuild) { return } # Preparing database if needed if ($usingMysql) { # # $TRAC_TEST_DB_URI="mysql://tracuser:password@localhost/trac" # $env:MYSQL_PWD = $mysqlPwd $env:Path = "$mysqlHome\bin;$($env:Path)" Write-Host "Creating 'trac' MySQL database with user 'tracuser'" & mysql.exe -u root -e ` ('CREATE DATABASE trac DEFAULT CHARACTER SET utf8mb4' + ' COLLATE utf8mb4_bin') & mysql.exe -u root -e ` 'CREATE USER tracuser@localhost IDENTIFIED BY ''password'';' & mysql.exe -u root -e ` 'GRANT ALL ON trac.* TO tracuser@localhost; FLUSH PRIVILEGES;' Add-AppveyorMessage -Message "2.1. MySQL database created" ` -Category Information } elseif ($usingPostgresql) { # # $TRAC_TEST_DB_URI= # "postgres://tracuser:password@localhost/trac?schema=tractest" # $env:PGUSER = $pgUser $env:PGPASSWORD = $pgPassword $env:Path = "$pgHome\bin;$($env:Path)" Write-Host "Creating 'trac' PostgreSQL database with user 'tracuser'" & psql.exe -U postgres -c ` ('CREATE USER tracuser NOSUPERUSER NOCREATEDB CREATEROLE' + ' PASSWORD ''password'';') & psql.exe -U postgres -c ` 'CREATE DATABASE trac OWNER tracuser;' Add-AppveyorMessage -Message "2.1. PostgreSQL database created" ` -Category Information } Write-Host "make compile" # compile: if there are fuzzy catalogs, an error message will be # generated on stderr. & make.exe Trac.egg-info compile 2>&1 | Tee-Object -Variable make $stderr = $make | ?{ $_ -is [System.Management.Automation.ErrorRecord] } $stdout = $make | ?{ $_ -isnot [System.Management.Automation.ErrorRecord] } if ($LastExitCode) { Add-AppveyorMessage -Message "2.2. make compile produced errors" ` -Category Error -Details ($stderr -join "`n") } elseif ($stderr) { Add-AppveyorMessage -Message "2.2. make compile produced warnings" ` -Category Warning -Details ($stderr -join "`n") } else { Add-AppveyorMessage -Message "2.2. make compile was successful" ` -Category Information } } function Trac-Tests { Write-Step -Name TESTS -Skip $skipTests $config = "$pyHome - $env:TRAC_TEST_DB_URI" if ("$env:TRAC_TEST_DB_URI" -eq '') { $config += 'sqlite :memory:' } function Make-Test([string]$goal, [string]$name, [ref]$code) { if ($skipTests) { Add-AppveyorTest -Name $name -Outcome Skipped return } Write-Host "make $goal" Add-AppveyorTest -Name $name -Outcome Running & make.exe $goal 2>&1 | Tee-Object -Variable make # Determine outcome Passed or Failed $outcome = 'Passed' if ($LastExitCode) { $outcome = 'Failed' $code.value += 1 } $stderr = $make | ?{ $_ -is [System.Management.Automation.ErrorRecord] } $stdout = $make | ?{ $_ -isnot [System.Management.Automation.ErrorRecord] } # Retrieve duration of the tests $msecs = 0 if ([string]$stderr -match "Ran \d+ tests in (\d+\.\d+)s") { $secs = $matches[1] $msecs = [math]::Round([float]$secs * 1000) } Update-AppveyorTest -Name $name -Outcome $outcome ` -StdOut ($stdout -join "`n") -StdErr ($stderr -join '') ` -Duration $msecs } $exit = 0 # # Running unit-tests # Make-Test -Goal unit-test -Name "Unit tests for $config" ` -Code ([ref]$exit) # # Running functional tests # Make-Test -Goal functional-test -Name "Functional tests for $config" ` -Code ([ref]$exit) if (-not $exit -eq 0) { Write-Host "Exiting with code $exit" Exit $exit } if (-not $skipTests) { Write-Host "All tests passed." } } Trac-1.2/contrib/trac-svn-hook0000755000175500017550000001742313007223745016072 0ustar debacledebacle#!/bin/sh # -*- coding: utf-8 -*- # # Copyright (C) 2009-2013 Edgewall Software # Copyright (C) 2009 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. # # = trac-svn-hook = # # Purpose:: this script is meant to be called from the Subversion hooks # for notifying Trac when changesets are added or modified. # # Scope:: The http://trac.edgewall.org/wiki/TracRepositoryAdmin page # describes how to directly call the relevant trac-admin commands # from the Subversion hooks. In most cases this should be enough, # however this script should make troubleshooting easier and # has support for notifying multiple Trac environments. # # Usage:: copy this script to some central place, for example in your # TRAC_ENV or TRAC_PARENT_ENV folder # **Be sure to read the Configuration Notes section below first** # then fill in the variables listed below the Configuration section. # # For each Subversion repository $REPOS that has to be monitored by # your Trac environment(s), you need to modify the hooks in order to # call the present script: # # Add this to your `$REPOS/hooks/post-commit` script: # # /path/to/trac-svn-hook $REPOS $REV # # If you allow revision property editing in `$REPOS/hooks/pre-revprop-change`, # then you can let Trac know about modified changesets by adding the following # lines to the `$REPOS/hooks/post-revprop-change` script: # # if [ "$PROPNAME" = "svn:log" -o "$PROPNAME" = "svn:author" ]; then # /path/to/trac-svn-hook $REPOS $REV $USER $PROPNAME # fi # # See also http://svnbook.red-bean.com/en/1.5/svn.reposadmin.create.html#svn.reposadmin.create.hooks # # Platform:: Unix or Cygwin. # # On Windows, if you have Cygwin installed, you can also use this # script instead of the `trac-svn-hook.cmd`. # In your `post-commit.bat` and `post-revprop-change.bat` hooks, call # this script using: # # bash /path/to/trac-svn-hook "%1" "%2" "%3" "%4" # # ----------------------------------------------------------------------------- # # == Configuration # # Uncomment and adapt to your local setup: # # export TRAC_ENV=/path/to/trac-env:/path/to/another/trac-env # export PATH=/path/to/python/bin:$PATH # export LD_LIBRARY_PATH=/path/to/python/lib:$LD_LIBRARY_PATH # # ----------------------------------------------------------------------------- # # == Configuration Notes # # As a preliminary remark, you should be aware that Subversion usually # run the hooks in a very minimal environment. # This is why we have to be very explicit about where to find things. # # According to http://subversion.apache.org/faq.html#hook-debugging, # one useful method for getting the post-commit hook to work is to call # the hook manually from a shell, as the user(s) which will end up running # the hook (e.g. wwwrun, www-data, nobody). For example: # # env - $REPOS/hooks/post-commit $REPOS 1234 # # or: # # env - $REPOS/hooks/post-revprop-change $REPOS 1234 nobody svn:log # # # The environment variables that have to be set in this script are # TRAC_ENV, PATH and eventually LD_LIBRARY_PATH. # # TRAC_ENV:: the path(s) to the Trac environment(s) # # In case you need to maintain more than one environment in sync with # the repository (using a different scope or not), simply specify more # than one path, using the ":" path separator (or ";" if the script is # used on Windows with Cygwin's bash - in this case also don't forget to # enclose the list of paths in quotes, e.g. TRAC_ENV="path1;path2"). # # Note that if you have to maintain multiple repositories and multiple Trac # environments, and you have some non-trivial mapping between repositories # and Trac environments, then you can leave the TRAC_ENV setting out of # this file and put it directly in your post-commit and post-revprop-change # hooks, so that the changes to each repositories are notified to the # appropriate environments (don't forget to export TRAC_ENV in this case). # # PATH:: the folder containing the trac-admin script # # This folder is typically the same as your Python installation bin/ folder. # If this is /usr/bin, then you probably don't need to put it in the PATH. # # Note that if you're using a python program installed in a non-default # location (such as /usr/local or a virtual environment), then you need # to add it to the PATH as well. # # LD_LIBRARY_PATH:: folder(s) containing additional required libraries # # You may also need to setup the LD_LIBRARY_PATH accordingly. # The same goes for any custom dependency, such as SQLite libraries or # SVN libraries: make sure everything is reachable. # For example, if you get errors like "global name 'sqlite' is not defined" # or similar, then make sure the LD_LIBRARY_PATH contains the path to all # the required libraries (libsqlite3.so in the above example). # # # ----------------------------------------------------------------------------- # # == Examples # # === Minimal setup example === # # Python is installed in /usr/bin, Trac was easy_install'ed. # # {{{ # export TRAC_ENV=/srv/trac/the_trac_env # }}} # # # === Virtualenv setup example === # # Here we're using a Trac installation set up using virtualenv # (http://pypi.python.org/pypi/virtualenv). # # In this example, the virtualenv is located in # /packages/trac/branches/trac-multirepos # and is based off a custom Python installation (/opt/python-2.4.4). # We're also using a custom SQLite build (/opt/sqlite-3.3.8). # # Note that virtualenv's activate script doesn't seem to care # about LD_LIBRARY_PATH and the only other thing it does and that # we need here is to set the PATH, we can as well do that ourselves: # # We also want to notify two Trac instances: # # {{{ # export TRAC_ENV=/srv/trac/the_trac_env:/srv/trac/trac_other_trac_env # export PATH=/packages/trac/branches/trac-multirepos/bin:$PATH # export LD_LIBRARY_PATH=/opt/python-2.4.4/lib:/opt/sqlite-3.3.8/lib:$LD_LIBRARY_PATH # }}} # # # === Cygwin setup example === # # {{{ # export TRAC_ENV=C:/Workspace/local/trac/devel # export PYTHONPATH=C:/Workspace/src/trac/repos/multirepos # export PATH=/C/Dev/Python261/Scripts:$PATH # }}} # # ----------------------------------------------------------------------------- # # This is the script itself, you shouldn't need to modify this part. # -- Command line arguments (cf. usage) REPOS="$1" REV="$2" USER="$3" PROPNAME="$4" # -- Foolproofing if [ -z "$REPOS" -o -z "$REV" ]; then echo "Usage: $0 REPOS REV" exit 2 fi if ! python -V 2>/dev/null; then echo "python is not in the PATH ($PATH), check PATH and LD_LIBRARY_PATH." exit 2 fi if [ -z "$TRAC_ENV" ]; then echo "TRAC_ENV is not set." exit 2 fi # -- Feedback echo "----" if [ -z "$USER" -a -z "$PROPNAME" ]; then EVENT="added" echo "Changeset $REV was added in $REPOS" else EVENT="modified" echo "Changeset $REV was modified by $USER in $REPOS" fi # -- Call "trac-admin ... changeset ... $REPOS $REV" for each Trac environment ifs=$IFS IFS=: if [ -n "$BASH_VERSION" ]; then # we can use Bash syntax if [[ ${BASH_VERSINFO[5]} = *cygwin ]]; then IFS=";" fi fi for env in $TRAC_ENV; do if [ -r "$env/VERSION" ]; then log=$env/log/svn-hooks-`basename $REPOS`.log nohup sh <> $log 2>&1 & echo "Changeset $REV $EVENT" trac-admin $env changeset $EVENT $REPOS $REV && \ echo "OK" || echo "FAILED: see the Trac log" EOF else echo "$env doesn't seem to be a Trac environment, skipping..." fi done IFS=$ifs Trac-1.2/contrib/make_status.py0000755000175500017550000000604213007223745016341 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2014-2015 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. import StringIO import warnings from trac.util.text import print_table, printout def _svn_version(): from svn import core version = (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_MICRO) return '%d.%d.%d' % version + core.SVN_VER_TAG def _genshi_version(): import genshi return '%s (%s speedups)' % \ (genshi.__version__, 'with' if hasattr(genshi, '_speedups') else 'without') PACKAGES = [ ("Python", 'sys.version'), ("Setuptools", 'setuptools.__version__'), ("Genshi", '__main__._genshi_version()'), ("Babel", 'babel.__version__'), ("sqlite3", ('sqlite3.version', 'sqlite3.sqlite_version')), ("PySqlite", ('pysqlite2.dbapi2.version', 'pysqlite2.dbapi2.sqlite_version')), ("MySQLdb", 'MySQLdb.__version__'), ("Psycopg2", 'psycopg2.__version__'), ("SVN bindings", '__main__._svn_version()'), ("Mercurial", 'mercurial.util.version()'), ("Pygments", 'pygments.__version__'), ("Textile", 'textile.__version__'), ("Pytz", 'pytz.__version__'), ("Docutils", 'docutils.__version__'), ("Twill", 'twill.__version__'), ("LXML", 'lxml.etree.__version__'), ("coverage", 'coverage.__version__'), ("figleaf", 'figleaf.__version__'), ] def package_versions(packages, out=None): name_version_pairs = [] for name, accessors in packages: version = get_version(accessors) name_version_pairs.append((name, version)) print_table(name_version_pairs, ("Package", "Version"), ' : ', out) def get_version(accessors): if isinstance(accessors, tuple): version = resolve_accessor(accessors[0]) details = resolve_accessor(accessors[1]) if version: return "%s (%s)" % (version, details or '?') else: version = resolve_accessor(accessors) return version or 'not installed' def resolve_accessor(accessor): try: module, attr = accessor.rsplit('.', 1) version = attr.replace('()', '') version = getattr(__import__(module, {}, {}, [version]), version) if attr.endswith('()'): version = version() return version except Exception: return None def shift(prefix, block): return '\n'.join(prefix + line for line in block.split('\n') if line) def print_status(): warnings.filterwarnings('ignore', '', DeprecationWarning) # Twill 0.9... buf = StringIO.StringIO() package_versions(PACKAGES, buf) printout(shift(' ', buf.getvalue())) if __name__ == '__main__': print_status() Trac-1.2/contrib/README0000644000175500017550000000050213007223745014317 0ustar debacledebacleThis directory contains useful contributed scripts and programs for Trac. Please note that these scripts are provided AS-IS are NOT tested to the same extent as the main sources. They are usually maintained by the original author, typically listed at the top of the source file. See ../THANKS for a list of contributors.Trac-1.2/contrib/rpm/0000755000175500017550000000000013007224103014225 5ustar debacledebacleTrac-1.2/contrib/rpm/installscript0000644000175500017550000000017113007223745017055 0ustar debacledebaclepython setup.py install --optimize=1 --single-version-externally-managed --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES Trac-1.2/contrib/rpm/makerpm0000755000175500017550000000025113007223745015620 0ustar debacledebacle#!/bin/bash # Run this script from the top of the Trac source tree. # Tested on Fedora Core 6 python setup.py bdist_rpm --install-script=`pwd`/contrib/rpm/installscript Trac-1.2/contrib/trac-svn-post-commit-hook.cmd0000644000175500017550000000630413007223745021076 0ustar debacledebacle@ECHO OFF :: :: Copyright (C) 2007-2013 Edgewall Software :: Copyright (C) 2007 Markus Tacker :: 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.com/license.html. :: :: This software consists of voluntary contributions made by many :: individuals. For the exact contribution history, see the revision :: history and logs, available at http://trac.edgewall.org/. :: Trac post-commit-hook script for Windows :: :: Modified for the multirepos branch to use the `changeset` command. :: Usage: :: :: 1. Insert the following line in your REPOS/hooks/post-commit.bat script: :: :: call %~dp0\trac-post-commit-hook.cmd %1 %2 :: :: 2. Check the 'Modify paths' section below, be sure to set at least TRAC_ENV :: :: 3. Verify that the hook is working: :: :: - enable DEBUG level logging to a file and to the console :: (see TracLogging) :: :: - call the trac-post-commit-hook.cmd from a cmd.exe shell: :: :: trac-post-commit-hook.cmd 123 :: :: - call the post-commit.bat hook from a cmd.exe shell (check that :: no unwanted side-effects could be triggered when doing this...): :: :: post-commit.bat 123 :: :: - in each case, verify that you actually see the logging from Trac :: and in particular that you see something like (near the end): :: :: DEBUG: Event changeset_added on for revision 123 :: :: ---------------------------------------------------------- :: Modify paths here: :: -- this one *must* be set set TRAC_ENV= :: -- set if Python is not in the system path set PYTHON_PATH= :: -- set to the folder containing trac/ if installed in a non-standard location set TRAC_PATH= :: ---------------------------------------------------------- :: -- Do not execute hook if trac environment does not exist if not exist %TRAC_ENV% goto :EOF :: -- Determine trac-admin :: By default assume it's reachable from the PATH set TRAC_ADMIN=trac-admin.exe :: ... or take it from the Scripts folder of the specified Python installation if not %PYTHON_PATH%.==. set TRAC_ADMIN="%PYTHON_PATH%/Scripts/trac-admin.exe" :: ... or take it from the specified Trac source checkout if not %TRAC_PATH%.==. set TRAC_ADMIN=python.exe "%TRAC_PATH%/trac/admin/console.py" :: -- Setup the environment set PATH=%PYTHON_PATH%;%PATH% set PYTHONPATH=%TRAC_PATH%;%PYTHONPATH% :: -- Retrieve the information that Subversion gave to the hook set REPOS=%1 set REV=%2 :: Now we're about to call trac-admin's changeset added command. :: We have to call it like that: :: :: repository changeset added :: :: where can be the repository symbolic name or directly :: the repository directory, which we happen to have in %REPOS%. %TRAC_ADMIN% "%TRAC_ENV%" changeset added "%REPOS%" "%REV%" :: Based on either the symbolic name or the %REPOS% information, :: Trac will figure out which repository (or which scoped repositories) :: it has to synchronize. Trac-1.2/contrib/sourceforge2trac.py0000755000175500017550000006116713007223745017311 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2004-2013 Edgewall Software # Copyright (C) 2004 Dmitry Yusupov # Copyright (C) 2004 Mark Rowe # Copyright (C) 2010 Anatoly Techtonik # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. """ Import a Sourceforge project's tracker items into a Trac database. Requires: Trac 1.0 from http://trac.edgewall.org/ Python 2.5 from http://www.python.org/ 1.0 clean-up by cboos **untested**, use at your own risks and send patches The Sourceforge tracker items can be exported from the 'Backup' page of the project admin section. Substitute XXXXX with project id: https://sourceforge.net/export/xml_export2.php?group_id=XXXXX $Id$ Uses Trac 0.11 DB format version 21 SourceForge XML Export format identified by the header: Works with all DB backends. Attachments are not downloaded, but inserted as links to SF tracker. Ticket Types, Priorities and Resolutions ---------------------------------------- Conversion kills default Trac ticket types: - defect 1 - enhancement 2 - task 3 and priorities: - blocker 1 - critical 2 - major 3 - minor 4 - trivial 5 and resolutions: - fixed 1 - invalid 2 - wontfix 3 - duplicate 4 - worksforme 5 Versions and Milestones ----------------------- Kills versions and milestones from existing Trac DB Mapping ------- tracker_name == ticket_type group_name == version category_name == component user nobody == anonymous Not implemented (feature:reason) -------------------------------- attachments:made as a comment with links to attachments stored on SF (type,id,filename,size,time,description,author,ipnr) ticket_custom:unknown (ticket,name,value) history:imported only for summary, priority. closed date and owner fields severities:no field in source data """ import optparse import sys import time from xml.etree.ElementTree import ElementTree from trac.env import Environment from trac.util.text import printerr #: rename users from SF to Trac user_map = {"nobody":"anonymous"} complete_msg = """ Conversion complete. You may want to login into Trac to verify names for ticket owners. You may also want to rename ticket types and priorities to default. """ # --- utility class DBNotEmpty(Exception): def __str__(self): return "Will not modify database with existing tickets!" class FlatXML(object): """Flat XML is XML without element attributes. Also each element may contain other elements or text, but not both. This object mirrors XML structure into own properties for convenient access to tree elements, i.e. flat.trackers[2].groups[2].group_name Uses recursion. """ def __init__(self, el=None): """el is ElementTree element""" if el: self.merge(el) def merge(self, el): """merge supplied ElementTree element into current object""" for c in el: if len(c.getchildren()) == 0: if c.text != None and len(c.text.strip()) != 0: self.__setattr__(c.tag, c.text) else: self.__setattr__(c.tag, []) else: #if c.getchildren()[0].tag == c.tag[:-1]: # c is a set of elements self.__setattr__(c.tag, [FlatXML(x) for x in c.getchildren()]) def __str__(self): buf = "" for sub in self.__dict__: val = self.__dict__[sub] if type(val) != list: buf += "%s : %s\n" % (sub, val) else: for x in val: buf += "\n ".join(x.__str__().split("\n")) return buf def __repr__(self): buf = "" for sub in self.__dict__: val = self.__dict__[sub] if type(val) != list: buf += "<%s>%s\n" % (sub, val, sub) else: for x in val: buf += "\n ".join(x.__repr__().split("\n")) return buf # --- SF data model class Tracker(FlatXML): """ http://sourceforge.net/?group_id=175454&atid=873299 873299 Bugs Bug Tracking System All site users Yes Send to goblinhack@gmail.com 2592000 1209600 0 0 1 632324 v1.0 (example) 885178 Interface (example) nobody 1 Fixed 2 Invalid ... 1 Open 2 Closed 3 Deleted 4 Pending ... http://sourceforge.net/support/tracker.php?aid=2471428 2471428 2 100 100 100 sbluen nobody goblinhack 1230400444 1231087612 5 glitch with edge of level
The mini-laser that the future soldier carries is so powerful that it even lets me go outside the level. I stand at the top edge of the level and then shoot up, and then it gets me somewhere where I am not supposed to go.
0 2335316 goblinhack 1175610236
Logged In: YES user_id=1577972 Originator: NO does this happen every game or just once? you could send me the saved file and I'll try and load it - old versions harldy ever work with newer versions - need to add some kind of warnings on that tx
...
http://sourceforge.net/tracker/download.php?group_id=175454&atid=873299&file_id=289080&aid= 289080 your_most_recent_game.gz my saved game 112968 application/x-gzip 1218987770 sbluen ... 7304242 IP Artifact Created: 76.173.48.148 1230400444 sbluen ...
...
...
""" def __init__(self, e): self.merge(e) class ExportedProjectData(object): """Project data container as Python object. """ def __init__(self, f): """Data parsing""" self.trackers = [] #: tracker properties and data self.groups = [] #: groups [] self.priorities = [] #: priorities used self.resolutions = [] #: resolutions (index, name) self.tickets = [] #: all tickets self.statuses = [] #: status (idx, name) self.used_resolutions = {} #: id:name self.used_categories = {} #: id:name # id '100' means no category self.used_categories['100'] = None self.users = {} #: id:name root = ElementTree().parse(f) self.users = dict([(FlatXML(u).userid, FlatXML(u).username) for u in root.find('referenced_users')]) for tracker in root.find('trackers'): tr = Tracker(tracker) self.trackers.append(tr) # groups-versions for grp in tr.groups: # group ids are tracker-specific even if names match g = (grp.id, grp.group_name) if g not in self.groups: self.groups.append(g) # resolutions for res in tr.resolutions: r = (res.id, res.name) if r not in self.resolutions: self.resolutions.append(r) # statuses self.statuses = [(s.id, s.name) for s in tr.statuses] # tickets for tck in tr.tracker_items: if type(tck) == str: print(repr(tck)) self.tickets.append(tck) if int(tck.priority) not in self.priorities: self.priorities.append(int(tck.priority)) res_id = getattr(tck, "resolution_id", None) if res_id is not None and res_id not in self.used_resolutions: for idx, name in self.resolutions: if idx == res_id: break self.used_resolutions[res_id] = \ dict(self.resolutions)[res_id] # used categories categories = dict(self.get_categories(tr, noowner=True)) if tck.category_id not in self.used_categories: self.used_categories[tck.category_id] = \ categories[tck.category_id] # sorting everything self.trackers.sort(key=lambda x:x.name) self.groups.sort() self.priorities.sort() def get_categories(self, tracker=None, noid=False, noowner=False): """ SF categories : Trac components (id, name, owner) tuples for specified tracker or all trackers if noid or noowner flags are set, specified tuple attribute is stripped """ trs = [tracker] if tracker is not None else self.trackers categories = [] for tr in trs: for cat in tr.categories: c = (cat.id, cat.category_name, cat.auto_assignee) if c not in categories: categories.append(c) #: sort by name if noid: categories.sort() else: categories.sort(key=lambda x:x[1]) if noowner: categories = [x[:2] for x in categories] if noid: categories = [x[1:] for x in categories] return categories class TracDatabase(object): def __init__(self, path): self.env = Environment(path) def hasTickets(self): return int(self.env.db_query("SELECT count(*) FROM ticket")[0][0]) > 0 def dbCheck(self): if self.hasTickets(): raise DBNotEmpty def setTypeList(self, s): """Remove all types, set them to `s`""" self.dbCheck() with self.env.db_transaction as db: db("DELETE FROM enum WHERE type='ticket_type'") for i, value in enumerate(s): db("INSERT INTO enum (type, name, value) VALUES (%s, %s, %s)", ("ticket_type", value, i)) def setPriorityList(self, s): """Remove all priorities, set them to `s`""" self.dbCheck() with self.env.db_transaction as db: db("DELETE FROM enum WHERE type='priority'") for i, value in enumerate(s): db("INSERT INTO enum (type, name, value) VALUES (%s, %s, %s)", ("priority", value, i)) def setResolutionList(self, t): """Remove all resolutions, set them to `t` (index, name)""" self.dbCheck() with self.env.db_transaction as db: db("DELETE FROM enum WHERE type='resolution'") for value, name in t: db("INSERT INTO enum (type, name, value) VALUES (%s, %s, %s)", ("resolution", name, value)) def setComponentList(self, t): """Remove all components, set them to `t` (name, owner)""" self.dbCheck() with self.env.db_transaction as db: db("DELETE FROM component") for name, owner in t: db("INSERT INTO component (name, owner) VALUES (%s, %s)", (name, owner)) def setVersionList(self, v): """Remove all versions, set them to `v`""" self.dbCheck() with self.env.db_transaction as db: db("DELETE FROM version") for value in v: # time and description are also available db("INSERT INTO version (name) VALUES (%s)", value) def setMilestoneList(self, m): """Remove all milestones, set them to `m` (""" self.dbCheck() with self.env.db_transaction as db: db("DELETE FROM milestone") for value in m: # due, completed, description are also available db("INSERT INTO milestone (name) VALUES (%s)", value) def addTicket(self, type, time, changetime, component, priority, owner, reporter, cc, version, milestone, status, resolution, summary, description, keywords): """ ticket table db21.py format id integer PRIMARY KEY, type text, -- the nature of the ticket time integer, -- the time it was created changetime integer, component text, severity text, priority text, owner text, -- who is this ticket assigned to reporter text, cc text, -- email addresses to notify version text, -- milestone text, -- status text, resolution text, summary text, -- one-line summary description text, -- problem description (long) keywords text """ if status.lower() == 'open': if owner != '': status = 'assigned' else: status = 'new' with self.env.db_transaction as db: c = db.cursor() c.execute(""" INSERT INTO ticket (type, time, changetime, component, priority, owner, reporter, cc, version, milestone, status, resolution, summary, description, keywords) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """, (type, time, changetime, component, priority, owner, reporter, cc, version, milestone, status.lower(), resolution, summary, '%s' % description, keywords)) return db.get_last_id(c, 'ticket') def addTicketComment(self, ticket, time, author, value): with self.env.db_transaction as db: db(""" INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue) VALUES (%s, %s, %s, %s, %s, %s) """, (ticket, time, author, 'comment', '', '%s' % value)) def addTicketChange(self, ticket, time, author, field, oldvalue, newvalue): with self.env.db_transaction as db: db("""INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue) VALUES (%s, %s, %s, %s, %s, %s) """, (ticket, time, author, field, oldvalue, newvalue)) def importData(f, env, opt): project = ExportedProjectData(f) trackers = project.trackers trac = TracDatabase(env) # Data conversion typeList = [x.name for x in trackers] print("%d trackers will be converted to the following ticket types:\n %s" \ % (len(trackers), typeList)) used_cat_names = set(project.used_categories.values()) #: make names unique, forget about competing owners (the last one wins) components = dict(project.get_categories(noid=True)).items() components.sort() components = [x for x in components if x[0] in used_cat_names] print("%d out of %d categories are used and will be converted to the" " following components:\n %s" % (len(components), len(project.get_categories()), components)) print("..renaming component owners:") for i,c in enumerate(components): if c[1] in user_map: components[i] = (c[0], user_map[c[1]]) print(" %s" % components) print("%d groups which will be converted to the following versions:\n" " %s" % (len(project.groups), project.groups)) print("%d resolutions found :\n %s" % (len(project.resolutions), project.resolutions)) resolutions = [(k,project.used_resolutions[k]) for k in project.used_resolutions] resolutions.sort(key=lambda x:int(x[0])) print(".. only %d used will be imported:\n %s" % (len(resolutions), resolutions)) print("Priorities used so far: %s" % project.priorities) if not(raw_input("Continue [y/N]?").lower() == 'y'): sys.exit() # Data save trac.setTypeList(typeList) trac.setComponentList(components) trac.setPriorityList(range(min(project.priorities), max(project.priorities))) trac.setVersionList(set([x[1] for x in project.groups])) trac.setResolutionList(resolutions) trac.setMilestoneList([]) for tracker in project.trackers: # id 100 means no component selected component_lookup = dict(project.get_categories(noowner=True) + [("100", None)]) for t in tracker.tracker_items: i = trac.addTicket(type=tracker.name, time=int(t.submit_date), changetime=int(t.submit_date), component=component_lookup[t.category_id], priority=t.priority, owner=t.assignee \ if t.assignee not in user_map \ else user_map[t.assignee], reporter=t.submitter \ if t.submitter not in user_map \ else user_map[t.submitter], cc=None, # 100 means no group selected version=dict(project.groups + [("100", None)])[t.group_id], milestone=None, status=dict(project.statuses)[t.status_id], resolution=dict(resolutions)[t.resolution_id] \ if hasattr(t, "resolution_id") else None, summary=t.summary, description=t.details, keywords='sf' + t.id) print("Imported %s as #%d" % (t.id, i)) if len(t.attachments): attmsg = "SourceForge attachments:\n" for a in t.attachments: attmsg = attmsg + " * [%s %s] (%s) - added by '%s' %s [[BR]] "\ % (a.url+t.id, a.filename, a.filesize+" bytes", user_map.get(a.submitter, a.submitter), time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(a.date)))) attmsg = attmsg + "''%s ''\n" % (a.description or '') # empty description is as empty list trac.addTicketComment(ticket=i, time=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(t.submit_date))), author=None, value=attmsg) print(" added information about %d attachments for #%d" % (len(t.attachments), i)) for msg in t.followups: """ 3280792 goblinhack 1231087739
done
""" trac.addTicketComment(ticket=i, time=msg.date, author=msg.submitter, value=msg.details) if t.followups: print(" imported %d messages for #%d" % (len(t.followups), i)) # Import history """ 4452195 resolution_id 100 1176043865 goblinhack """ revision = t.__dict__.copy() # iterate the history in reverse order and update ticket revision from # current (last) to initial changes = 0 for h in sorted(t.history_entries, reverse=True): """ Processed fields (field - notes): IP - no target field, just skip summary priority close_date assigned_to Fields not processed (field: explanation): File Added - TODO resolution_id - need to update used_resolutions status_id artifact_group_id category_id group_id """ f = None if h.field_name in ("IP",): changes += 1 continue elif h.field_name in ("summary", "priority"): f = h.field_name oldvalue = h.old_value newvalue = revision.get(h.field_name) elif h.field_name == 'assigned_to': f = "owner" newvalue = revision['assignee'] if h.old_value == '100': # was not assigned revision['assignee'] = None oldvalue = None else: username = project.users[h.old_value] if username in user_map: username = user_map[username] revision['assignee'] = oldvalue = username elif h.field_name == 'close_date' and revision['close_date'] != 0: f = 'status' oldvalue = 'assigned' newvalue = 'closed' if f: changes += 1 trac.addTicketChange(ticket=i, time=h.date, author=h.updator, field=f, oldvalue=oldvalue, newvalue=newvalue) if h.field_name != 'assigned_to': revision[h.field_name] = h.old_value if changes: print(" processed %d out of %d history items for #%d" % (changes, len(t.history_entries), i)) def main(): p = optparse.OptionParser( "Usage: %prog xml_export.xml /path/to/trac/environment") opt, args = p.parse_args() if len(args) != 2: p.error("Incorrect number of arguments") try: importData(open(args[0]), args[1], opt) except DBNotEmpty as e: printerr("Error: %s" % e) sys.exit(1) print(complete_msg) if __name__ == '__main__': main() Trac-1.2/contrib/workflow/0000755000175500017550000000000013007224103015301 5ustar debacledebacleTrac-1.2/contrib/workflow/workflow_parser.py0000755000175500017550000001103213007223745021114 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2007-2013 Edgewall Software # Copyright (C) 2007 Eli Carter # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. import sys import getopt import locale import pkg_resources pkg_resources.require('Trac') from trac.config import Configuration from trac.ticket.default_workflow import parse_workflow_config _debug = False def debug(s): if _debug: sys.stderr.write(s) def readconfig(filename): """Returns a list of raw config options""" config = Configuration(filename) rawactions = list(config.options('ticket-workflow')) debug("%s\n" % str(rawactions)) if not rawactions: sys.stderr.write("ERROR: You don't seem to have a [ticket-workflow] " "section.\n") sys.exit(1) return rawactions class ColorScheme(object): # cyan, yellow are too light in color colors = ['black', 'blue', 'red', 'green', 'purple', 'orange', 'darkgreen'] def __init__(self): self.mapping = {} self.coloruse = [0] * len(self.colors) def get_color(self, name): try: colornum = self.mapping[name] except KeyError: self.mapping[name] = colornum = self.pick_color(name) self.coloruse[colornum] += 1 return self.colors[colornum] def pick_color(self, name): """Pick a color that has not been used much so far.""" return self.coloruse.index(min(self.coloruse)) def actions2graphviz(actions, show_ops=False, show_perms=False): """Returns a list of lines to be fed to graphviz.""" # The size value makes it easier to create a useful printout. color_scheme = ColorScheme() digraph_lines = [""" digraph G { center=1 size="10,8" { rank=source; new [ shape=invtrapezium ] } { rank=sink; closed [ shape=trapezium ] } """] for action, attributes in actions.items(): label = [attributes['label']] if show_ops: label += attributes['operations'] if show_perms: label += attributes['permissions'] if 'set_resolution' in attributes: label += ['(' + attributes['set_resolution'] + ')'] for oldstate in attributes['oldstates']: color = color_scheme.get_color(attributes['label']) digraph_lines.append( ' "%s" -> "%s" [label="%s" color=%s fontcolor=%s]' % (oldstate, attributes['newstate'], '\\n'.join(label), color, color)) digraph_lines.append('}') return digraph_lines def main(filename, output, show_ops=False, show_perms=False): # Read in the config rawactions = readconfig(filename) # Parse the config information actions = parse_workflow_config(rawactions) # Convert to graphviz digraph_lines = actions2graphviz(actions, show_ops, show_perms) # And output output.write(unicode.encode('\n'.join(digraph_lines), locale.getpreferredencoding())) def usage(output): output.write('workflow_parser [options] configfile.ini [output.dot]\n' '-h --help shows this message\n' '-o --operations include operations in the graph\n' '-p --permissions include permissions in the graph\n') if __name__ == '__main__': show_ops = False show_perms = False try: opts, args = getopt.getopt(sys.argv[1:], 'hop', ['help', 'operations', 'permissions']) except getopt.GetoptError: usage(sys.stderr) sys.exit(1) for option, argument in opts: if option in ('-h', '--help'): usage(sys.stdout) sys.exit(0) elif option in ('-o', '--operations'): show_ops = True elif option in ('-p', '--permissions'): show_perms = True if not args: sys.stderr.write('Syntax error: config filename required.\n') usage(sys.stderr) sys.stderr.flush() sys.exit(1) ini_filename = args[0] if len(args) > 1: output = open(args[1], 'w') else: output = sys.stdout main(ini_filename, output, show_ops, show_perms) Trac-1.2/contrib/workflow/enterprise-workflow.ini0000644000175500017550000000372313007223745022052 0ustar debacledebacle[ticket-workflow] ; enterprise-workflow.ini ; create actions create = -> new create.default = 1 create_and_assign = -> assigned create_and_assign.label = assign create_and_assign.operations = may_set_owner create_and_assign.permissions = TICKET_MODIFY ; assign, reassign, unassign actions assign = new -> assigned assign.operations = set_owner assign.permissions = TICKET_MODIFY reassign = assigned,in_work -> assigned reassign.operations = set_owner reassign.permissions = TICKET_MODIFY reassign_closed = closed -> closed reassign_closed.label = reassign reassign_closed.operations = set_owner reassign_closed.permissions = TICKET_MODIFY unassign = assigned,in_work -> new unassign.operations = del_owner unassign.permissions = TICKET_MODIFY ; leave actions leave = * -> * leave.operations = leave_status leave.default = 1 ; test actions test = new,assigned,in_work -> in_QA test.permissions = TICKET_MODIFY ; resolve actions resolve = in_QA -> closed resolve.operations = set_resolution resolve.permissions = TICKET_MODIFY fail = in_QA -> assigned fail.permissions = TICKET_MODIFY ; start/stop actions start = assigned -> in_work start.operations = set_owner_to_self start.permissions = TICKET_MODIFY stop = in_work -> assigned stop.permissions = TICKET_MODIFY ; reopen actions reopen = closed -> new reopen.operations = del_resolution reopen.permissions = TICKET_CREATE ; request info actions ; For tickets with an owner, they go back to assigned request_info = assigned,in_work,in_QA -> infoneeded request_info.permissions = TICKET_MODIFY provide_info = infoneeded -> assigned provide_info.permissions = TICKET_MODIFY provide_info.default = 2 ; But tickets without an owner go back to new. request_info_new = new -> infoneeded_new request_info_new.label = request info request_info_new.permissions = TICKET_MODIFY provide_info_new = infoneeded_new -> new provide_info_new.label = provide info provide_info_new.permissions = TICKET_MODIFY provide_info_new.default = 2 Trac-1.2/contrib/workflow/showworkflow0000755000175500017550000000265113007223745020021 0ustar debacledebacle#!/bin/bash -x # -*- coding: utf-8 -*- # # Copyright (C) 2007-2013 Edgewall Software # Copyright (C) 2007 Eli Carter # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. basedir=`dirname $0` options="" while [ $# -gt 1 ]; do options="$options $1" shift done config="$1" if [ ! -e "$config" ]; then echo "Syntax error: requires a .ini file to work with." >&2 exit 1 fi dot=`echo "$config" | sed 's/\.ini$/.dot/g'` ps=`echo "$config" | sed 's/\.ini$/.ps/g'` pdf=`echo "$config" | sed 's/\.ini$/.pdf/g'` png=`echo "$config" | sed 's/\.ini$/.png/g'` $basedir/workflow_parser.py $options "$config" "$dot" if [ $? -ne 0 ]; then echo "Failed to parse \"$config\", exiting." >&2 exit 1 fi cat "$dot" if [ "$OSTYPE" = cygwin ]; then dot -T png -o "$png" "$dot" cmd /c start $png else dot -T ps -o "$ps" "$dot" dot -T pdf -o "$pdf" "$dot" # attempt to find a Linux pdf viewer for viewer in kpdf okular evince; do if which $viewer >/dev/null 2>&1; then break fi done $viewer "$pdf" fi Trac-1.2/contrib/workflow/README0000644000175500017550000000030413007223745016171 0ustar debacledebacleThere are two more workflows available, original-workflow and basic-workflow. Look in trac/ticket/workflows for them. (They are used internally by Trac, so they aren't here in the contrib tree.) Trac-1.2/contrib/workflow/simple-workflow.ini0000644000175500017550000000140313007223745021154 0ustar debacledebacle[ticket-workflow] ; simple-workflow.ini ; This is action-centric leave = * -> * leave.operations = leave_status leave.default = 1 create = -> new resolve_new = new -> closed resolve_new.label = resolve resolve_new.permissions = TICKET_MODIFY resolve_new.operations = set_owner_to_self,set_resolution accept = new,accepted -> accepted accept.permissions = TICKET_MODIFY accept.operations = set_owner_to_self resolve_accepted = accepted -> closed resolve_accepted.label = resolve resolve_accepted.permissions = TICKET_MODIFY resolve_accepted.operations = set_resolution unaccept = accepted -> new unaccept.permissions = TICKET_MODIFY unaccept.operations = del_owner reopen = closed -> new reopen.permissions = TICKET_CREATE reopen.operations = del_resolution Trac-1.2/contrib/workflow/opensource-workflow.ini0000644000175500017550000000375313007223745022057 0ustar debacledebacle[ticket-workflow] ; opensource-workflow.ini ; create action create = -> new ; accept action ; When you accept a ticket, you get ownership of it. (You can't accept a ; ticket on someone else's behalf.) accept = new,assigned,accepted,started -> accepted accept.operations = set_owner_to_self accept.permissions = TICKET_MODIFY ; assign, reassign, unassign actions assign = new -> assigned assign.operations = set_owner assign.permissions = TICKET_MODIFY reassign = assigned,accepted,started -> assigned reassign.operations = set_owner reassign.permissions = TICKET_MODIFY ; Allow correcting the ownership of a closed ticket. change_owner = closed -> closed change_owner.label = change ownership change_owner.operations = set_owner change_owner.permissions = TICKET_MODIFY unassign = assigned,accepted,started -> new unassign.operations = del_owner unassign.permissions = TICKET_MODIFY ; leave actions leave = * -> * leave.operations = leave_status leave.default = 1 ; resolve actions resolve = new,assigned,accepted,started -> closed resolve.operations = set_resolution resolve.permissions = TICKET_MODIFY ; start/stop actions start = accepted,assigned -> started start.operations = set_owner_to_self start.permissions = TICKET_MODIFY stop = started -> assigned stop.permissions = TICKET_MODIFY ; reopen actions reopen = closed -> new reopen.operations = del_resolution reopen.permissions = TICKET_CREATE ; request info actions ; For tickets with an owner, they go back to assigned request_info = assigned,accepted,started -> infoneeded request_info.permissions = TICKET_MODIFY provide_info = infoneeded -> assigned provide_info.permissions = TICKET_MODIFY provide_info.default = 2 ; But tickets without an owner go back to new. request_info_new = new -> infoneeded_new request_info_new.label = request info request_info_new.permissions = TICKET_MODIFY provide_info_new = infoneeded_new -> new provide_info_new.label = provide info provide_info_new.permissions = TICKET_MODIFY provide_info_new.default = 2 Trac-1.2/contrib/workflow/migrate_original_to_basic.py0000755000175500017550000000266013007223745023054 0ustar debacledebacle#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright (C) 2007-2013 Edgewall Software # Copyright (C) 2007 Eli Carter # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. import sys import trac.env from trac.ticket.default_workflow import load_workflow_config_snippet def main(): """Rewrite the ticket-workflow section of the config; and change all 'assigned' tickets to 'accepted'. """ if len(sys.argv) != 2: print("Usage: %s path_to_trac_environment" % sys.argv[0]) sys.exit(1) tracdir = sys.argv[1] trac_env = trac.env.open_environment(tracdir) # Update the config... old_workflow = trac_env.config.options('ticket-workflow') for name, value in old_workflow: trac_env.config.remove('ticket-workflow', name) load_workflow_config_snippet(trac_env.config, 'basic-workflow.ini') trac_env.config.save() # Update the ticket statuses... trac_env.db_transaction(""" UPDATE ticket SET status = 'accepted' WHERE status = 'assigned' """) if __name__ == '__main__': main() Trac-1.2/contrib/workflow/trivial-workflow.ini0000644000175500017550000000050213007223745021334 0ustar debacledebacle[ticket-workflow] ; trivial-workflow.ini create = -> new resolve = new -> closed resolve.permissions = TICKET_MODIFY resolve.operations = set_resolution reopen = closed -> new reopen.permissions = TICKET_CREATE reopen.operations = del_resolution leave = * -> * leave.operations = leave_status leave.default = 1 Trac-1.2/contrib/wiki2rst.py0000755000175500017550000000727413007223745015607 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- import codecs import os.path import re import sys from cStringIO import StringIO from pkg_resources import resource_listdir, resource_string from trac.loader import load_components from trac.test import EnvironmentStub, Mock, MockPerm from trac.util.text import printerr, printout from trac.web.chrome import web_context from trac.web.href import Href from trac.wiki.formatter import format_to_html from trac.wiki.model import WikiPage try: import html2rest except ImportError: printerr("The html2rest package must be installed.") sys.exit(1) class Parser(html2rest.Parser): def __init__(self, writer=sys.stdout, encoding='utf8', relroot=None, relpath=None): html2rest.Parser.__init__(self, writer, encoding, relroot, relpath) self.links = {} def end_a(self): if '#pending' in self.hrefs: href = self.hrefs['#pending'] label = self.hrefs[href] key = label.lower() if key not in self.links: self.links[key] = (label, href) elif href != self.links[key][1]: alt = label while True: alt += '*' if alt not in self.links: break continue self.data(alt[len(label):]) self.hrefs[href] = alt self.links[alt] = (alt, href) self.data('`_') del self.hrefs['#pending'] def end_body(self): self.end_p() for label, href in self.links.itervalues(): if href[0] != '#': self.writeline('.. _%s: %s' % (label, href)) self.end_p() def wiki2rest(env, context, wiki): text = re.sub('\r?\n', '\n', wiki.text) text = re.sub(r'\[\[TracGuideToc\]\]\r?\n?', '', text) html = format_to_html(env, context, text) html = html.replace(u'\u200b', '') html = re.sub(r'\s*([^<]*?)\s*', r'\1', html) html = '%s' % html writer = StringIO() parser = Parser(writer, 'utf-8', None, None) parser.feed(html) parser.close() rst = writer.getvalue().strip('\n') rst = re.sub('\n{4,}', '\n\n\n', rst) # sort links rst = re.sub(r'(?:\n\.\. _[^\n]*)+\Z', lambda m: '\n'.join(sorted(m.group(0).split('\n'), key=lambda v: v.lower())), rst) if any(ord(c) > 0x7f for c in rst): # Trac detects utf-8 using BOM rst = '%s.. charset=utf-8\n\n%s' % (codecs.BOM_UTF8, rst) return rst + '\n' def main(): names = sorted(name for name in resource_listdir('trac.wiki', 'default-pages') if not name.startswith('.')) env = EnvironmentStub() load_components(env) with env.db_transaction: for name in names: wiki = WikiPage(env, name) wiki.text = resource_string('trac.wiki', 'default-pages/' + name).decode('utf-8') if wiki.text: wiki.save('trac', '') else: printout('%s: Skipped empty page' % name) req = Mock(href=Href('/'), abs_href=Href('http://trac.edgewall.org/'), perm=MockPerm(), chrome={}) for name in sys.argv[1:]: name = os.path.basename(name) wiki = WikiPage(env, name) if not wiki.exists: continue context = web_context(req, wiki.resource, absurls=True) rst = wiki2rest(env, context, wiki) sys.stdout.write(rst) if __name__ == '__main__': main() Trac-1.2/contrib/l10n_revert_lineno_conflicts.py0000755000175500017550000000422513007223745021573 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2013 Edgewall Software # Copyright (C) 2013 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. """ L10N tool which takes a list of .po in conflicted state and revert ignorable changes. It resolve the conflicts for which "theirs" changes consist solely of line number changes, by reverting to the working copy content. This makes it easier to merge translation .po files across branches. """ import re ignore_lineno_re = re.compile(r''' <<<< .* \n ( (?: [^=] .* \n )+ ) # \1 == "working copy" ==== .* \n ( (?: \# .* \n )+ ) # \2 == comment only for "theirs" >>>> .* \n ''', re.MULTILINE | re.VERBOSE) HEADERS = ''' Project-Id-Version Report-Msgid-Bugs-To POT-Creation-Date PO-Revision-Date Last-Translator Language-Team Plural-Forms MIME-Version Content-Type Content-Transfer-Encoding Generated-By '''.split() po_headers_re = re.compile(r''' <<<< .* \n ( (?: "(?:%(header)s): \s [^"]+" \n )+ ) # \1 == "working copy" ==== .* \n ( (?: "(?:%(header)s): \s [^"]+" \n )+ ) # \2 == another date for "theirs" >>>> .* \n ''' % dict(header='|'.join(HEADERS)), re. MULTILINE | re.VERBOSE) def sanitize_file(path): with open(path, 'r+') as f: sanitized, nsub = ignore_lineno_re.subn(r'\1', f.read()) sanitized, nsub2 = po_headers_re.subn(r'\1', sanitized) nsub += nsub2 if nsub: print("reverted %d ignorable changes in %s" % (nsub, path)) f.seek(0) f.write(sanitized) f.truncate() else: print("no ignorable changes in %s" % (path,)) if __name__ == '__main__': import sys for path in sys.argv[1:]: sanitize_file(path) Trac-1.2/contrib/checkwiki.py0000755000175500017550000001454313007223745015767 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2015 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. import os import sys from pkg_resources import resource_listdir, resource_string from trac.loader import load_components from trac.test import EnvironmentStub, Mock, MockPerm from trac.util.text import printout from trac.web.chrome import web_context from trac.web.href import Href from trac.wiki.formatter import Formatter from trac.wiki.model import WikiPage TURN_ON = '\033[30m\033[41m' TURN_OFF = '\033[m' class DefaultWikiChecker(Formatter): def __init__(self, env, context, name): Formatter.__init__(self, env, context) self.__name = name self.__marks = [] self.__super = super(DefaultWikiChecker, self) def handle_match(self, fullmatch): rv = self.__super.handle_match(fullmatch) if rv: if not isinstance(rv, basestring): text = unicode(rv) else: text = rv if text.startswith('') and \ 'class="missing ' in text: self.__marks.append((fullmatch.start(0), fullmatch.end(0))) return rv def handle_code_block(self, line, startmatch=None): prev_processor = getattr(self, 'code_processor', None) try: return self.__super.handle_code_block(line, startmatch) finally: processor = self.code_processor if startmatch and processor and processor != prev_processor and \ processor.error: self.__marks.append((startmatch.start(0), startmatch.end(0))) def format(self, text, out=None): return self.__super.format(SourceWrapper(self, text), out) def next_callback(self, line, idx): marks = self.__marks if marks: buf = [] prev = 0 for start, end in self.__marks: buf.append(line[prev:start]) buf.append(TURN_ON) buf.append(line[start:end]) buf.append(TURN_OFF) prev = end buf.append(line[prev:]) printout('%s:%d:%s' % (self.__name, idx + 1, ''.join(buf))) self.__marks[:] = () class SourceWrapper(object): def __init__(self, formatter, text): self.formatter = formatter self.text = text def __iter__(self): return LinesIterator(self.formatter, self.text.splitlines()) class LinesIterator(object): def __init__(self, formatter, lines): self.formatter = formatter self.lines = lines self.idx = 0 self.current = None def next(self): idx = self.idx if self.current is not None: self.formatter.next_callback(self.current, idx) if idx >= len(self.lines): self.current = None raise StopIteration self.idx = idx + 1 self.current = self.lines[idx] return self.current class DummyIO(object): def write(self, data): pass def parse_args(): from optparse import OptionParser parser = OptionParser(usage='Usage: %prog [options] [PAGES...]') parser.add_option('-d', '--download', dest='download', default=False, action='store_true', help='Download default pages from trac.edgewall.org ' 'before checking') parser.add_option('-p', '--prefix', dest='prefix', default='', help='Prepend "prefix/" to the page when downloading') return parser.parse_args() def download_default_pages(names, prefix): from httplib import HTTPSConnection host = 'trac.edgewall.org' if prefix and not prefix.endswith('/'): prefix += '/' conn = HTTPSConnection(host) for name in names: if name in ('WikiStart', 'SandBox'): continue sys.stdout.write('Downloading %s%s' % (prefix, name)) conn.request('GET', '/wiki/%s%s?format=txt' % (prefix, name)) response = conn.getresponse() content = response.read() if prefix and (response.status != 200 or not content): sys.stdout.write(' %s' % name) conn.request('GET', '/wiki/%s?format=txt' % name) response = conn.getresponse() content = response.read() if response.status == 200 and content: with open('trac/wiki/default-pages/' + name, 'w') as f: lines = content.replace('\r\n', '\n').splitlines(True) f.write(''.join(line for line in lines if line.strip() != '[[TranslatedPages]]')) sys.stdout.write('\tdone.\n') else: sys.stdout.write('\tmissing or empty.\n') conn.close() def main(): options, args = parse_args() names = sorted(name for name in resource_listdir('trac.wiki', 'default-pages') if not name.startswith('.')) if args: args = sorted(set(names) & set(map(os.path.basename, args))) else: args = names if options.download: download_default_pages(args, options.prefix) env = EnvironmentStub(disable=['trac.mimeview.pygments.*']) load_components(env) with env.db_transaction: for name in names: wiki = WikiPage(env, name) wiki.text = resource_string('trac.wiki', 'default-pages/' + name).decode('utf-8') if wiki.text: wiki.save('trac', '') else: printout('%s: Skipped empty page' % name) req = Mock(href=Href('/'), abs_href=Href('http://localhost/'), perm=MockPerm()) for name in args: wiki = WikiPage(env, name) if not wiki.exists: continue context = web_context(req, wiki.resource) out = DummyIO() DefaultWikiChecker(env, context, name).format(wiki.text, out) if __name__ == '__main__': main() Trac-1.2/contrib/migrateticketmodel.py0000755000175500017550000000340113007223745017672 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2005-2013 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. # This script completely migrates a <= 0.8.x Trac environment to use the new # default ticket model introduced in Trac 0.9. # # In particular, this means that the severity field is removed (or rather # disabled by removing all possible values), and the priority values are # changed to the more meaningful new defaults. # # Make sure to make a backup of the Trac environment before running this! from __future__ import print_function import os import sys from trac.env import open_environment from trac.ticket.model import Priority, Severity priority_mapping = { 'highest': 'blocker', 'high': 'critical', 'normal': 'major', 'low': 'minor', 'lowest': 'trivial' } def main(): if len(sys.argv) < 2: print("usage: %s /path/to/projenv" % os.path.basename(sys.argv[0]), file=sys.stderr) sys.exit(2) env = open_environment(sys.argv[1]) with env.db_transaction: for oldprio, newprio in priority_mapping.items(): priority = Priority(env, oldprio) priority.name = newprio priority.update() for severity in list(Severity.select(env)): severity.delete() if __name__ == '__main__': main() Trac-1.2/contrib/htpasswd.py0000755000175500017550000001070113007223745015653 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2008-2013 Edgewall Software # Copyright (C) 2008 Eli Carter # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. import getpass import optparse import sys from trac.util import salt from trac.util.compat import crypt, wait_for_file_mtime_change from trac.util.text import printerr, printout if crypt is None: printerr("The crypt module is not found. Install the passlib package " "from PyPI.", newline=True) sys.exit(1) class HtpasswdFile(object): """A class for manipulating htpasswd files.""" def __init__(self, filename, create=False): self.entries = [] self.filename = filename if not create: self.load() def load(self): """Read the htpasswd file into memory.""" self.entries = [] with open(self.filename, 'r') as f: for line in f: username, pwhash = line.split(':') entry = [username, pwhash.rstrip()] self.entries.append(entry) def save(self): """Write the htpasswd file to disk""" wait_for_file_mtime_change(self.filename) with open(self.filename, 'w') as f: f.writelines("%s:%s\n" % (entry[0], entry[1]) for entry in self.entries) def update(self, username, password): """Replace the entry for the given user, or add it if new.""" pwhash = crypt(password, salt()) matching_entries = [entry for entry in self.entries if entry[0] == username] if matching_entries: matching_entries[0][1] = pwhash else: self.entries.append([username, pwhash]) def delete(self, username): """Remove the entry for the given user.""" self.entries = [entry for entry in self.entries if entry[0] != username] def main(): """ %prog [-c] filename username %prog -b[c] filename username password %prog -D filename username """ # For now, we only care about the use cases that affect tests/functional.py parser = optparse.OptionParser(usage=main.__doc__) parser.add_option('-b', action='store_true', dest='batch', default=False, help='Batch mode; password is passed on the command line IN THE CLEAR.' ) parser.add_option('-c', action='store_true', dest='create', default=False, help='Create a new htpasswd file, overwriting any existing file.') parser.add_option('-D', action='store_true', dest='delete_user', default=False, help='Remove the given user from the password file.') if len(sys.argv) <= 1: parser.print_help() sys.exit(1) options, args = parser.parse_args() def syntax_error(msg): """Utility function for displaying fatal error messages with usage help. """ printerr("Syntax error: " + msg, newline=True) printerr(parser.format_help(), newline=True) sys.exit(1) # Non-option arguments if len(args) < 2: syntax_error("Insufficient number of arguments.\n") filename, username = args[:2] password = None if options.delete_user: if len(args) != 2: syntax_error("Incorrect number of arguments.\n") else: if len(args) == 3 and options.batch: password = args[2] elif len(args) == 2 and not options.batch: first = getpass.getpass("New password:") second = getpass.getpass("Re-type new password:") if first == second: password = first else: printout("htpasswd: password verification error") return else: syntax_error("Incorrect number of arguments.\n") try: passwdfile = HtpasswdFile(filename, create=options.create) except IOError: syntax_error("File not found.\n") else: if options.delete_user: passwdfile.delete(username) else: passwdfile.update(username, password) passwdfile.save() if __name__ == '__main__': main() Trac-1.2/contrib/trac-pre-commit-hook0000755000175500017550000000460113007223745017332 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2004-2013 Edgewall Software # Copyright (C) 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. # # This script will enforce the following policy: # # "A checkin must reference an open ticket." # # This script should be invoked from the subversion pre-commit hook like this: # # REPOS="$1" # TXN="$2" # TRAC_ENV="/somewhere/trac/project/" # LOG=`/usr/bin/svnlook log -t "$TXN" "$REPOS"` # /usr/bin/python /some/path/trac-pre-commit-hook "$TRAC_ENV" "$LOG" || exit 1 # from __future__ import print_function import os import re import sys if not 'PYTHON_EGG_CACHE' in os.environ: os.environ['PYTHON_EGG_CACHE'] = os.path.join(sys.argv[1], '.egg-cache') from trac.env import open_environment def main(): if len(sys.argv) != 3: print("Usage: %s " % sys.argv[0], file=sys.stderr) sys.exit(1) env_path = sys.argv[1] log = sys.argv[2] tickets = [] for tmp in re.findall('(?:close|closed|closes|fix|fixed|fixes|addresses|references|refs|re|see)' '.?(#[0-9]+(?:(?:[, &]+| *and *)#[0-9]+)*)', log.lower()): tickets += re.findall('#([0-9]+)', tmp) # At least one ticket has to be mentioned in the log message if tickets == []: print("At least one open ticket must be mentioned in the log " "message.", file=sys.stderr) sys.exit(1) env = open_environment(env_path) db = env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT COUNT(id) FROM ticket WHERE " "status <> 'closed' AND id IN (%s)" % ','.join(tickets)) row = cursor.fetchone() # At least one of the tickets mentioned in the log messages has to # be open if not row or row[0] < 1: print("At least one open ticket must be mentioned in the log " "message.", file=sys.stderr) sys.exit(1) else: sys.exit(0) if __name__ == '__main__': main() Trac-1.2/contrib/bugzilla2trac.py0000755000175500017550000010756513007223745016602 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2004-2013 Edgewall Software # Copyright (C) 2004 Dmitry Yusupov # Copyright (C) 2004 Mark Rowe # Copyright (C) 2005 Bill Soudan # Copyright (C) 2005 Florent Guillaume # Copyright (C) 2005 Jeroen Ruigrok van der Werven # Copyright (C) 2010 Jeff Moreland # # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. """ Import a Bugzilla items into a Trac database. Requires: Trac 0.9b1 from http://trac.edgewall.org/ Python 2.3 from http://www.python.org/ MySQL >= 3.23 from http://www.mysql.org/ or PostGreSQL 8.4 from http://www.postgresql.org/ or SQLite 3 from http://www.sqlite.org/ $Id: bugzilla2trac.py 14963 2016-07-11 20:06:47Z rjollos $ """ import re ### ### Conversion Settings -- edit these before running if desired ### # Bugzilla version. You can find this in Bugzilla's globals.pl file. # # Currently, the following bugzilla versions are known to work: # 2.11 (2110), 2.16.5 (2165), 2.16.7 (2167), 2.18.3 (2183), 2.19.1 (2191), # 2.23.3 (2233), 3.04.4 (3044) # # If you run this script on a version not listed here and it is successful, # please file a ticket at http://trac.edgewall.org # BZ_VERSION = 3044 # MySQL connection parameters for the Bugzilla database. These can also # be specified on the command line. BZ_DB = "" BZ_HOST = "" BZ_USER = "" BZ_PASSWORD = "" # Path to the Trac environment. TRAC_ENV = "/usr/local/trac" # If true, all existing Trac tickets and attachments will be removed # prior to import. TRAC_CLEAN = True # Enclose imported ticket description and comments in a {{{ }}} # preformat block? This formats the text in a fixed-point font. PREFORMAT_COMMENTS = False # Replace bug numbers in comments with #xyz REPLACE_BUG_NO = False # Severities SEVERITIES = [ ("blocker", "1"), ("critical", "2"), ("major", "3"), ("normal", "4"), ("minor", "5"), ("trivial", "6") ] # Priorities # If using the default Bugzilla priorities of P1 - P5, do not change anything # here. # If you have other priorities defined please change the P1 - P5 mapping to # the order you want. You can also collapse multiple priorities on bugzilla's # side into the same priority on Trac's side, simply adjust PRIORITIES_MAP. PRIORITIES = [ ("highest", "1"), ("high", "2"), ("normal", "3"), ("low", "4"), ("lowest", "5") ] # Bugzilla: Trac # NOTE: Use lowercase. PRIORITIES_MAP = { "p1": "highest", "p2": "high", "p3": "normal", "p4": "low", "p5": "lowest" } # By default, all bugs are imported from Bugzilla. If you add a list # of products here, only bugs from those products will be imported. PRODUCTS = [] # These Bugzilla products will be ignored during import. IGNORE_PRODUCTS = [] # These milestones are ignored IGNORE_MILESTONES = ["---"] # Don't import user names and passwords into htpassword if # user is disabled in bugzilla? (i.e. profiles.DisabledText<>'') IGNORE_DISABLED_USERS = True # These logins are converted to these user ids LOGIN_MAP = { #'some.user@example.com': 'someuser', } # These emails are removed from CC list IGNORE_CC = [ #'loser@example.com', ] # The 'component' field in Trac can come either from the Product or # or from the Component field of Bugzilla. COMPONENTS_FROM_PRODUCTS # switches the behavior. # If COMPONENTS_FROM_PRODUCTS is True: # - Bugzilla Product -> Trac Component # - Bugzilla Component -> Trac Keyword # IF COMPONENTS_FROM_PRODUCTS is False: # - Bugzilla Product -> Trac Keyword # - Bugzilla Component -> Trac Component COMPONENTS_FROM_PRODUCTS = False # If COMPONENTS_FROM_PRODUCTS is True, the default owner for each # Trac component is inferred from a default Bugzilla component. DEFAULT_COMPONENTS = ["default", "misc", "main"] # This mapping can assign keywords in the ticket entry to represent # products or components (depending on COMPONENTS_FROM_PRODUCTS). # The keyword will be ignored if empty. KEYWORDS_MAPPING = { #'Bugzilla_product_or_component': 'Keyword', "default": "", "misc": "", } # If this is True, products or components are all set as keywords # even if not mentionned in KEYWORDS_MAPPING. MAP_ALL_KEYWORDS = True # Custom field mappings CUSTOMFIELD_MAP = { #'Bugzilla_field_name': 'Trac_customfield_name', #'op_sys': 'os', #'cf_featurewantedby': 'wanted_by', #'product': 'product' } # Bug comments that should not be imported. Each entry in list should # be a regular expression. IGNORE_COMMENTS = [ "^Created an attachment \(id=" ] ########################################################################### ### You probably don't need to change any configuration past this line. ### ########################################################################### # Bugzilla status to Trac status translation map. # # NOTE: bug activity is translated as well, which may cause bug # activity to be deleted (e.g. resolved -> closed in Bugzilla # would translate into closed -> closed in Trac, so we just ignore the # change). # # There is some special magic for open in the code: if there is no # Bugzilla owner, open is mapped to 'new' instead. STATUS_TRANSLATE = { "unconfirmed": "new", "open": "assigned", "resolved": "closed", "verified": "closed", "released": "closed" } # Translate Bugzilla statuses into Trac keywords. This provides a way # to retain the Bugzilla statuses in Trac. e.g. when a bug is marked # 'verified' in Bugzilla it will be assigned a VERIFIED keyword. STATUS_KEYWORDS = { "verified": "VERIFIED", "released": "RELEASED" } # Some fields in Bugzilla do not have equivalents in Trac. Changes in # fields listed here will not be imported into the ticket change history, # otherwise you'd see changes for fields that don't exist in Trac. IGNORED_ACTIVITY_FIELDS = ["everconfirmed"] # Regular expression and its replacement # this expression will update references to bugs 1 - 99999 that # have the form "bug 1" or "bug #1" BUG_NO_RE = re.compile(r"\b(bug #?)([0-9]{1,5})\b", re.I) BUG_NO_REPL = r"#\2" ### ### Script begins here ### import os import sys import string import StringIO import MySQLdb import MySQLdb.cursors from trac.attachment import Attachment from trac.env import Environment if not hasattr(sys, 'setdefaultencoding'): reload(sys) sys.setdefaultencoding('latin1') # simulated Attachment class for trac.add #class Attachment: # def __init__(self, name, data): # self.filename = name # self.file = StringIO.StringIO(data.tostring()) # simple field translation mapping. if string not in # mapping, just return string, otherwise return value class FieldTranslator(dict): def __getitem__(self, item): if item not in self: return item return dict.__getitem__(self, item) statusXlator = FieldTranslator(STATUS_TRANSLATE) class TracDatabase(object): def __init__(self, path): self.env = Environment(path) self.loginNameCache = {} self.fieldNameCache = {} from trac.db.api import DatabaseManager self.using_postgres = \ DatabaseManager(self.env).connection_uri.startswith("postgres:") def hasTickets(self): return int(self.env.db_query("SELECT count(*) FROM ticket")[0][0] > 0) def assertNoTickets(self): if self.hasTickets(): raise Exception("Will not modify database with existing tickets!") def setSeverityList(self, s): """Remove all severities, set them to `s`""" self.assertNoTickets() with self.env.db_transaction as db: db("DELETE FROM enum WHERE type='severity'") for value, i in s: print(" inserting severity '%s' - '%s'" % (value, i)) db("""INSERT INTO enum (type, name, value) VALUES (%s, %s, %s)""", ("severity", value, i)) def setPriorityList(self, s): """Remove all priorities, set them to `s`""" self.assertNoTickets() with self.env.db_transaction as db: db("DELETE FROM enum WHERE type='priority'") for value, i in s: print(" inserting priority '%s' - '%s'" % (value, i)) db("INSERT INTO enum (type, name, value) VALUES (%s, %s, %s)", ("priority", value, i)) def setComponentList(self, l, key): """Remove all components, set them to `l`""" self.assertNoTickets() with self.env.db_transaction as db: db("DELETE FROM component") for comp in l: print(" inserting component '%s', owner '%s'" % (comp[key], comp['owner'])) db("INSERT INTO component (name, owner) VALUES (%s, %s)", (comp[key], comp['owner'])) def setVersionList(self, v, key): """Remove all versions, set them to `v`""" self.assertNoTickets() with self.env.db_transaction as db: db("DELETE FROM version") for vers in v: print(" inserting version '%s'" % vers[key]) db("INSERT INTO version (name) VALUES (%s)", (vers[key],)) def setMilestoneList(self, m, key): """Remove all milestones, set them to `m`""" self.assertNoTickets() with self.env.db_transaction as db: db("DELETE FROM milestone") for ms in m: milestone = ms[key] print(" inserting milestone '%s'" % milestone) db("INSERT INTO milestone (name) VALUES (%s)", (milestone,)) def addTicket(self, id, time, changetime, component, severity, priority, owner, reporter, cc, version, milestone, status, resolution, summary, description, keywords, customfields): desc = description type = "defect" if SEVERITIES: if severity.lower() == "enhancement": severity = "minor" type = "enhancement" else: if priority.lower() == "enhancement": priority = "minor" type = "enhancement" if PREFORMAT_COMMENTS: desc = '{{{\n%s\n}}}' % desc if REPLACE_BUG_NO: if BUG_NO_RE.search(desc): desc = re.sub(BUG_NO_RE, BUG_NO_REPL, desc) if priority in PRIORITIES_MAP: priority = PRIORITIES_MAP[priority] print(" inserting ticket %s -- %s" % (id, summary)) with self.env.db_transaction as db: db("""INSERT INTO ticket (id, type, time, changetime, component, severity, priority, owner, reporter, cc, version, milestone, status, resolution, summary, description, keywords) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """, (id, type, datetime2epoch(time), datetime2epoch(changetime), component, severity, priority, owner, reporter, cc, version, milestone, status.lower(), resolution, summary, desc, keywords)) if self.using_postgres: with self.env.db_transaction as db: c = db.cursor() c.execute(""" SELECT SETVAL('ticket_id_seq', MAX(id)) FROM ticket; SELECT SETVAL('report_id_seq', MAX(id)) FROM report""") ticket_id = db.get_last_id(c, 'ticket') # add all custom fields to ticket for name, value in customfields.iteritems(): self.addTicketCustomField(ticket_id, name, value) return ticket_id def addTicketCustomField(self, ticket_id, field_name, field_value): if field_value == None: return self.env.db_transaction(""" INSERT INTO ticket_custom (ticket, name, value) VALUES (%s, %s, %s) """, (ticket_id, field_name, field_value)) def addTicketComment(self, ticket, time, author, value): comment = value if PREFORMAT_COMMENTS: comment = '{{{\n%s\n}}}' % comment if REPLACE_BUG_NO: if BUG_NO_RE.search(comment): comment = re.sub(BUG_NO_RE, BUG_NO_REPL, comment) with self.env.db_transaction as db: db("""INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue) VALUES (%s, %s, %s, %s, %s, %s) """, (ticket, datetime2epoch(time), author, 'comment', '', comment)) def addTicketChange(self, ticket, time, author, field, oldvalue, newvalue): if field == "owner": if oldvalue in LOGIN_MAP: oldvalue = LOGIN_MAP[oldvalue] if newvalue in LOGIN_MAP: newvalue = LOGIN_MAP[newvalue] if field == "priority": if oldvalue.lower() in PRIORITIES_MAP: oldvalue = PRIORITIES_MAP[oldvalue.lower()] if newvalue.lower() in PRIORITIES_MAP: newvalue = PRIORITIES_MAP[newvalue.lower()] # Doesn't make sense if we go from highest -> highest, for example. if oldvalue == newvalue: return with self.env.db_transaction as db: db("""INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue) VALUES (%s, %s, %s, %s, %s, %s) """, (ticket, datetime2epoch(time), author, field, oldvalue, newvalue)) def addAttachment(self, author, a): if a['filename'] != '': description = a['description'] id = a['bug_id'] filename = a['filename'] filedata = StringIO.StringIO(a['thedata']) filesize = len(filedata.getvalue()) time = a['creation_ts'] print(" ->inserting attachment '%s' for ticket %s -- %s" % (filename, id, description)) attachment = Attachment(self.env, 'ticket', id) attachment.author = author attachment.description = description attachment.insert(filename, filedata, filesize, datetime2epoch(time)) del attachment def getLoginName(self, cursor, userid): if userid not in self.loginNameCache: cursor.execute("SELECT * FROM profiles WHERE userid = %s", (userid)) loginName = cursor.fetchall() if loginName: loginName = loginName[0]['login_name'] else: print("WARNING: unknown bugzilla userid %d, recording as" " anonymous" % userid) loginName = "anonymous" loginName = LOGIN_MAP.get(loginName, loginName) self.loginNameCache[userid] = loginName return self.loginNameCache[userid] def getFieldName(self, cursor, fieldid): if fieldid not in self.fieldNameCache: # fielddefs.fieldid got changed to fielddefs.id in Bugzilla # 2.23.3. if BZ_VERSION >= 2233: cursor.execute("SELECT * FROM fielddefs WHERE id = %s", (fieldid)) else: cursor.execute("SELECT * FROM fielddefs WHERE fieldid = %s", (fieldid)) fieldName = cursor.fetchall() if fieldName: fieldName = fieldName[0]['name'].lower() else: print("WARNING: unknown bugzilla fieldid %d, " " recording as unknown" % userid) fieldName = "unknown" self.fieldNameCache[fieldid] = fieldName return self.fieldNameCache[fieldid] def makeWhereClause(fieldName, values, negative=False): if not values: return '' if negative: connector, op = ' AND ', '!=' else: connector, op = ' OR ', '=' clause = connector.join(["%s %s '%s'" % (fieldName, op, value) for value in values]) return ' (' + clause + ')' def convert(_db, _host, _user, _password, _env, _force): activityFields = FieldTranslator() # account for older versions of bugzilla print("Using Bugzilla v%s schema." % BZ_VERSION) if BZ_VERSION == 2110: activityFields['removed'] = "oldvalue" activityFields['added'] = "newvalue" # init Bugzilla environment print("Bugzilla MySQL('%s':'%s':'%s':'%s'): connecting..." % (_db, _host, _user, ("*" * len(_password)))) mysql_con = MySQLdb.connect(host=_host, user=_user, passwd=_password, db=_db, compress=1, cursorclass=MySQLdb.cursors.DictCursor, charset='utf8') mysql_cur = mysql_con.cursor() # init Trac environment print("Trac SQLite('%s'): connecting..." % _env) trac = TracDatabase(_env) # force mode... if _force == 1: print("\nCleaning all tickets...") with trac.env.db_transaction as db: db("DELETE FROM ticket_change") db("DELETE FROM ticket") db("DELETE FROM ticket_custom") db("DELETE FROM attachment") attachments_dir = os.path.join(os.path.normpath(trac.env.path), "attachments") # Straight from the Python documentation. for root, dirs, files in os.walk(attachments_dir, topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) if not os.stat(attachments_dir): os.mkdir(attachments_dir) print("All tickets cleaned...") print("\n0. Filtering products...") if BZ_VERSION >= 2180: mysql_cur.execute("SELECT name FROM products") else: mysql_cur.execute("SELECT product AS name FROM products") products = [] for line in mysql_cur.fetchall(): product = line['name'] if PRODUCTS and product not in PRODUCTS: continue if product in IGNORE_PRODUCTS: continue products.append(product) PRODUCTS[:] = products print(" Using products", " ".join(PRODUCTS)) print("\n1. Import severities...") trac.setSeverityList(SEVERITIES) print("\n2. Import components...") if not COMPONENTS_FROM_PRODUCTS: if BZ_VERSION >= 2180: sql = """SELECT DISTINCT c.name AS name, c.initialowner AS owner FROM components AS c, products AS p WHERE c.product_id = p.id AND""" sql += makeWhereClause('p.name', PRODUCTS) else: sql = "SELECT value AS name, initialowner AS owner FROM components" sql += " WHERE" + makeWhereClause('program', PRODUCTS) mysql_cur.execute(sql) components = mysql_cur.fetchall() for component in components: component['owner'] = trac.getLoginName(mysql_cur, component['owner']) trac.setComponentList(components, 'name') else: if BZ_VERSION >= 2180: sql = ("SELECT p.name AS product, c.name AS comp, " " c.initialowner AS owner " "FROM components c, products p " "WHERE c.product_id = p.id AND" + makeWhereClause('p.name', PRODUCTS)) else: sql = ("SELECT program AS product, value AS comp, " " initialowner AS owner " "FROM components WHERE" + makeWhereClause('program', PRODUCTS)) mysql_cur.execute(sql) lines = mysql_cur.fetchall() all_components = {} # product -> components all_owners = {} # product, component -> owner for line in lines: product = line['product'] comp = line['comp'] owner = line['owner'] all_components.setdefault(product, []).append(comp) all_owners[(product, comp)] = owner component_list = [] for product, components in all_components.items(): # find best default owner default = None for comp in DEFAULT_COMPONENTS: if comp in components: default = comp break if default is None: default = components[0] owner = all_owners[(product, default)] owner_name = trac.getLoginName(mysql_cur, owner) component_list.append({'product': product, 'owner': owner_name}) trac.setComponentList(component_list, 'product') print("\n3. Import priorities...") trac.setPriorityList(PRIORITIES) print("\n4. Import versions...") if BZ_VERSION >= 2180: sql = """SELECT DISTINCTROW v.value AS value FROM products p, versions v""" sql += " WHERE v.product_id = p.id AND" sql += makeWhereClause('p.name', PRODUCTS) else: sql = "SELECT DISTINCTROW value FROM versions" sql += " WHERE" + makeWhereClause('program', PRODUCTS) mysql_cur.execute(sql) versions = mysql_cur.fetchall() trac.setVersionList(versions, 'value') print("\n5. Import milestones...") sql = "SELECT DISTINCT value FROM milestones" sql += " WHERE" + makeWhereClause('value', IGNORE_MILESTONES, negative=True) mysql_cur.execute(sql) milestones = mysql_cur.fetchall() trac.setMilestoneList(milestones, 'value') print("\n6. Retrieving bugs...") if BZ_VERSION >= 2180: sql = """SELECT DISTINCT b.*, c.name AS component, p.name AS product FROM bugs AS b, components AS c, products AS p """ sql += " WHERE" + makeWhereClause('p.name', PRODUCTS) sql += " AND b.product_id = p.id" sql += " AND b.component_id = c.id" sql += " ORDER BY b.bug_id" else: sql = """SELECT DISTINCT b.*, c.value AS component, p.product AS product FROM bugs AS b, components AS c, products AS p """ sql += " WHERE" + makeWhereClause('p.product', PRODUCTS) sql += " AND b.product = p.product" sql += " AND b.component = c.value" sql += " ORDER BY b.bug_id" mysql_cur.execute(sql) bugs = mysql_cur.fetchall() print("\n7. Import bugs and bug activity...") for bug in bugs: bugid = bug['bug_id'] ticket = {} keywords = [] ticket['id'] = bugid ticket['time'] = bug['creation_ts'] ticket['changetime'] = bug['delta_ts'] if COMPONENTS_FROM_PRODUCTS: ticket['component'] = bug['product'] else: ticket['component'] = bug['component'] if SEVERITIES: ticket['severity'] = bug['bug_severity'] ticket['priority'] = bug['priority'].lower() else: # use bugzilla severities as trac priorities, and ignore bugzilla # priorities ticket['severity'] = '' ticket['priority'] = bug['bug_severity'] ticket['owner'] = trac.getLoginName(mysql_cur, bug['assigned_to']) ticket['reporter'] = trac.getLoginName(mysql_cur, bug['reporter']) # pack bugzilla fields into dictionary of trac custom field # names and values customfields = {} for bugfield, customfield in CUSTOMFIELD_MAP.iteritems(): customfields[customfield] = bug[bugfield] ticket['customfields'] = customfields mysql_cur.execute("SELECT * FROM cc WHERE bug_id = %s", bugid) cc_records = mysql_cur.fetchall() cc_list = [] for cc in cc_records: cc_list.append(trac.getLoginName(mysql_cur, cc['who'])) cc_list = [cc for cc in cc_list if cc not in IGNORE_CC] ticket['cc'] = string.join(cc_list, ', ') ticket['version'] = bug['version'] target_milestone = bug['target_milestone'] if target_milestone in IGNORE_MILESTONES: target_milestone = '' ticket['milestone'] = target_milestone bug_status = bug['bug_status'].lower() ticket['status'] = statusXlator[bug_status] ticket['resolution'] = bug['resolution'].lower() # a bit of extra work to do open tickets if bug_status == 'open': if owner != '': ticket['status'] = 'assigned' else: ticket['status'] = 'new' ticket['summary'] = bug['short_desc'] mysql_cur.execute("SELECT * FROM longdescs WHERE bug_id = %s" % bugid) longdescs = list(mysql_cur.fetchall()) # check for empty 'longdescs[0]' field... if len(longdescs) == 0: ticket['description'] = '' else: ticket['description'] = longdescs[0]['thetext'] del longdescs[0] for desc in longdescs: ignore = False for comment in IGNORE_COMMENTS: if re.match(comment, desc['thetext']): ignore = True if ignore: continue trac.addTicketComment(ticket=bugid, time = desc['bug_when'], author=trac.getLoginName(mysql_cur, desc['who']), value = desc['thetext']) mysql_cur.execute("""SELECT * FROM bugs_activity WHERE bug_id = %s ORDER BY bug_when""" % bugid) bugs_activity = mysql_cur.fetchall() resolution = '' ticketChanges = [] keywords = [] for activity in bugs_activity: field_name = trac.getFieldName(mysql_cur, activity['fieldid']).lower() removed = activity[activityFields['removed']] added = activity[activityFields['added']] # statuses and resolutions are in lowercase in trac if field_name == "resolution" or field_name == "bug_status": removed = removed.lower() added = added.lower() # remember most recent resolution, we need this later if field_name == "resolution": resolution = added.lower() add_keywords = [] remove_keywords = [] # convert bugzilla field names... if field_name == "bug_severity": if SEVERITIES: field_name = "severity" else: field_name = "priority" elif field_name == "assigned_to": field_name = "owner" elif field_name == "bug_status": field_name = "status" if removed in STATUS_KEYWORDS: remove_keywords.append(STATUS_KEYWORDS[removed]) if added in STATUS_KEYWORDS: add_keywords.append(STATUS_KEYWORDS[added]) added = statusXlator[added] removed = statusXlator[removed] elif field_name == "short_desc": field_name = "summary" elif field_name == "product" and COMPONENTS_FROM_PRODUCTS: field_name = "component" elif ((field_name == "product" and not COMPONENTS_FROM_PRODUCTS) or (field_name == "component" and COMPONENTS_FROM_PRODUCTS)): if MAP_ALL_KEYWORDS or removed in KEYWORDS_MAPPING: kw = KEYWORDS_MAPPING.get(removed, removed) if kw: remove_keywords.append(kw) if MAP_ALL_KEYWORDS or added in KEYWORDS_MAPPING: kw = KEYWORDS_MAPPING.get(added, added) if kw: add_keywords.append(kw) if field_name == "component": # just keep the keyword change added = removed = "" elif field_name == "target_milestone": field_name = "milestone" if added in IGNORE_MILESTONES: added = "" if removed in IGNORE_MILESTONES: removed = "" ticketChange = {} ticketChange['ticket'] = bugid ticketChange['time'] = activity['bug_when'] ticketChange['author'] = trac.getLoginName(mysql_cur, activity['who']) ticketChange['field'] = field_name ticketChange['oldvalue'] = removed ticketChange['newvalue'] = added if add_keywords or remove_keywords: # ensure removed ones are in old old_keywords = keywords + [kw for kw in remove_keywords if kw not in keywords] # remove from new keywords = [kw for kw in keywords if kw not in remove_keywords] # add to new keywords += [kw for kw in add_keywords if kw not in keywords] if old_keywords != keywords: ticketChangeKw = ticketChange.copy() ticketChangeKw['field'] = "keywords" ticketChangeKw['oldvalue'] = ' '.join(old_keywords) ticketChangeKw['newvalue'] = ' '.join(keywords) ticketChanges.append(ticketChangeKw) if field_name in IGNORED_ACTIVITY_FIELDS: continue # Skip changes that have no effect (think translation!). if added == removed: continue # Bugzilla splits large summary changes into two records. for oldChange in ticketChanges: if (field_name == "summary" and oldChange['field'] == ticketChange['field'] and oldChange['time'] == ticketChange['time'] and oldChange['author'] == ticketChange['author']): oldChange['oldvalue'] += " " + ticketChange['oldvalue'] oldChange['newvalue'] += " " + ticketChange['newvalue'] break # cc and attachments.isobsolete sometime appear # in different activities with same time if ((field_name == "cc" or field_name == "attachments.isobsolete") \ and oldChange['time'] == ticketChange['time']): oldChange['newvalue'] += ", " + ticketChange['newvalue'] break else: ticketChanges.append (ticketChange) for ticketChange in ticketChanges: trac.addTicketChange (**ticketChange) # For some reason, bugzilla v2.11 seems to clear the resolution # when you mark a bug as closed. Let's remember it and restore # it if the ticket is closed but there's no resolution. if not ticket['resolution'] and ticket['status'] == "closed": ticket['resolution'] = resolution bug_status = bug['bug_status'] if bug_status in STATUS_KEYWORDS: kw = STATUS_KEYWORDS[bug_status] if kw not in keywords: keywords.append(kw) product = bug['product'] if product in KEYWORDS_MAPPING and not COMPONENTS_FROM_PRODUCTS: kw = KEYWORDS_MAPPING.get(product, product) if kw and kw not in keywords: keywords.append(kw) component = bug['component'] if (COMPONENTS_FROM_PRODUCTS and \ (MAP_ALL_KEYWORDS or component in KEYWORDS_MAPPING)): kw = KEYWORDS_MAPPING.get(component, component) if kw and kw not in keywords: keywords.append(kw) ticket['keywords'] = string.join(keywords) ticketid = trac.addTicket(**ticket) if BZ_VERSION >= 2210: mysql_cur.execute("SELECT attachments.*, attach_data.thedata " "FROM attachments, attach_data " "WHERE attachments.bug_id = %s AND " "attachments.attach_id = attach_data.id" % bugid) else: mysql_cur.execute("SELECT * FROM attachments WHERE bug_id = %s" % bugid) attachments = mysql_cur.fetchall() for a in attachments: author = trac.getLoginName(mysql_cur, a['submitter_id']) trac.addAttachment(author, a) print("\n8. Importing users and passwords...") if BZ_VERSION >= 2164: selectlogins = "SELECT login_name, cryptpassword FROM profiles"; if IGNORE_DISABLED_USERS: selectlogins = selectlogins + " WHERE disabledtext=''" mysql_cur.execute(selectlogins) users = mysql_cur.fetchall() else: users = () with open('htpasswd', 'w') as f: for user in users: if user['login_name'] in LOGIN_MAP: login = LOGIN_MAP[user['login_name']] else: login = user['login_name'] f.write(login + ':' + user['cryptpassword'] + '\n') print(" Bugzilla users converted to htpasswd format, see 'htpasswd'.") print("\nAll tickets converted.") def log(msg): print("DEBUG: %s" % msg) def datetime2epoch(dt) : import time return time.mktime(dt.timetuple()) * 1000000 def usage(): print("""bugzilla2trac - Imports a bug database from Bugzilla into Trac. Usage: bugzilla2trac.py [options] Available Options: --db - Bugzilla's database name --tracenv /path/to/trac/env - Full path to Trac db environment -h | --host - Bugzilla's DNS host name -u | --user - Effective Bugzilla's database user -p | --passwd - Bugzilla's user password -c | --clean - Remove current Trac tickets before importing -n | --noseverities - import Bugzilla severities as Trac priorities and forget Bugzilla priorities --help | help - This help info Additional configuration options can be defined directly in the script. """) sys.exit(0) def main(): global BZ_DB, BZ_HOST, BZ_USER, BZ_PASSWORD, TRAC_ENV, TRAC_CLEAN global SEVERITIES, PRIORITIES, PRIORITIES_MAP if len (sys.argv) > 1: if sys.argv[1] in ['--help','help'] or len(sys.argv) < 4: usage() iter = 1 while iter < len(sys.argv): if sys.argv[iter] in ['--db'] and iter+1 < len(sys.argv): BZ_DB = sys.argv[iter+1] iter = iter + 1 elif sys.argv[iter] in ['-h', '--host'] and iter+1 < len(sys.argv): BZ_HOST = sys.argv[iter+1] iter = iter + 1 elif sys.argv[iter] in ['-u', '--user'] and iter+1 < len(sys.argv): BZ_USER = sys.argv[iter+1] iter = iter + 1 elif sys.argv[iter] in ['-p', '--passwd'] and iter+1 < len(sys.argv): BZ_PASSWORD = sys.argv[iter+1] iter = iter + 1 elif sys.argv[iter] in ['--tracenv'] and iter+1 < len(sys.argv): TRAC_ENV = sys.argv[iter+1] iter = iter + 1 elif sys.argv[iter] in ['-c', '--clean']: TRAC_CLEAN = 1 elif sys.argv[iter] in ['-n', '--noseverities']: # treat Bugzilla severites as Trac priorities PRIORITIES = SEVERITIES SEVERITIES = [] PRIORITIES_MAP = {} else: print("Error: unknown parameter: " + sys.argv[iter]) sys.exit(0) iter = iter + 1 convert(BZ_DB, BZ_HOST, BZ_USER, BZ_PASSWORD, TRAC_ENV, TRAC_CLEAN) if __name__ == '__main__': main() Trac-1.2/contrib/emailfilter.py0000755000175500017550000000450113007223745016314 0ustar debacledebacle#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright (C) 2005-2013 Edgewall Software # Copyright (C) 2005 Daniel Lundin # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. """ emailfilter.py -- Email tickets to Trac. A simple MTA filter to create Trac tickets from inbound emails. Copyright 2005, Daniel Lundin Copyright 2005, Edgewall Software Please note: This is only a starting point. See * http://trac.edgewall.org/ticket/5327 and * http://trac-hacks.org/intertrac/EmailtoTracScript for more complete and advanced examples. The scripts reads emails from stdin and inserts directly into a Trac database. MIME headers are mapped as follows: * From: => Reporter * Subject: => Summary * Body => Description How to use ---------- * Set TRAC_ENV_PATH to the path of your project's Trac environment * Configure script as a mail (pipe) filter with your MTA typically, this involves adding a line like this to /etc/aliases: somename: |/path/to/email2trac.py Check your MTA's documentation for specifics. Todo ---- * Configure target database through env variable? * Handle/discard HTML parts * Attachment support """ TRAC_ENV_PATH = '/var/trac/test' import email import sys from trac.env import Environment from trac.ticket import Ticket class TicketEmailParser(object): env = None def __init__(self, env): self.env = env def parse(self, fp): msg = email.message_from_file(fp) tkt = Ticket(self.env) tkt['status'] = 'new' tkt['reporter'] = msg['from'] tkt['summary'] = msg['subject'] for part in msg.walk(): if part.get_content_type() == 'text/plain': tkt['description'] = part.get_payload(decode=1).strip() if tkt['description']: tkt.insert() if __name__ == '__main__': env = Environment(TRAC_ENV_PATH, create=0) tktparser = TicketEmailParser(env) tktparser.parse(sys.stdin) Trac-1.2/contrib/help_guide_version_notice.py0000644000175500017550000000741113007223745021232 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2014 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.com/license.html. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/. from pkg_resources import resource_listdir from trac.config import ListOption, Option from trac.core import Component, implements from trac.resource import Resource, resource_exists from trac.web.api import IRequestFilter NOTICE_TEMPLATE = """\ {{{#!box note This page documents the %(release)s (%(desc)s) release.%(alt_notice)s }}} """ ALT_NOTICE_TEMPLATE = """ See [[%(alt_page)s]] if you need the %(alt_desc)s version. """ class HelpGuideVersionNotice(Component): """Adds a version notice to pages in the Help/Guide with a link to the previous or current version of the page in the guide. The WikiExtraPlugin needs to be installed for pretty rendering of the notice using the `box` WikiProcessor. """ implements(IRequestFilter) lts_release = Option('teo', 'lts_release', '0.12', doc="Version of the LTS release of Trac.") stable_release = Option('teo', 'stable_release', '1.0', doc="Version of the stable release of Trac.") dev_release = Option('teo', 'dev_release', '1.1', doc="Version of the dev release of Trac.") ignored_pages = ListOption('teo', 'ignored_pages', 'TitleIndex, SandBox, WikiStart', doc="List of pages to ignore.") def __init__(self): self.default_pages = resource_listdir('trac.wiki', 'default-pages') for page in self.ignored_pages: self.default_pages.remove(page) def pre_process_request(self, req, handler): return handler def post_process_request(self, req, template, data, content_type): if data and 'page' in data and 'text' in data: name = data['page'].name notice = "" release = desc = alt_rel_path = alt_id = alt_desc = None if name in self.default_pages: release = self.stable_release desc = 'latest stable' alt_id = self.lts_release + '/' + name alt_rel_path = alt_id alt_desc = 'previous' elif name.startswith(self.lts_release) and \ name[len(self.lts_release)+1:] in self.default_pages: release = self.lts_release desc = 'maintenance' alt_id = name[len(self.lts_release)+1:] alt_rel_path = '../../' + alt_id alt_desc = 'latest stable' elif name.startswith(self.dev_release) and \ name[len(self.dev_release)+1:] in self.default_pages: release = self.dev_release desc = 'development' alt_id = name[len(self.dev_release)+1:] alt_rel_path = '../../' + alt_id alt_desc = 'latest stable' if alt_id: resource = Resource('wiki', alt_id) alt_notice = ALT_NOTICE_TEMPLATE % {'alt_page': alt_rel_path, 'alt_desc': alt_desc} \ if resource_exists(self.env, resource) \ else "" notice = NOTICE_TEMPLATE % {'release': release, 'desc': desc, 'alt_notice': alt_notice} data['text'] = notice + data['text'] return template, data, content_type Trac-1.2/messages-js.cfg0000644000175500017550000000107013007223732014676 0ustar debacledebacle# mapping file for extracting messages from javascript files into # trac/locale/messages-js.pot (see setup.cfg) [extractors] javascript_script = trac.dist:extract_javascript_script ignore = babel.messages.extract:extract_nothing [ignore: **/htdocs/js/jquery.js] [ignore: **/htdocs/js/jquery-ui.js] [ignore: **/htdocs/js/jquery-ui-addons.js] [ignore: **/htdocs/js/babel.js] [ignore: **/htdocs/js/excanvas.js] [ignore: **/htdocs/js/messages/*.js] [javascript: **.js] [javascript_script: **.html] Trac-1.2/.tx/0000755000175500017550000000000013007224103012500 5ustar debacledebacleTrac-1.2/.tx/config0000644000175500017550000000075413007223745013711 0ustar debacledebacle[main] host = https://www.transifex.com [trac.1_2-stable-messages-js-pot] file_filter = trac/locale//LC_MESSAGES/messages-js.po source_file = trac/locale/messages-js.pot source_lang = en [trac.1_2-stable-messages-pot] file_filter = trac/locale//LC_MESSAGES/messages.po source_file = trac/locale/messages.pot source_lang = en [trac.1_2-stable-tracini-pot] file_filter = trac/locale//LC_MESSAGES/tracini.po source_file = trac/locale/tracini.pot source_lang = en type = PO Trac-1.2/README.rst0000644000175500017550000000163713007223745013500 0ustar debacledebacleAbout Trac ========== Trac is a minimalistic web-based software project management and bug/issue tracking system. It provides an interface to the Git and Subversion revision control systems, an integrated wiki, flexible issue tracking and convenient report facilities. Trac is distributed using the modified BSD License. * For installation instructions, please see the INSTALL for a short overview and the trac/wiki/default-pages/TracInstall page for complete, up-to-date instructions * If you are upgrading from a previous Trac version, please read UPGRADE for a short overview and the trac/wiki/default-pages/TracUpgrade for complete, up-to-date instructions You might also want to take a look at the RELEASE and ChangeLog files for more information about this release. Otherwise, the primary source of information is the main Trac web site: We hope you enjoy it, /The Trac Team Trac-1.2/COPYING0000644000175500017550000000261113007223745013035 0ustar debacledebacleCopyright (C) 2003-2016 Edgewall Software All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Trac-1.2/RELEASE.rst0000644000175500017550000000252113007223732013610 0ustar debacledebacleRelease Notes for Trac 1.2rc1 'Hermes' Release ========================================= September 14, 2016 Highlights ---------- * Extensible notification system * Notification preference panel * Usernames replaced with full names * Restyled ticket changelog * Workflow controls on the New Ticket page * Editable wiki page version comments * Datetime custom fields Detailed User Visible Changes ----------------------------- A detailed view of the API changes since 1.0.x can be found in https://trac.edgewall.org/wiki/TracDev/ReleaseNotes/1.2 Developer-visible changes ------------------------- A detailed view of the API changes since 1.0.x can be found in https://trac.edgewall.org/wiki/TracDev/ApiChanges/1.2 Acknowledgements ---------------- Many thanks to the growing number of people who have, and continue to, support the project. Also our thanks to all people providing feedback and bug reports that helps us making Trac better, easier to use and more effective. Starting with the 0.12 release, special thanks go to our many translators. Without your invaluable help, Trac would not evolve. Thank you all. Finally, we hope that Trac will prove itself useful to like-minded programmers around the world, and that this release will prove an improvement over the last version. Please let us know. :-) /The Trac Team https://trac.edgewall.org/ Trac-1.2/setup.cfg0000644000175500017550000000373113007224103013614 0ustar debacledebacle[bdist_wininst] bitmap = setup_wininst.bmp [extract_messages] add_comments = TRANSLATOR: copyright_holder = Edgewall Software msgid_bugs_address = trac-dev@googlegroups.com output_file = trac/locale/messages.pot keywords = _ ngettext:1,2 N_ tag_ tagn_:1,2 cleandoc_ [init_catalog] input_file = trac/locale/messages.pot output_dir = trac/locale [compile_catalog] directory = trac/locale [update_catalog] input_file = trac/locale/messages.pot output_dir = trac/locale [extract_messages_js] add_comments = TRANSLATOR: copyright_holder = Edgewall Software msgid_bugs_address = trac-dev@googlegroups.com output_file = trac/locale/messages-js.pot keywords = _ ngettext:1,2 N_ mapping_file = messages-js.cfg [init_catalog_js] domain = messages-js input_file = trac/locale/messages-js.pot output_dir = trac/locale [compile_catalog_js] domain = messages-js directory = trac/locale [update_catalog_js] domain = messages-js input_file = trac/locale/messages-js.pot output_dir = trac/locale [generate_messages_js] domain = messages-js input_dir = trac/locale output_dir = trac/htdocs/js/messages [extract_messages_tracini] add_comments = TRANSLATOR: copyright_holder = Edgewall Software msgid_bugs_address = trac-dev@googlegroups.com output_file = trac/locale/tracini.pot keywords = ConfigSection:2 Option:4 BoolOption:4 IntOption:4 FloatOption:4 ListOption:6 ChoiceOption:4 PathOption:4 ExtensionOption:5 OrderedExtensionsOption:6 no-default-keywords = yes mapping_file = tracini.cfg [init_catalog_tracini] domain = tracini input_file = trac/locale/tracini.pot output_dir = trac/locale [compile_catalog_tracini] domain = tracini directory = trac/locale [update_catalog_tracini] domain = tracini input_file = trac/locale/tracini.pot output_dir = trac/locale [check_catalog] domain = messages input_dir = trac/locale [check_catalog_js] domain = messages-js input_dir = trac/locale [check_catalog_tracini] domain = tracini input_dir = trac/locale [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 Trac-1.2/trac/0000755000175500017550000000000013007224103012720 5ustar debacledebacleTrac-1.2/trac/core.py0000644000175500017550000002161513007223733014237 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2003-2011 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', 'TracBaseError', 'TracError'] def N_(string): """No-op translation marker, inlined here to avoid importing from `trac.util`. """ return string class TracBaseError(Exception): """Base class for all exceptions defined in Trac.""" title = N_("Trac Error") class TracError(TracBaseError): """Standard exception for errors in Trac.""" 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. """ from trac.util.translation import gettext super(TracError, self).__init__(message) self._message = message self.title = title or gettext(self.title) self.show_traceback = show_traceback message = property(lambda self: self._message, lambda self, v: setattr(self, '_message', v)) 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.%s`" % (self.interface.__module__, self.interface.__name__)) def extensions(self, component): """Return a list of components that declare to implement the extension point interface. """ classes = ComponentMeta._registry.get(self.interface, ()) components = [component.compmgr[cls] for cls in classes] return [c for c in components if c] 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__(mcs, name, bases, d): """Create the component class.""" new_class = type.__new__(mcs, name, bases, d) if name == 'Component': # Don't put the Component base class in the registry return new_class 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 cls in new_class.__mro__: for interface in cls.__dict__.get('_implements', ()): classes = registry.setdefault(interface, []) if new_class not in classes: classes.append(new_class) return new_class def __call__(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 = cls.__new__(cls) self.compmgr = self self.__init__(*args, **kwargs) return self # The normal case where the component is not also the component manager assert len(args) >= 1 and isinstance(args[0], ComponentManager), \ "First argument must be a ComponentManager instance" compmgr = args[0] self = compmgr.components.get(cls) # Note that this check is racy, we intentionally don't use a # lock in order to keep things simple and avoid the risk of # deadlocks, as the impact of having temporarily two (or more) # instances for a given `cls` is negligible. if self is None: self = cls.__new__(cls) self.compmgr = compmgr compmgr.component_activated(self) self.__init__() # Only register the instance once it is fully initialized (#9418) compmgr.components[cls] = self return self 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 @staticmethod def implements(*interfaces): """Can be used in the class definition 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) def __repr__(self): """Return a textual representation of the component.""" return '' % (self.__class__.__module__, self.__class__.__name__) 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 whether 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 instance if the component has already been activated. Note that `ComponentManager` components can't be activated that way. """ if not self.is_enabled(cls): return None component = self.components.get(cls) if not component and not issubclass(cls, ComponentManager): if cls not in ComponentMeta._components: raise TracError('Component "%s" not registered' % cls.__name__) try: component = cls(self) except TypeError as e: raise TracError("Unable to instantiate component %r (%s)" % (cls, e)) return component def is_enabled(self, cls): """Return whether the given component class is enabled.""" if cls not in self.enabled: self.enabled[cls] = self.is_component_enabled(cls) return self.enabled[cls] def disable_component(self, component): """Force a component to be disabled. :param component: can be a class or an instance. """ if not isinstance(component, type): component = component.__class__ self.enabled[component] = False self.components[component] = None def enable_component(self, component): """Force a component to be enabled. :param component: can be a class or an instance. :since: 1.0.13 """ if not isinstance(component, type): component = component.__class__ self.enabled[component] = True 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 was disabled explicitly. If it returns `None`, the component was neither enabled nor disabled explicitly. In both cases, the component with the given class will not be available. """ return True Trac-1.2/trac/prefs/0000755000175500017550000000000013007224103014037 5ustar debacledebacleTrac-1.2/trac/prefs/web_ui.py0000644000175500017550000002367713007223733015712 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2004-2014 Edgewall Software # Copyright (C) 2004-2005 Daniel Lundin # 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 pkg_resources import re from genshi.builder import tag from trac.core import * from trac.prefs.api import IPreferencePanelProvider from trac.util import lazy from trac.util.datefmt import all_timezones, get_timezone, localtz from trac.util.translation import _, Locale, deactivate,\ get_available_locales, make_activable from trac.web.api import HTTPNotFound, IRequestHandler, \ is_valid_default_handler from trac.web.chrome import Chrome, INavigationContributor, \ ITemplateProvider, add_notice, add_stylesheet class PreferencesModule(Component): implements(INavigationContributor, IRequestHandler, ITemplateProvider) panel_providers = ExtensionPoint(IPreferencePanelProvider) # INavigationContributor methods def get_active_navigation_item(self, req): return 'prefs' def get_navigation_items(self, req): panels = self._get_panels(req)[0] if panels: yield 'metanav', 'prefs', tag.a(_("Preferences"), href=req.href.prefs()) # IRequestHandler methods def match_request(self, req): match = re.match('/prefs(?:/([^/]+))?$', req.path_info) if match: req.args['panel_id'] = match.group(1) return True def process_request(self, req): if req.is_xhr and req.method == 'POST' and 'save_prefs' in req.args: self._do_save_xhr(req) panels, providers = self._get_panels(req) if not panels: raise HTTPNotFound(_("No preference panels available")) panels = [] child_panels = {} providers = {} for provider in self.panel_providers: for panel in provider.get_preference_panels(req) or []: if len(panel) == 3: name, label, parent = panel child_panels.setdefault(parent, []).append((name, label)) else: name = panel[0] panels.append(panel) providers[name] = provider panels = sorted(panels) panel_id = req.args.get('panel_id') if panel_id is None: panel_id = panels[1][0] \ if len(panels) > 1 and panels[0][0] == 'advanced' \ else panels[0][0] chosen_provider = providers.get(panel_id) if not chosen_provider: raise HTTPNotFound(_("Unknown preference panel '%(panel)s'", panel=panel_id)) session_data = { 'session': req.session, 'settings': {'session': req.session, # Compat: remove in 1.3.1 'session_id': req.session.sid}, } # Render child preference panels. chrome = Chrome(self.env) children = [] if child_panels.get(panel_id): for name, label in child_panels[panel_id]: ctemplate, cdata = provider.render_preference_panel(req, name) cdata.update(session_data) rendered = chrome.render_template(req, ctemplate, cdata, fragment=True) children.append((name, label, rendered)) template, data = \ chosen_provider.render_preference_panel(req, panel_id) data.update(session_data) data.update({ 'active_panel': panel_id, 'panels': panels, 'children': children, }) add_stylesheet(req, 'common/css/prefs.css') return template, data, None # ITemplateProvider methods def get_htdocs_dirs(self): return [] def get_templates_dirs(self): return [pkg_resources.resource_filename('trac.prefs', 'templates')] # Internal methods def _get_panels(self, req): """Return a list of available preference panels.""" panels = [] providers = {} for provider in self.panel_providers: p = list(provider.get_preference_panels(req) or []) for panel in p: providers[panel[0]] = provider panels += p return panels, providers def _do_save_xhr(self, req): for key in req.args: if key not in ('save_prefs', 'panel_id', '__FORM_TOKEN'): req.session[key] = req.args[key] req.session.save() req.send_no_content() class AdvancedPreferencePanel(Component): implements(IPreferencePanelProvider) _form_fields = ('newsid',) # IPreferencePanelProvider methods def get_preference_panels(self, req): if not req.authname or req.authname == 'anonymous': yield 'advanced', _("Advanced") def render_preference_panel(self, req, panel): if req.method == 'POST': if 'restore' in req.args: self._do_load(req) else: _do_save(req, panel, self._form_fields) return 'prefs_advanced.html', {'session_id': req.session.sid} def _do_load(self, req): if req.authname == 'anonymous': oldsid = req.args.get('loadsid') if oldsid: req.session.get_session(oldsid) add_notice(req, _("The session has been loaded.")) class GeneralPreferencePanel(Component): implements(IPreferencePanelProvider) _form_fields = ('name', 'email') # IPreferencePanelProvider methods def get_preference_panels(self, req): yield None, _("General") def render_preference_panel(self, req, panel): if req.method == 'POST': _do_save(req, panel, self._form_fields) return 'prefs_general.html', {} class KeyBindingsPreferencePanel(Component): implements(IPreferencePanelProvider) _form_fields = ('accesskeys',) # IPreferencePanelProvider methods def get_preference_panels(self, req): yield 'keybindings', _("Keyboard Shortcuts") def render_preference_panel(self, req, panel): if req.method == 'POST': _do_save(req, panel, self._form_fields) return 'prefs_keybindings.html', {} class LocalizationPreferencePanel(Component): implements(IPreferencePanelProvider) _form_fields = ('tz', 'lc_time', 'dateinfo', 'language') # IPreferencePanelProvider methods def get_preference_panels(self, req): yield 'localization', _("Localization") def render_preference_panel(self, req, panel): if req.method == 'POST': if Locale and \ req.args.get('language') != req.session.get('language'): # reactivate translations with new language setting # when changed del req.locale # for re-negotiating locale deactivate() make_activable(lambda: req.locale, self.env.path) _do_save(req, panel, self._form_fields) data = { 'timezones': all_timezones, 'timezone': get_timezone, 'localtz': localtz, 'has_babel': False, } if Locale: locale_ids = get_available_locales() locales = [Locale.parse(locale) for locale in locale_ids] # use locale identifiers from get_available_locales() instead # of str(locale) to prevent storing expanded locale identifier # to session, e.g. zh_Hans_CN and zh_Hant_TW, since Babel 1.0. # see #11258. languages = sorted((id_, locale.display_name) for id_, locale in zip(locale_ids, locales)) data['locales'] = locales data['languages'] = languages data['has_babel'] = True return 'prefs_localization.html', data class UserInterfacePreferencePanel(Component): implements(IPreferencePanelProvider) _request_handlers = ExtensionPoint(IRequestHandler) _form_fields = ('default_handler', 'ui.hide_help', 'ui.use_symbols') # IPreferencePanelProvider methods def get_preference_panels(self, req): yield 'userinterface', _("User Interface") def render_preference_panel(self, req, panel): if req.method == 'POST': _do_save(req, panel, self._form_fields) data = { 'project_default_handler': self._project_default_handler, 'valid_default_handlers': self._valid_default_handlers, } return 'prefs_userinterface.html', data # Internal methods @property def _project_default_handler(self): return self.config.get('trac', 'default_handler') @lazy def _valid_default_handlers(self): return sorted(handler.__class__.__name__ for handler in self._request_handlers if is_valid_default_handler(handler)) def _do_save(req, panel, form_fields): for field in form_fields: val = req.args.get(field, '').strip() if val: if field == 'tz' and 'tz' in req.session and \ val not in all_timezones: del req.session['tz'] elif field == 'newsid': req.session.change_sid(val) else: req.session[field] = val elif (field in req.args or field + '_cb' in req.args) and \ field in req.session: del req.session[field] add_notice(req, _("Your preferences have been saved.")) req.redirect(req.href.prefs(panel)) Trac-1.2/trac/prefs/templates/0000755000175500017550000000000013007224103016035 5ustar debacledebacleTrac-1.2/trac/prefs/templates/prefs_general.html0000644000175500017550000000334013007223733021547 0ustar debacledebacle General

This information is used to automatically populate some forms on this site with your contact details. This information is used to associate your login name with your email address and full name, which is used for email notification and RSS feeds, for example.

Trac-1.2/trac/prefs/templates/prefs_userinterface.html0000644000175500017550000000526613007223733023002 0ustar debacledebacle User Interface

Display only the icon or symbol for short inline buttons, and hide the text caption.

Don't show the various help links. This reduces the verbosity of the pages.

$default_handler is not a valid IRequestHandler or is not enabled.
Trac-1.2/trac/prefs/templates/prefs_advanced.html0000644000175500017550000000451613007223733021705 0ustar debacledebacle Advanced

The session key is used to identify stored custom settings and session data on the server. Although it is automatically generated by default, you may change it to something easier to remember at any time if you wish to load your settings in a different web browser.

You may load a previously created session by entering the corresponding session key below. This lets you share settings between multiple computers and web browsers.

Trac-1.2/trac/prefs/templates/prefs_keybindings.html0000644000175500017550000000301213007223733022434 0ustar debacledebacle Keyboard Shortcuts
Trac-1.2/trac/prefs/templates/prefs_localization.html0000644000175500017550000001613313007223733022626 0ustar debacledebacle Localization
Install Babel for extended language support. Message catalogs have not been compiled.

Configuring your language will result in all text displayed on this site to use your language instead of that of the server.

The Default language option uses the browser's language negotiation feature to select the appropriate language.

Trac has been localized to more than a dozen of languages but in order to be able to use them, the Babel package needs to be present when installing Trac. See TracInstall for details. Please contact your Trac administrator to enable existing translations.

Configuring your time zone will result in all dates and times displayed on this site to use your time zone instead of that of the server.

Example: The current time is ${format_time(now, 'iso8601', tzinfo=utc)} (UTC).
In your time zone ${nowtz.tzname()}, this would be displayed as $formatted. In the default time zone, this would be displayed as $formatted.

Note: Universal Co-ordinated Time (UTC) is also known as Greenwich Mean Time (GMT).
A positive offset is used to indicate a timezone at the east of Greenwich, i.e. ahead of Universal Time.

Configuring your date format will result in formatting and parsing datetime displayed on this site to use your date format instead of that of the server.

Configuring your relative/absolute format will result in formatting datetime displayed on this site to use your format instead of that of the server.

Trac-1.2/trac/prefs/templates/prefs.html0000644000175500017550000000431113007223733020051 0ustar debacledebacle Preferences: ${select('title/text()')} ${select("*[local-name() != 'title']")}

Preferences

This page lets you customize your personal settings for this site. These settings are stored on the server and are identified by a session key stored in a browser cookie. That cookie allows your settings to be restored on subsequent visits.

${select("*|text()")}

${title}

${child}
Trac-1.2/trac/prefs/__init__.py0000644000175500017550000000101613007223733016156 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2006-2014 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.prefs.api import * Trac-1.2/trac/prefs/tests/0000755000175500017550000000000013007224103015201 5ustar debacledebacleTrac-1.2/trac/prefs/tests/__init__.py0000644000175500017550000000105113007223733017317 0ustar debacledebacle# -*- coding: utf-8 -*- # # Copyright (C) 2008-2013 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.prefs.tests.functional import functionalSuite Trac-1.2/trac/prefs/tests/functional.py0000755000175500017550000002541313007223733017735 0ustar debacledebacle#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2008-2014 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 re import unittest from trac.tests.functional import FunctionalTwillTestCaseSetup, \ internal_error, tc #TODO: split this into multiple smaller testcases class TestPreferences(FunctionalTwillTestCaseSetup): def runTest(self): """Set preferences for admin user""" self._tester.go_to_preferences() try: tc.notfind('Your preferences have been saved.') tc.formvalue('userprefs', 'name', ' System Administrator ') tc.formvalue('userprefs', 'email', ' admin@example.com ') tc.submit() tc.find('Your preferences have been saved.') self._tester.go_to_preferences("Localization") tc.formvalue('userprefs', 'tz', 'GMT -10:00') tc.submit() tc.find('Your preferences have been saved.') self._tester.go_to_preferences() tc.notfind('Your preferences have been saved.') tc.find('value="System Administrator"') tc.find(r'value="admin@example\.com"') self._tester.go_to_preferences("Localization") tc.find('GMT -10:00') finally: self._tester.go_to_preferences() tc.formvalue('userprefs', 'name', '') tc.formvalue('userprefs', 'email', '') tc.submit() tc.find('Your preferences have been saved.') class TestDefaultHandler(FunctionalTwillTestCaseSetup): def runTest(self): """Set default handler.""" # Project default_handler is selected. self._tester.go_to_preferences("User Interface") tc.notfind(r']+selected="selected"') tc.find("Default \(WikiModule\)") try: # Project default handler still selected after value is changed. hint = "SearchModule is not a valid IRequestHandler or is not" \ " enabled\." self._testenv.set_config('trac', 'default_handler', 'SearchModule') self._tester.go_to_preferences("User Interface") tc.notfind(']+selected="selected"') tc.find("Default \(SearchModule\)") tc.notfind(hint) # Project default handler still selected after module is disabled. component = 'trac.search.web_ui.*' self._testenv.set_config('components', component, 'disabled') self._tester.go_to_preferences("User Interface") try: tc.notfind(']+selected="selected"') tc.find(r"Default \(SearchModule\)") tc.find(hint) finally: self._testenv.remove_config('components', component) finally: self._testenv.set_config('trac', 'default_handler', 'WikiModule') # Set session default handler and navigate to base URL. tc.formvalue('userprefs', 'default_handler', 'TimelineModule') tc.submit() tc.find("Your preferences have been saved\.") tc.find(']+selected="selected"[^>]+TimelineModule') self._tester.go_to_front() tc.find("

Timeline

") # Clear session default handler. self._tester.go_to_preferences("User Interface") tc.formvalue('userprefs', 'default_handler', '') tc.submit() tc.find("Your preferences have been saved\.") tc.notfind(r']+selected="selected"') tc.find("Default \(WikiModule\)") class RegressionTestRev5785(FunctionalTwillTestCaseSetup): def runTest(self): """Test for regression of the fix in r5785""" self._tester.go_to_preferences() tc.submit('logout', 'logout') tc.notfind(internal_error) # See [5785] tc.follow('Login') class RegressionTestTicket5765(FunctionalTwillTestCaseSetup): def runTest(self): """Test for regression of http://trac.edgewall.org/ticket/5765 Unable to turn off 'Enable access keys' in Preferences """ self._tester.go_to_preferences("Keyboard Shortcuts") tc.formvalue('userprefs', 'accesskeys', True) tc.submit() tc.find('name="accesskeys".*checked="checked"') tc.formvalue('userprefs', 'accesskeys', False) tc.submit() tc.notfind('name="accesskeys".*checked="checked"') class RegressionTestTicket11319(FunctionalTwillTestCaseSetup): def runTest(self): """Test for regression of http://trac.edgewall.org/ticket/11319 Only alphanumeric characters can be used for session key in advanced panel. """ try: self._tester.logout() self._tester.go_to_preferences('Advanced') tc.formvalue('userprefs', 'newsid', 'śeśśion_id') tc.submit('change') tc.notfind(internal_error) tc.find('Session ID must be alphanumeric') self._tester.go_to_preferences('Advanced') tc.formvalue('userprefs', 'loadsid', 'śeśśion_id') tc.submit('restore') tc.notfind(internal_error) tc.find('Session ID must be alphanumeric') finally: self._tester.login('admin') class RegressionTestTicket11337(FunctionalTwillTestCaseSetup): def runTest(self): """Test for regression of http://trac.edgewall.org/ticket/11337 The language select will be disabled if Babel is not installed and a hint will be shown. The text of the hint is dependent on whether the user has TRAC_ADMIN and the message catalogs have been compiled. """ from trac.util.translation import has_babel, get_available_locales babel_hint = "Install Babel for extended language support." catalog_hint = "Message catalogs have not been compiled." nonadmin_hint = r"Please contact your\s+Trac administrator\s+" \ r"to enable existing translations." language_select = '' self._tester.go_to_preferences("Localization") if has_babel: tc.notfind(babel_hint) if get_available_locales(): tc.find(language_select) tc.notfind(babel_hint) tc.notfind(catalog_hint) else: tc.find(disabled_language_select) tc.find(catalog_hint) tc.notfind(babel_hint) else: tc.find(disabled_language_select) tc.find(babel_hint) tc.notfind(catalog_hint) tc.notfind(nonadmin_hint) # For users without TRAC_ADMIN, the Language tab should only be # present when Babel is installed self._tester.logout() self._tester.go_to_preferences("Localization") try: if has_babel and get_available_locales(): tc.find(language_select) tc.notfind(nonadmin_hint) elif has_babel: tc.find(disabled_language_select) tc.find(nonadmin_hint) else: tc.find(disabled_language_select) tc.find(nonadmin_hint) tc.notfind(catalog_hint) tc.notfind(babel_hint) finally: self._tester.login('admin') class RegressionTestTicket11515(FunctionalTwillTestCaseSetup): def runTest(self): """Test for regression of http://trac.edgewall.org/ticket/11515 Show a notice message with new language setting after it is changed. """ from trac.util.translation import Locale, has_babel, \ get_available_locales from pkg_resources import resource_exists, resource_filename if not has_babel: print("SKIP: RegressionTestTicket11515 (Babel not installed)") return if not resource_exists('trac', 'locale'): return locale_dir = resource_filename('trac', 'locale') from babel.support import Translations string = 'Your preferences have been saved.' translated = None for second_locale_id in get_available_locales(): tx = Translations.load(locale_dir, second_locale_id) translated = tx.dgettext('messages', string) if string != translated: break # the locale has a translation else: print("SKIP: RegressionTestTicket11515 " "(Message catalogs not compiled)") return try: self._tester.go_to_preferences('Localization') tc.formvalue('userprefs', 'language', second_locale_id) tc.submit() tc.find(re.escape(translated)) tc.find('