Commits

Anonymous committed 4fbeaad

0.13dev: get rid of obsolete support for the Clearsilver template engine.

The `trac.web.clearsilver.FormTokenInjector` which could be of some use for plugins independantly from Clearsilver has been moved to `trac.util.html.FormTokenInjector`.

Comments (0)

Files changed (12)

 From 0.12.x to 0.13.x
 ---------------------
 
-See below.
+Note that Clearsilver based plugin are now no longer supported.
 
 From 0.11.x to 0.12.x
 ---------------------
 Also, you should be careful to check that the plugins you depend on
 have been ported to 0.11, as they most probably won't work without
 adaptation due to the numerous internal changes that occurred during
-0.11 development.  Note however that Clearsilver based plugin are
-still supported.
+0.11 development.
 
 See: http://trac.edgewall.org/wiki/TracDev/ApiChanges/0.11
 

trac/templates/README

 This directory contains Trac's default Genshi templates.
 
-It also still contains the base Clearsilver templates for 
-compatibility with pre-0.11 plugins.
+It is not advised to make local modifications to those files after
+installation, as they might be lost during the installation of a new
+Trac version.
 
-Local modifications to these files might be lost during the installation of 
-a new Trac version. This can be avoided by making a copy of this entire
-directory before beginning modifications.
+As an alternative, you can copy the templates you want to modify and
+place them in the templates/ directory of your Trac environment or in
+the location specified in the trac.ini file under the `[inherit]
+templates_dir` setting (the former having precedence over the latter).
+
+Nevertheless, if you use such a locally modified template, it will be
+easy to miss the functionality provided by a newer version of the
+template, after an upgrade. Because of that, the preferred way to
+alter the output would be to write plugins doing Genshi stream
+transformation (see for example the sample-plugins/ticket_clone.py),
+as this is robust when upgrading to a newer version of Trac: you only
+have to check that your filtering still matches and that the
+transformation works as expected...

trac/templates/footer.cs

-<?cs if:len(chrome.links.alternate) ?>
-<div id="altlinks"><h3>Download in other formats:</h3><ul><?cs
- each:link = chrome.links.alternate ?><?cs
-  set:isfirst = name(link) == 0 ?><?cs
-  set:islast = name(link) == len(chrome.links.alternate) - 1?><li<?cs
-    if:isfirst || islast ?> class="<?cs
-     if:isfirst ?>first<?cs /if ?><?cs
-     if:isfirst && islast ?> <?cs /if ?><?cs
-     if:islast ?>last<?cs /if ?>"<?cs
-    /if ?>><a rel="nofollow" href="<?cs var:link.href ?>"<?cs if:link.class ?> class="<?cs
-    var:link.class ?>"<?cs /if ?>><?cs var:link.title ?></a></li><?cs
- /each ?></ul></div><?cs
-/if ?>
-
-</div>
-
-<div id="footer">
- <hr />
- <a id="tracpowered" href="http://trac.edgewall.org/"><img src="<?cs
-   var:htdocs_location ?>trac_logo_mini.png" height="30" width="107"
-   alt="Trac Powered"/></a>
- <p class="left">
-  Powered by <a href="<?cs var:trac.href.about ?>"><strong>Trac <?cs
-  var:trac.version ?></strong></a><br />
-  By <a href="http://www.edgewall.org/">Edgewall Software</a>.
- </p>
- <p class="right">
-  <?cs var:project.footer ?>
- </p>
-</div>
-
-<?cs include "site_footer.cs" ?>
- </body>
-</html>

trac/templates/header.cs

