|Version 10 (modified by 15 years ago) ( diff ),|
Writing Plugins for Trac
Starting with version 0.9, you can develop plugins for Trac that extend the builtin functionality. The plugin functionality is based on the component architecture, so please read that document before continuing here.
Trac offers an increasing number of extension points that allow you to plugin custom extensions for various functions. You can view a list of provided extension points on the page About Trac/Plugins of your Trac installation.
Currently we have:
- Allows plugins to participate in the creation and upgrade of the environment. Can be used to setup additional database tables or directories needed for the plugin to operate
- Allows plugins to add handlers for HTTP requests.
- Allows plugins to extend the navigation menus of the web interface.
- Extension point interface for components that provide their own ClearSilver templates and accompanying static resources.
- Plugins can use this extension point to define additional "actions" for the permission system.
- Allows plugins to contribute events to the timeline.
- Allows plugins to provide support for rendering specific content of a specific type as HTML (used for TracSyntaxColoring and image preview)
- Allows plugins to observe creation, modification and deletion of wiki pages.
- Allows plugins to contribute WikiMacros to Trac.
- Plugins can extend this extension point to add custom syntax rules to the wiki formatting system. In particular, this allows registration of additional TracLinks types.
Note that plugins can themselves add new extension points, so the list above is incomplete by nature.
Writing the plugin code
To extend Trac with a custom plugin, you need to implement a component. For example, to add a new web module to Trac (i.e. a component that handles HTTP requests and extends the navigation bar), you'd start with something like the following code:
from trac.core import * from trac.web import IRequestHandler from trac.web.chrome import INavigationContributor class HelloWorldPlugin(Component): implements(INavigationContributor, IRequestHandler) # INavigationContributor methods def get_active_navigation_item(self, req): return 'helloworld' def get_navigation_items(self, req): yield 'mainnav', 'helloworld', '<a href="%s">Hello World</a>' \ % self.env.href.helloworld() # IRequestHandler methods def match_request(self, req): return req.path_info == '/helloworld' def process_request(self, req): req.send_response(200) req.send_header('Content-Type', 'text/plain') req.end_headers() req.write('Hello world!')
Look at the API documentation for the extension point interfaces to see what you're expected to return.
Component member variables
Every component that gets instantiated through the Trac environment gets three extra member variables for convenience:
env: The environment, an instance of the
trac.env.Environmentclass (see trac.env).
config: The configuration, an instance of the
trac.config.Configurationclass (see trac.config).
log: The configured logger, see the Python logging API for more information.
These variables can also be accessed from the initializer (
__init__) of a component.
Storing any other objects as instance variables of your component is probably a bad idea: remember that a component is only instantiated once for a given environment; unless your plugin is used in a CGI deployment of Trac, that means that the same component instance will get invoked for multiple HTTP requests; if the server is multi-threaded, this will even happen concurrently.
Packaging and deploying plugins
The egg file needs to have a file named
trac_plugin.txt in its
EGG-INFO directory. This file should contain the names of all modules that need to be imported by Trac to register your components.
Note that this will change in the very near future: setuptools 0.6 will introduce the concept of “entry points”, which will be used instead of the
trac_plugin.txtdescriptor.. See TracPlugins for details.
A plugin can either be deployed globally, or only for a specific environment. Global deployment is done by installing the plugin:
$ cd /path/to/pluginsource $ python setup.py install
To deploy a plugin only to a specific Trac environment, copy the egg file into the
plugins directory of that environment:
$ cd /path/to/pluginsource $ python setup.py bdist_egg $ cp dist/*.egg /path/to/projenv/plugins
During development of a plugin, it is inconvenient to have to install it in either of the ways described above. Instead, you should use the setuptools
$ cd /path/to/pluginsource $ python setup.py develop --install-dir=/path/to/projenv/plugins
You can omit the
--install-dir argument to make the development version of your plugin available globally.
This will install an
.egg-link file instead of the actual egg. That file is basically a link to the source directory of your plugin, so that Trac will always see the latest version of your code. In this case you will have to explicitly enable your plugin in the trac configuration as explained on TracPlugins.
A tutorial to build your own plugins is available here.
Disabling built-in components
Sometimes you might want to write a plugin that completely replaces a built-in component, for example to develop an advanced variant of an existing module. Trac uses a list of default component to load, as specified in the
default_components list in trac.db_default. These built-in components are always loaded, and might therefore conflict with your replacement plugin.
You can however disable built-in components using a special trac.ini section called
[disabled_components]. This section contains the qualified name of the components to disable, along with
off as the value.
For example, to disable the built-in Wiki macro
RecentChanges, you'd include the following in trac.ini:
[components] trac.wiki.macros.RecentChanges = disabled
You can also use a wildcard at the end of a name, so you could even disable the complete Wiki module (although wiki formatting will still work in the remaining modules, of course):
[components] trac.wiki.* = disabled