Edgewall Software

Changes between Initial Version and Version 1 of TracDev/PluginDevelopment


Ignore:
Timestamp:
May 21, 2005, 3:14:06 PM (15 years ago)
Author:
Christopher Lenz
Comment:

Initial version of a guide for plugin authors

Legend:

Unmodified
Added
Removed
Modified
  • TracDev/PluginDevelopment

    v1 v1  
     1= Writing Plugins for Trac =
     2
     3Starting with version [milestone:0.9 0.9], you can develop plugins for Trac that extend the builtin functionality. The plugin functionality is based on the [wiki:TracDev/ComponentArchitecture component architecture], so please read that document before continuing here.
     4
     5== Extension points ==
     6
     7Trac 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'' (not available here yet).
     8
     9Currently we have:
     10
     11 * {{{trac.web.main.IRequestHandler}}} [[BR]]
     12   Allows plugins to add handlers for HTTP requests.
     13 * {{{trac.web.chrome.INavigationContributor}}} [[BR]]
     14   Allows plugins to extend the navigation menus of the web interface.
     15 * {{{trac.Timeline.ITimelineEventProvider}}} [[BR]]
     16   Allows plugins to contribute events to the [wiki:TracTimeline timeline].
     17 * {{{trac.mimeview.api.IHTMLPreviewRenderer}}} [[BR]]
     18   Allows plugins to provide support for rendering specific content of a specific type as HTML (used for TracSyntaxColoring and image preview)
     19 * {{{trac.wiki.api.IWikiChangeListener}}} [[BR]]
     20   Allows plugins to observe creation, modification and deletion of wiki pages.
     21 * {{{trac.wiki.api.IWikiMacroProvider}}} [[BR]]
     22   Allows plugins to contribute WikiMacros to Trac.
     23
     24''Note that plugins can themselves add new extension points, so the list above is incomplete by nature.''
     25
     26== Adding custom plugins ==
     27
     28To extend Trac with a custom plugin, you need to provide 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:
     29
     30{{{
     31#!python
     32from trac.core import *
     33from trac.web.chrome import INavigationContributor
     34from trac.web.main import IRequestHandler
     35
     36class HelloWorldPlugin(Component):
     37    implements(INavigationContributor, IRequestHandler)
     38
     39    # INavigationContributor methods
     40    def get_active_navigation_item(self, req):
     41        return 'helloworld'
     42    def get_navigation_items(self, req):
     43        yield 'mainnav', 'helloworld', '<a href="%s">Hello World</a>'
     44                                       % self.env.href.helloworld()
     45
     46    # IRequestHandler methods
     47    def match_request(self, req):
     48        return req.path_info == '/helloworld'
     49    def process_request(self, req):
     50        req.send_response(200)
     51        req.send_header('Content-Type', 'text/plain')
     52        req.end_headers()
     53        req.write('Hello world!')
     54}}}
     55
     56Look at the API documentation for the extension point interfaces to see what you're expected to return.
     57
     58== Deploying a custom plugin ==
     59
     60To register a custom plugin with a Trac environment, you edit the [wiki:TracIni trac.ini] file to add a new configuration section for your plugin. Let's assume the above plugin is in a Python module with the name ''example.helloworld''
     61
     62{{{
     63[helloworld]
     64module = example.helloworld
     65}}}
     66
     67Your plugin should now get loaded and registered when Trac is run (when running under [wiki:TracModPython mod_python] or [wiki:TracStandalone tracd], you'll need to restart the server). This assumes that your module is on the Python path. If it is not, you can simply tell Trac where to look for it by using the {{{path}}} option:
     68
     69{{{
     70[helloworld]
     71module = example.helloworld
     72path = /home/cmlenz/src/trac-plugins
     73}}}
     74
     75The name of the configuration section is not significant: Trac simply goes through all the sections and looks for an option called {{{module}}}. But if your plugin also uses its own configuration options, you should of course document the name of the section where these options are expected to be found, and the admin will probably choose to put the {{{module}}} and {{{path}}} options in the same section:
     76
     77{{{
     78[helloworld]
     79module = example.helloworld
     80path = /home/cmlenz/src/trac-plugins
     81message = Hello world!
     82}}}
     83
     84You can use this configuration option from the plugin as follows:
     85
     86{{{
     87#!python
     88...
     89class HelloWorldPlugin(Component):
     90    ...
     91    def process_request(self, req):
     92        req.send_response(200)
     93        req.send_header('Content-Type', 'text/plain')
     94        req.end_headers()
     95        req.write(self.config.get('helloworld', 'message')
     96}}}
     97
     98== Disabling built-in components ==
     99
     100Sometimes 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 [source:/trunk/trac/db_default.py#latest trac.db_default]. These built-in components are always loaded, and might therefore conflict with your replacement plugin.
     101
     102You can however disable built-in components using a special [wiki:TracIni trac.ini] section called {{{[disabled_components]}}}. This section contains the qualified name of the components to disable, along with a boolean value (yes/true/1 or no/false/0), where a positive value means the component is disabled, and a negative value means its enabled.
     103
     104For example, to disable the built-in Wiki macro {{{RecentChanges}}}, you'd include the following in {{{trac.ini}}}:
     105{{{
     106[disabled_components]
     107trac.wiki.macros.RecentChanges = yes
     108}}}
     109
     110This mechanism uses string prefix matching, so you could even disable the complete Wiki module (although wiki formatting will still work in the remaining modules, of course):
     111{{{
     112[disabled_components]
     113trac.wiki = yes
     114}}}