-<!DOCTYPE html
-    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-<head><?cs
- if:project.name_encoded ?>
- <title><?cs if:title ?><?cs var:title ?> - <?cs /if ?><?cs
-   var:project.name_encoded ?></title><?cs
- else ?>
- <title>Trac: <?cs var:title ?></title><?cs
- /if ?><?cs
- if:html.norobots ?>
- <meta name="ROBOTS" content="NOINDEX, NOFOLLOW" /><?cs
- /if ?><?cs
- each:rel = chrome.links ?><?cs
-  each:link = rel ?><link rel="<?cs
-   var:name(rel) ?>" href="<?cs var:link.href ?>"<?cs
-   if:link.title ?> title="<?cs var:link.title ?>"<?cs /if ?><?cs
-   if:link.type ?> type="<?cs var:link.type ?>"<?cs /if ?> /><?cs
-  /each ?><?cs
- /each ?><style type="text/css"><?cs include:"site_css.cs" ?></style><?cs
- each:script = chrome.scripts ?>
- <script type="<?cs var:script.type ?>" src="<?cs var:script.href ?>"></script><?cs
- /each ?>
-</head>
-<body>
-<?cs include "site_header.cs" ?>
-<div id="banner">
-
-<div id="header"><?cs
- if:chrome.logo.src ?><a id="logo" href="<?cs
-  var:chrome.logo.link ?>"><img src="<?cs var:chrome.logo.src ?>"<?cs
-  if:chrome.logo.width ?> width="<?cs var:chrome.logo.width ?>"<?cs /if ?><?cs
-  if:chrome.logo.height ?> height="<?cs var:chrome.logo.height ?>"<?cs
-  /if ?> alt="<?cs var:chrome.logo.alt ?>" /></a><hr /><?cs
- elif:project.name_encoded ?><h1><a href="<?cs var:chrome.logo.link ?>"><?cs
-  var:project.name_encoded ?></a></h1><?cs
- /if ?></div>
-
-<form id="search" action="<?cs var:trac.href.search ?>" method="get">
- <?cs if:trac.acl.SEARCH_VIEW ?><div>
-  <label for="proj-search">Search:</label>
-  <input type="text" id="proj-search" name="q" size="10" accesskey="f" value="" />
-  <input type="submit" value="Search" />
- </div><?cs /if ?>
-</form>
-
-<?cs def:nav(items) ?><?cs
- if:len(items) ?><ul><?cs
-  set:idx = 0 ?><?cs
-  set:max = len(items) - 1 ?><?cs
-  each:item = items ?><?cs
-   set:first = idx == 0 ?><?cs
-   set:last = idx == max ?><li<?cs
-   if:first || last || item.active ?> class="<?cs
-    if:item.active ?>active<?cs /if ?><?cs
-    if:item.active && (first || last) ?> <?cs /if ?><?cs
-    if:first ?>first<?cs /if ?><?cs
-    if:(item.active || first) && last ?> <?cs /if ?><?cs
-    if:last ?>last<?cs /if ?>"<?cs
-   /if ?>><?cs var:item ?></li><?cs
-   set:idx = idx + 1 ?><?cs
-  /each ?></ul><?cs
- /if ?><?cs
-/def ?>
-
-<div id="metanav" class="nav"><?cs call:nav(chrome.nav.metanav) ?></div>
-</div>
-
-<div id="mainnav" class="nav"><?cs call:nav(chrome.nav.mainnav) ?></div>
-<div id="main">

trac/templates/macros.cs

