[[TOC(heading=Egg Cooking Tutorial, EggCookingTutorial/BasicEggCooking, EggCookingTutorial/AdvancedEggCooking, EggCookingTutorial/AdvancedEggCooking2, EggCookingTutorial/publish)]] = Cooking high-end eggs Now that you have a working version of a plugin already, let's add a final twist and serve some static content like a stylesheet and an image. == Important thing to check First step is to ensure that your ''trac.ini'' doesn't have ''htdocs_location'' set, otherwise Trac can't serve static data. == More directories We will need a few more directories for the files that we will add: {{{#!sh ./helloworld-plugin/helloworld/htdocs/ ./helloworld-plugin/helloworld/htdocs/css/ ./helloworld-plugin/helloworld/htdocs/images/ }}} == Style is everything We want to use our own stylesheet to give some color to our content. For that we need [wikipedia:Cascading_Style_Sheets cascading stylesheets]. Create ''helloworld.css'' in ''./helloworld-plugin/helloworld/htdocs/css/'': {{{#!text/css div.helloworld h1 { color: red; } }}} == Image tells more than thousand words Images enhance the look and feel of your site. Put small image named ''helloworld.jpg'' in ''./helloworld-plugin/helloworld/htdocs/images/''. == Egg grows Even natural eggs doesn't grow Python eggs does. Modify ''setup.py'' to include our static data: {{{#!python from setuptools import setup PACKAGE = 'TracHelloworld' VERSION = '0.1' setup(name=PACKAGE, version=VERSION, packages=['helloworld'], entry_points={'trac.plugins': '%s = helloworld' % PACKAGE}, package_data={'helloworld': ['htdocs/css/*.css', 'htdocs/images/*.jpg', 'templates/*.cs']}, ) }}} == Tell it to Trac Trac doesn't know where our stylesheet and image is located, so you have to tell it to Trac. Add the following code at the tail in file ''helloworld.py'', which implements {{{get_htdocs_dir()}}} to tell the static data path information for this plugin with identical prefix 'hw': {{{#!python def get_htdocs_dirs(self): """Return a list of directories with static resources (such as style sheets, images, etc.) Each item in the list must be a `(prefix, abspath)` tuple. The `prefix` part defines the path in the URL that requests to these resources are prefixed with. The `abspath` is the absolute path to the directory containing the resources on the local file system. """ from pkg_resources import resource_filename return [('hw', resource_filename(__name__, 'htdocs'))] }}} == Remember to load stylesheet To make Trac to load our stylesheet you need to modify ''process_request'' method starting from line 23 to following: {{{#!python def process_request(self, req): add_stylesheet(req, 'hw/css/helloworld.css') return 'helloworld.cs', None }}} Note that prefix path 'hw/' specified by {{{get_htdocs_dirs()}}} should be used. And also import {{{add_stylesheet()}}} at the beginning of ''helloworld.py'': {{{#!python from trac.web.chrome import INavigationContributor, ITemplateProvider, \ add_stylesheet }}} == Complete version of code The whole of the Python code is here: {{{#!python # Helloworld plugin from trac.core import * from trac.web.chrome import INavigationContributor, ITemplateProvider, \ add_stylesheet from trac.web.main import IRequestHandler from trac.util import escape, Markup class UserbaseModule(Component): implements(INavigationContributor, IRequestHandler, ITemplateProvider) # INavigationContributor methods def get_active_navigation_item(self, req): return 'helloworld' def get_navigation_items(self, req): yield 'mainnav', 'helloworld', Markup('Hello', self.env.href.helloworld()) # IRequestHandler methods def match_request(self, req): return req.path_info == '/helloworld' def process_request(self, req): add_stylesheet(req, 'hw/css/helloworld.css') return 'helloworld.cs', None # ITemplateProvider methods def get_templates_dirs(self): """Return a list of directories containing the provided ClearSilver templates. """ from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): """Return a list of directories with static resources (such as style sheets, images, etc.) Each item in the list must be a `(prefix, abspath)` tuple. The `prefix` part defines the path in the URL that requests to these resources are prefixed with. The `abspath` is the absolute path to the directory containing the resources on the local file system. """ from pkg_resources import resource_filename return [('hw', resource_filename(__name__, 'htdocs'))] }}} == Back to images We need to add our image to the template to see it. Our new ''helloworld.cs'': {{{#!text/html

Hello world!

}}} == Compilation and deployment Now you should be familiar with both, so make an egg and deploy it. Click and see "Hello world!" with your chosen image. == Aftermath Now you have successfully completed a simple plugin that uses its own template and serves some static data.