-<?cs def:hdf_select(options, name, selected, optional) ?>
- <select size="1" id="<?cs var:name ?>" name="<?cs var:name ?>"><?cs
-  if:optional ?><option></option><?cs /if ?><?cs
-  each:option = options ?>
-   <option<?cs if:option == selected ?> selected="selected"<?cs /if ?>><?cs 
-     var:option ?></option><?cs
-  /each ?>
- </select><?cs
-/def?><?cs
-
-def:labelled_hdf_select(label, options, name, selected, optional) ?><?cs 
- if:len(options) > #0 ?>
-  <label for="<?cs var:name ?>"><?cs var:label ?></label><?cs
-   call:hdf_select(options, name, selected, optional) ?>
-  </label>
-  <br /><?cs
- /if ?><?cs
-/def ?><?cs
-
-def:browser_path_links(path, file) ?><?cs
- set:first = #1 ?><?cs
-  each:part = path ?><?cs
-   set:last = name(part) == len(path) - #1 ?><a<?cs 
-   if:first ?> class="first" title="Go to root directory"<?cs 
-    set:first = #0 ?><?cs 
-   else ?> title="View <?cs var:part.name ?>"<?cs
-   /if ?> href="<?cs var:part.href ?>"><?cs var:part.name ?></a><?cs
-   if:!last ?><span class="sep">/</span><?cs /if ?><?cs 
- /each ?><?cs
-/def ?><?cs
-
-def:diff_line_class(block, line) ?><?cs
- set:first = name(line) == 0 ?><?cs
- set:last = name(line) + 1 == len(block.lines) ?><?cs
- if:first || last ?> class="<?cs
-  if:first ?>first<?cs /if ?><?cs
-  if:first && last ?> <?cs /if ?><?cs
-  if:last ?>last<?cs /if ?>"<?cs
- /if ?><?cs
-/def ?><?cs
-
-def:diff_display(diff, style) ?><?cs
- if:style == 'sidebyside' ?><?cs
-  each:block = diff ?><?cs
-   if:block.type == 'unmod' ?><tbody><?cs
-    each:line = block.base.lines ?><tr><th><?cs
-     var:#block.base.offset + name(line) + 1 ?></th><td class="l"><span><?cs
-     var:line ?></span>&nbsp;</td><th><?cs
-     var:#block.changed.offset + name(line) + 1 ?></th><td class="r"><span><?cs
-     var:block.changed.lines[name(line)] ?></span>&nbsp;</td></tr><?cs
-    /each ?></tbody><?cs
-   elif:block.type == 'mod' ?><tbody class="mod"><?cs
-    if:len(block.base.lines) >= len(block.changed.lines) ?><?cs
-     each:line = block.base.lines ?><tr><th><?cs
-      var:#block.base.offset + name(line) + 1 ?></th><td class="l"><?cs
-      var:line ?>&nbsp;</td><?cs
-      if:len(block.changed.lines) >= name(line) + 1 ?><?cs
-       each:changedline = block.changed.lines ?><?cs
-        if:name(changedline) == name(line) ?><th><?cs
-         var:#block.changed.offset + name(changedline) + 1 ?></th><td class="r"><?cs
-         var:changedline ?>&nbsp;</td><?cs
-        /if ?><?cs
-       /each ?><?cs
-      else ?><th>&nbsp;</th><td class="r">&nbsp;</td><?cs
-      /if ?></tr><?cs
-     /each ?><?cs
-    else ?><?cs
-     each:line = block.changed.lines ?><tr><?cs
-      if:len(block.base.lines) >= name(line) + 1 ?><?cs
-       each:baseline = block.base.lines ?><?cs
-        if:name(baseline) == name(line) ?><th><?cs
-         var:#block.base.offset + name(baseline) + 1 ?></th><td class="l"><?cs
-         var:baseline ?>&nbsp;</td><?cs
-        /if ?><?cs
-       /each ?><?cs
-      else ?><th>&nbsp;</th><td class="l">&nbsp;</td><?cs
-      /if ?>
-      <th><?cs var:#block.changed.offset + name(line) + 1 ?></th>
-      <td class="r"><?cs var:line ?>&nbsp;</td></tr><?cs
-     /each ?><?cs
-    /if ?></tbody><?cs
-   elif:block.type == 'add' ?><tbody class="add"><?cs
-    each:line = block.changed.lines ?><tr><th>&nbsp;</th><td class="l">&nbsp;</td><th><?cs
-     var:#block.changed.offset + name(line) + 1 ?></th><td class="r"><ins><?cs
-     var:line ?></ins>&nbsp;</td></tr><?cs
-    /each ?><?cs
-   elif:block.type == 'rem' ?><tbody class="rem"><?cs
-    each:line = block.base.lines ?><tr><th><?cs
-     var:#block.base.offset + name(line) + 1 ?></th><td class="l"><del><?cs
-     var:line ?></del>&nbsp;</td><th>&nbsp;</th><td class="r">&nbsp;</td></tr><?cs
-    /each ?><?cs
-   /if ?></tbody><?cs
-  /each ?><?cs
- else ?><?cs
-  each:block = diff ?><?cs
-   if:block.type == 'unmod' ?><tbody><?cs
-    each:line = block.base.lines ?><tr><th><?cs
-     var:#block.base.offset + name(line) + #1 ?></th><th><?cs
-     var:#block.changed.offset + name(line) + #1 ?></th><td class="l"><span><?cs
-     var:line ?></span>&nbsp;</td></tr><?cs
-    /each ?></tbody><?cs
-   elif:block.type == 'mod' ?><tbody class="mod"><?cs
-    each:line = block.base.lines ?><tr<?cs
-     if:name(line) == 0 ?> class="first"<?cs /if ?>><th><?cs
-     var:#block.base.offset + name(line) + #1 ?></th><th>&nbsp;</th><td class="l"><?cs
-     var:line ?>&nbsp;</td></tr><?cs
-    /each ?><?cs
-    each:line = block.changed.lines ?><tr<?cs
-     if:name(line) + 1 == len(block.changed.lines) ?> class="last"<?cs /if ?>><th>&nbsp;</th><th><?cs
-     var:#block.changed.offset + name(line) + #1 ?></th><td class="r"><?cs
-     var:line ?>&nbsp;</td></tr><?cs
-    /each ?></tbody><?cs
-   elif:block.type == 'add' ?><tbody class="add"><?cs
-    each:line = block.changed.lines ?><tr<?cs
-     call:diff_line_class(block.changed, line) ?>><th>&nbsp;</th><th><?cs
-     var:#block.changed.offset + name(line) + #1 ?></th><td class="r"><ins><?cs
-     var:line ?></ins>&nbsp;</td></tr><?cs
-    /each ?></tbody><?cs
-   elif:block.type == 'rem' ?><tbody class="rem"><?cs
-    each:line = block.base.lines ?><tr<?cs
-     call:diff_line_class(block.base, line) ?>><th><?cs
-     var:#block.base.offset + name(line) + 1 ?></th><th>&nbsp;</th><td class="l"><del><?cs
-     var:line ?></del>&nbsp;</td></tr><?cs
-    /each ?></tbody><?cs
-   /if ?><?cs
-  /each ?><?cs
- /if ?><?cs
-/def ?><?cs
-
-def:ticket_custom_props(ticket) ?><?cs
- each c=ticket.custom ?>
-  <div class="field custom_<?cs var c.name ?>"><?cs
-   if c.type == 'text' ?>
-    <label>
-     <?cs alt c.label ?><?cs var c.name ?><?cs /alt ?>:
-     <input type="text" name="custom_<?cs var c.name ?>" value="<?cs var c.value ?>" />
-    </label><?cs
-   elif c.type == 'textarea' ?>
-    <label>
-     <?cs alt c.label ?><?cs var c.name ?><?cs /alt ?>:<br />
-     <textarea cols="<?cs alt c.width ?>60<?cs /alt ?>" rows="<?cs
-       alt c.height ?>12<?cs /alt ?>" name="custom_<?cs var c.name ?>"><?cs
-       var c.value ?></textarea>
-    </label><?cs
-   elif c.type == 'checkbox' ?>
-    <input type="hidden" name="checkbox_<?cs var c.name ?>" />
-    <label>
-     <input type="checkbox" name="custom_<?cs var c.name ?>" value="1"<?cs
-       if c.selected ?> checked="checked"<?cs /if ?> />
-     <?cs alt c.label ?><?cs var c.name ?><?cs /alt ?>
-    </label><?cs
-   elif c.type == 'select' ?>
-    <label>
-     <?cs alt c.label ?><?cs var c.name ?><?cs /alt ?>:
-     <select name="custom_<?cs var c.name ?>"><?cs each v = c.option ?>
-      <option<?cs if v.selected ?> selected="selected"<?cs /if ?>><?cs
-        var v ?></option><?cs /each ?>
-     </select>
-    </label><?cs
-   elif c.type == 'radio' ?>
-    <fieldset class="radio">
-     <legend><?cs alt c.label ?><?cs var c.name ?><?cs /alt ?>:</legend><?cs
-     each v = c.option ?>
-      <label><input type="radio" name="custom_<?cs var c.name ?>" value="<?cs
-         var v ?>"<?cs if v.selected ?> checked="checked"<?cs /if ?> /> <?cs
-         var v ?></label><?cs
-     /each ?>
-    </fieldset><?cs
-   /if ?>
-  </div><?cs
- /each ?><?cs
-/def ?><?cs 
-
-def:list_of_attachments(attachments, attach_href) ?>
-<h2>Attachments</h2><?cs
- if:len(attachments) ?><div id="attachments">
-  <dl class="attachments"><?cs each:attachment = attachments ?>
-   <dt><a href="<?cs var:attachment.href ?>" title="View attachment"><?cs
-   var:attachment.filename ?></a> (<?cs var:attachment.size ?>) - added by <em><?cs
-   var:attachment.author ?></em> on <?cs
-   var:attachment.time ?>.</dt><?cs
-   if:attachment.description ?>
-    <dd><?cs var:attachment.description ?></dd><?cs
-   /if ?><?cs
-  /each ?></dl><?cs
- /if ?><?cs
- if:attach_href ?>
-  <form method="get" action="<?cs var:attach_href ?>"><div>
-   <input type="hidden" name="action" value="new" />
-   <input type="submit" value="Attach File" />
-  </div></form><?cs
- /if ?><?cs if:len(attachments) ?></div><?cs /if ?><?cs
-/def ?><?cs
-
-def:plural(base, count) ?><?cs
- var:base ?><?cs if:count != 1 ?>s<?cs /if ?><?cs
-/def ?>

trac/util/html.py

 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://trac.edgewall.org/log/.
 
+from HTMLParser import HTMLParser
 import re
 
 from genshi import Markup, escape, unescape
 from genshi.builder import Element, ElementFactory, Fragment
 from genshi.filters.html import HTMLSanitizer
 
-__all__ = ['escape', 'unescape', 'html', 'plaintext', 'TracHTMLSanitizer']
+__all__ = ['escape', 'unescape', 'html', 'plaintext', 'TracHTMLSanitizer',
+           'Deuglifier', 'FormTokenInjector']
 
 
 class TracHTMLSanitizer(HTMLSanitizer):
                 return '<span class="code-%s">' % mtype
 
 
+class FormTokenInjector(HTMLParser):
+    """Identify and protect forms from CSRF attacks.
+
+    This filter works by adding a input type=hidden field to POST forms.
+    """
+    def __init__(self, form_token, out):
+        HTMLParser.__init__(self)
+        self.out = out
+        self.token = form_token
+
+    def handle_starttag(self, tag, attrs):
+        self.out.write(self.get_starttag_text())
+        if tag.lower() == 'form':
+            for name, value in attrs:
+                if name.lower() == 'method' and value.lower() == 'post':
+                    self.out.write('<input type="hidden" name="__FORM_TOKEN"'
+                                   ' value="%s"/>' % self.token)
+                    break
+                    
+    def handle_startendtag(self, tag, attrs):
+        self.out.write(self.get_starttag_text())
+        
+    def handle_charref(self, name):
+        self.out.write('&#%s;' % name)
+
+    def handle_entityref(self, name):
+        self.out.write('&%s;' % name)
+
+    def handle_comment(self, data):
+        self.out.write('<!--%s-->' % data)
+
+    def handle_decl(self, data):
+        self.out.write('<!%s>' % data)
+
+    def handle_pi(self, data):
+        self.out.write('<?%s?>' % data)
+
+    def handle_data(self, data):
+        self.out.write(data)
+
+    def handle_endtag(self, tag):
+        self.out.write('</' + tag + '>')
+
+
 class TransposingElementFactory(ElementFactory):
     """A `genshi.builder.ElementFactory` which applies `func` to the
     named attributes before creating a `genshi.builder.Element`.
         self.end_headers()
         raise RequestDone
 
-    def display(self, template, content_type='text/html', status=200):
-        """Render the response using the ClearSilver template given by the
-        `template` parameter, which can be either the name of the template
-        file, or an already parsed `neo_cs.CS` object.
-        """
-        assert self.hdf, \
-               'HDF dataset not available. Check your clearsilver installation'
-        if self.args.has_key('hdfdump'):
-            # FIXME: the administrator should probably be able to disable HDF
-            #        dumps
-            self.perm.require('TRAC_ADMIN')
-            content_type = 'text/plain'
-            data = str(self.hdf)
-        else:
-            try:
-                form_token = self.form_token
-            except AttributeError:
-                form_token = None
-            data = self.hdf.render(template, form_token)
-
-        self.send(data, content_type, status)
-
     def send(self, content, content_type='text/html', status=200):
         self.send_response(status)
         self.send_header('Cache-Control', 'must-revalidate')

trac/web/clearsilver.py

-# -*- coding: utf-8 -*-
-#
-# Copyright (C)2005-2009 Edgewall Software
-# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
-# All rights reserved.
-#
-# This software is licensed as described in the file COPYING, which
-# you should have received as part of this distribution. The terms
-# are also available at http://trac.edgewall.org/wiki/TracLicense.
-#
-# This software consists of voluntary contributions made by many
-# individuals. For the exact contribution history, see the revision
-# history and logs, available at http://trac.edgewall.org/log/.
-#
-# Author: Christopher Lenz <cmlenz@gmx.de>
-
-from HTMLParser import HTMLParser
-
-from trac.core import TracError
-from trac.util.html import Markup, Fragment, escape
-from trac.util.text import to_unicode
-
-
-class HDFWrapper:
-    """
-    Convenience layer on top of the low-level ClearSilver python bindings
-    for HDF manipulation. This class makes the HDF look and behave more
-    like a standard Python dict.
-
-    >>> hdf = HDFWrapper()
-    >>> hdf['trac.url'] = 'http://trac.edgewall.org/'
-    >>> hdf['trac.version'] = '1.0'
-    >>> print hdf
-    trac {
-      url = http://trac.edgewall.org/
-      version = 1.0
-    }
-
-    HDFWrapper can also assign Python lists and dicts to HDF nodes,
-    automatically expanding them into the corresponding HDF structure.
-
-    A dictionary is mapped to a HDF node with named children:
-
-    >>> hdf = HDFWrapper()
-    >>> hdf['item'] = {'name': 'An item', 'value': '0'}
-    >>> print hdf
-    item {
-      name = An item
-      value = 0
-    }
-
-    A sequence is mapped to a HDF node with children whose names are
-    the indexes of the elements:
-
-    >>> hdf = HDFWrapper()
-    >>> hdf['items'] = ['Item 1', 'Item 2']
-    >>> print hdf
-    items {
-      0 = Item 1
-      1 = Item 2
-    }
-
-    Simple values can also be easily retrieved using the same syntax.
-
-    >>> hdf = HDFWrapper()
-    >>> hdf['time'] = 42
-    >>> hdf['time']
-    u'42'
-    >>> hdf['name'] = 'Foo'
-    >>> hdf['name']
-    u'Foo'
-
-    An attempt to retrieve a value that hasn't been set will raise a KeyError,
-    just like a standard dictionary:
-
-    >>> hdf['undef']
-    Traceback (most recent call last):
-        ...
-    KeyError: 'undef'
-    
-    It may be preferable to return a default value if the given key does not exit.
-    It will return 'None' when the specified key is not present:
-
-    >>> hdf.get('time')
-    u'42'
-    >>> hdf.get('undef')
-
-    A second argument may be passed to specify the default return value:
-
-    >>> hdf.get('time', 'Undefined Key')
-    u'42'
-    >>> hdf.get('undef', 'Undefined Key')
-    'Undefined Key'
-
-    The 'in' and 'not in' operators can be used to test whether the HDF contains
-    a value with a given name.
-
-    >>> 'name' in hdf
-    True
-    >>> 'undef' in hdf
-    False
-
-    has_key() performs the same function:
-
-    >>> hdf.has_key('name')
-    True
-    >>> hdf.has_key('undef')
-    False
-    """
-
-    has_clearsilver = None
-    
-    def __init__(self, loadpaths=[]):
-        """Create a new HDF dataset.
-        
-        The loadpaths parameter can be used to specify a sequence of paths under
-        which ClearSilver will search for template files:
-
-        >>> hdf = HDFWrapper(loadpaths=['/etc/templates',
-        ...                             '/home/john/templates'])
-        >>> print hdf
-        hdf {
-          loadpaths {
-            0 = /etc/templates
-            1 = /home/john/templates
-          }
-        }
-        """
-        try:
-            import neo_cgi
-            # The following line is needed so that ClearSilver can be loaded when
-            # we are being run in multiple interpreters under mod_python
-            neo_cgi.update()
-            import neo_util
-            self.hdf = neo_util.HDF()
-            self.has_clearsilver = True
-        except ImportError:
-            self.has_clearsilver = False
-        
-        self['hdf.loadpaths'] = loadpaths
-
-    def __repr__(self):
-        return '<HDFWrapper 0x%x>' % id(self)
-
-    def __nonzero__(self):
-        return self.has_clearsilver
-
-    def __getattr__(self, name):
-        # For backwards compatibility, expose the interface of the underlying HDF
-        # object
-        if self.has_clearsilver:
-            return getattr(self.hdf, name)
-        else:
-            return None
-
-    def __contains__(self, name):
-        return self.hdf.getObj(str(name)) != None
-    has_key = __contains__
-
-    def get(self, name, default=None):
-        value = self.hdf.getValue(str(name), '<<NONE>>')
-        if value == '<<NONE>>':
-            return default
-        return value.decode('utf-8')
-
-    def __getitem__(self, name):
-        value = self.get(name, None)
-        if value == None:
-            raise KeyError, name
-        return value
-
-    def __setitem__(self, name, value):
-        """Add data to the HDF dataset.
-        
-        The `name` parameter is the path of the node in dotted syntax. The
-        `value` parameter can be a simple value such as a string or number, but
-        also data structures such as dicts and lists.
-
-        >>> hdf = HDFWrapper()
-
-        Adding a simple value results in that value being inserted into the HDF
-        after being converted to a string.
-
-        >>> hdf['test.num'] = 42
-        >>> hdf['test.num']
-        u'42'
-        >>> hdf['test.str'] = 'foo'
-        >>> hdf['test.str']
-        u'foo'
-
-        The boolean literals `True` and `False` are converted to there integer
-        representation before being added:
-
-        >>> hdf['test.true'] = True
-        >>> hdf['test.true']
-        u'1'
-        >>> hdf['test.false'] = False
-        >>> hdf['test.false']
-        u'0'
-
-        If value is `None`, nothing is added to the HDF:
-
-        >>> hdf['test.true'] = None
-        >>> hdf['test.none']
-        Traceback (most recent call last):
-            ...
-        KeyError: 'test.none'
-        """
-        self.set_value(name, value, True)
-        
-    def set_unescaped(self, name, value):
-        """
-        Add data to the HDF dataset.
-        
-        This method works the same way as `__setitem__` except that `value`
-        is not escaped if it is a string.
-        """
-        self.set_value(name, value, False)
-        
-    def set_value(self, name, value, do_escape=True):
-        """
-        Add data to the HDF dataset.
-        """
-        if not self.has_clearsilver:
-            return
-        def set_unicode(prefix, value):
-            self.hdf.setValue(prefix.encode('utf-8'), value.encode('utf-8'))
-        def set_str(prefix, value):
-            self.hdf.setValue(prefix.encode('utf-8'), str(value))
-            
-        def add_value(prefix, value):
-            if value is None:
-                return
-            if value in (True, False):
-                set_str(prefix, int(value))
-            elif isinstance(value, (Markup, Fragment)):
-                set_unicode(prefix, unicode(value))
-            elif isinstance(value, str):
-                if do_escape:
-                    # Assume UTF-8 here, for backward compatibility reasons
-                    set_unicode(prefix, escape(to_unicode(value)))
-                else:
-                    set_str(prefix, value)
-            elif isinstance(value, unicode):
-                if do_escape:
-                    set_unicode(prefix, escape(value))
-                else:
-                    set_unicode(prefix, value)
-            elif isinstance(value, dict):
-                for k in value.keys():
-                    add_value('%s.%s' % (prefix, to_unicode(k)), value[k])
-            else:
-                if hasattr(value, '__iter__') or \
-                        isinstance(value, (list, tuple)):
-                    for idx, item in enumerate(value):
-                        add_value('%s.%d' % (prefix, idx), item)
-                else:
-                    set_str(prefix, value)
-        add_value(name, value)
-
-    def __str__(self):
-        from StringIO import StringIO
-        buf = StringIO()
-        def hdf_tree_walk(node, prefix=''):
-            while node:
-                name = node.name() or ''
-                buf.write('%s%s' % (prefix, name))
-                value = node.value()
-                if value or not node.child():
-                    if value.find('\n') == -1:
-                        buf.write(' = %s' % value)
-                    else:
-                        buf.write(' = << EOM\n%s\nEOM' % value)
-                if node.child():
-                    buf.write(' {\n')
-                    hdf_tree_walk(node.child(), prefix + '  ')
-                    buf.write('%s}\n' % prefix)
-                else:
-                    buf.write('\n')
-                node = node.next()
-        hdf_tree_walk(self.hdf.child())
-        return buf.getvalue().strip()
-
-    def parse(self, string):
-        """Parse the given string as template text, and returns a neo_cs.CS
-        object.
-        """
-        import neo_cs
-        cs = neo_cs.CS(self.hdf)
-        cs.parseStr(string)
-        return cs
-
-    def render(self, template, form_token=None):
-        """Render the HDF using the given template.
-
-        The template parameter can be either an already parse neo_cs.CS
-        object, or a string. In the latter case it is interpreted as name of the
-        template file.
-        """
-        if isinstance(template, basestring):
-            filename = template
-            try:
-                import neo_cs
-            except ImportError:
-                raise TracError("You're using a plugin which requires "
-                                "the Clearsilver template engine and "
-                                "Clearsilver is not installed. "
-                                "Either disable that plugin or install "
-                                "Clearsilver.")
-            template = neo_cs.CS(self.hdf)
-            template.parseFile(filename)
-
-        if form_token:
-            from cStringIO import StringIO
-            out = StringIO()
-            injector = FormTokenInjector(form_token, out)
-            injector.feed(template.render())
-            return out.getvalue()
-        else:
-            return template.render()
-
-
-class FormTokenInjector(HTMLParser):
-    """Identify and protect forms from CSRF attacks
-
-    This filter works by adding a input type=hidden field to POST forms.
-    """
-    def __init__(self, form_token, out):
-        HTMLParser.__init__(self)
-        self.out = out
-        self.token = form_token
-
-    def handle_starttag(self, tag, attrs):
-        self.out.write(self.get_starttag_text())
-        if tag.lower() == 'form':
-            for name, value in attrs:
-                if name.lower() == 'method' and value.lower() == 'post':
-                    self.out.write('<input type="hidden" name="__FORM_TOKEN"'
-                                   ' value="%s"/>' % self.token)
-                    break
-                    
-    def handle_startendtag(self, tag, attrs):
-        self.out.write(self.get_starttag_text())
-        
-    def handle_charref(self, name):
-        self.out.write('&#%s;' % name)
-
-    def handle_entityref(self, name):
-        self.out.write('&%s;' % name)
-
-    def handle_comment(self, data):
-        self.out.write('<!--%s-->' % data)
-
-    def handle_decl(self, data):
-        self.out.write('<!%s>' % data)
-
-    def handle_pi(self, data):
-        self.out.write('<?%s?>' % data)
-
-    def handle_data(self, data):
-        self.out.write(data)
-
-    def handle_endtag(self, tag):
-        self.out.write('</' + tag + '>')
-
-
-if __name__ == '__main__':
-    import doctest, sys
-    doctest.testmod(sys.modules[__name__])
                                   safefmt, tag_
 from trac.web.api import *
 from trac.web.chrome import Chrome
-from trac.web.clearsilver import HDFWrapper
 from trac.web.href import Href
 from trac.web.session import Session
 
         return self
 
 
-def populate_hdf(hdf, env, req=None):
-    """Populate the HDF data set with various information, such as common URLs,
-    project information and request-related information.
-    """
-    # FIXME: do we really have req==None at times?
-    hdf['trac'] = {
-        'version': TRAC_VERSION,
-        'time': format_datetime(),
-        'time.gmt': http_date()
-    }
-    hdf['project'] = {
-        'shortname': os.path.basename(env.path),
-        'name': env.project_name,
-        'name_encoded': env.project_name,
-        'descr': env.project_description,
-        'footer': Markup(env.project_footer),
-        'url': env.project_url
-    }
-
-    if req:
-        hdf['trac.href'] = {
-            'wiki': req.href.wiki(),
-            'browser': req.href.browser('/'),
-            'timeline': req.href.timeline(),
-            'roadmap': req.href.roadmap(),
-            'milestone': req.href.milestone(None),
-            'report': req.href.report(),
-            'query': req.href.query(),
-            'newticket': req.href.newticket(),
-            'search': req.href.search(),
-            'about': req.href.about(),
-            'about_config': req.href.about('config'),
-            'login': req.href.login(),
-            'logout': req.href.logout(),
-            'settings': req.href.settings(),
-            'homepage': 'http://trac.edgewall.org/'
-        }
-
-        hdf['base_url'] = req.base_url
-        hdf['base_host'] = req.base_url[:req.base_url.rfind(req.base_path)]
-        hdf['cgi_location'] = req.base_path
-        hdf['trac.authname'] = req.authname
-
-        if req.perm:
-            for action in req.perm.permissions():
-                hdf['trac.acl.' + action] = True
-
-        for arg in [k for k in req.args.keys() if k]:
-            if isinstance(req.args[arg], (list, tuple)):
-                hdf['args.%s' % arg] = [v for v in req.args[arg]]
-            elif isinstance(req.args[arg], basestring):
-                hdf['args.%s' % arg] = req.args[arg]
-            # others are file uploads
-
-
 class RequestDispatcher(Component):
     """Web request dispatcher.
     
         req.callbacks.update({
             'authname': self.authenticate,
             'chrome': chrome.prepare_request,
-            'hdf': self._get_hdf,
             'perm': self._get_perm,
             'session': self._get_session,
             'locale': self._get_locale,
                 # Process the request and render the template
                 resp = chosen_handler.process_request(req)
                 if resp:
-                    if len(resp) == 2: # Clearsilver
-                        chrome.populate_hdf(req)
-                        template, content_type = \
-                                  self._post_process_request(req, *resp)
-                        # Give the session a chance to persist changes
-                        req.session.save()
-                        req.display(template, content_type or 'text/html')
-                    else: # Genshi
-                        template, data, content_type = \
-                                  self._post_process_request(req, *resp)
-                        if 'hdfdump' in req.args:
-                            req.perm.require('TRAC_ADMIN')
-                            # debugging helper - no need to render first
-                            out = StringIO()
-                            pprint(data, out)
-                            req.send(out.getvalue(), 'text/plain')
-                        else:
-                            output = chrome.render_template(req, template,
-                                                            data,
-                                                            content_type)
-                            # Give the session a chance to persist changes
-                            req.session.save()
-                            req.send(output, content_type or 'text/html')
+                    if len(resp) == 2: # old Clearsilver template and HDF data
+                        self.log.error("Clearsilver template are no longer "
+                                       "supported (%s)", resp[0])
+                        raise TracError(
+                            _("Clearsilver templates are no longer supported, "
+                              "please contact your Trac administrator."))
+                    # Genshi
+                    template, data, content_type = \
+                              self._post_process_request(req, *resp)
+                    if 'hdfdump' in req.args:
+                        req.perm.require('TRAC_ADMIN')
+                        # debugging helper - no need to render first
+                        out = StringIO()
+                        pprint(data, out)
+                        req.send(out.getvalue(), 'text/plain')
+
+                    output = chrome.render_template(req, template, data,
+                                                    content_type)
+                    # Give the session a chance to persist changes
+                    req.session.save()
+                    req.send(output, content_type or 'text/html')
                 else:
                     self._post_process_request(req)
             except RequestDone:
 
     # Internal methods
 
-    def _get_hdf(self, req):
-        hdf = HDFWrapper(loadpaths=Chrome(self.env).get_all_templates_dirs())
-        populate_hdf(hdf, self.env, req)
-        return hdf
-
     def _get_perm(self, req):
         if isinstance(req.session, FakeSession):
             return FakePerm()
     req = Request(environ, start_response)
 
     loadpaths = [pkg_resources.resource_filename('trac', 'templates')]
-    use_clearsilver = False
     if req.environ.get('trac.env_index_template'):
         tmpl_path, template = os.path.split(req.environ['trac.env_index_template'])
         loadpaths.insert(0, tmpl_path)
-        use_clearsilver = template.endswith('.cs') # assume Clearsilver
-        if use_clearsilver:
-            req.hdf = HDFWrapper(loadpaths) # keep that for custom .cs templates
     else:
         template = 'index.html'
 
         for pair in req.environ['trac.template_vars'].split(','):
             key, val = pair.split('=')
             data[key] = val
-            if use_clearsilver:
-                req.hdf[key] = val
     try:
         href = Href(req.base_path)
         projects = []
         projects.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower()))
 
         data['projects'] = projects
-        if use_clearsilver:
-            req.hdf['projects'] = projects
-            req.display(template)
 
         loader = TemplateLoader(loadpaths, variable_lookup='lenient',
                                 default_encoding='utf-8')

trac/web/tests/__init__.py

 from trac.web.tests import api, auth, cgi_frontend, chrome, href, session, \
                            wikisyntax, main
 
-try:
-    import neo_cgi
-    from trac.web.tests import clearsilver
-except ImportError:
-    clearsilver = None
-
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(api.suite())
     suite.addTest(auth.suite())
     suite.addTest(cgi_frontend.suite())
     suite.addTest(chrome.suite())
-    if clearsilver:
-        suite.addTest(clearsilver.suite())
     suite.addTest(href.suite())
     suite.addTest(session.suite())
     suite.addTest(wikisyntax.suite())

trac/web/tests/clearsilver.py

-from trac.web import clearsilver
-
-import unittest
-
-
-def suite():
-    try:
-        from doctest import DocTestSuite
-        return DocTestSuite(clearsilver)
-    except ImportError:
-        import sys
-        print >> sys.stderr, "WARNING: DocTestSuite required to run these " \
-                             "tests"
-    return unittest.TestSuite()
-
-if __name__ == '__main__':
-    runner = unittest.TextTestRunner()
-    runner.run(suite())

wiki-macros/README

-Support for HDF-based macros has been dropped starting with Trac 0.11
-as with the switch to the Genshi templating engine, the `hdf` data
-prepared for the Clearsilver engine used upto Trac 0.10 has simply
-disappeared.
-
-This means that the old-style macros installed in the `wiki-macros` folder,
-i.e. the Python source files containing the simple function:
-
-  def execute(hdf, txt, env):
-    ...
-
-must be rewritten into new-style macros,
-i.e. plugins implementing the IWikiMacroProvider interface.
-
-This is not necessarily a complex task, given that:
- - the plugin can be a "single file plugin" which doesn't need to be
-   configured to use setuptools. Creating a single .py source file
-   and dropping it at the right place (the `plugins` folder next to 
-   the old `wiki-macros` folder) also works;
- - instead of implementing the IWikiMacroProvider directly, 
-   inheriting from the WikiMacroBase class also works well and is
-   a bit more convenient.
-
-Note that not only is this not more complex, but it's also much more
-powerful, as the macro can now access the Wiki `formatter` object,
-and through it, all the other objects that are meaningful in the
-context of the Wiki text containing the macro call.
-
-You can see how the examples for the old-style macros have been 
-converted to the new-style macros (single file plugins), in the
-`sample-plugins/HelloWorld.py` and `sample-plugins/Timestamp.py` files.
-
-Note that the TracGuideToc macro has been integrated in the main
-source code base (in `trac/wiki/macros.py`).