| 1 | |
| 2 | |
| 3 | == Interface IWikiSyntaxProvider == |
| 4 | === File: trac/wiki/api.py === |
| 5 | The IWikiSyntaxProvider is an Interface you could use to write plugins that allow you to bring your own wiki syntax into trac. |
| 6 | Like the e.g. #5 to see Ticket with id 5. Or the e.g {1} way to see report number 1. |
| 7 | |
| 8 | Here is the interface description from IWikiSyntaxProvider taken from the source file trac/wiki/api.py |
| 9 | |
| 10 | {{{ |
| 11 | #!python |
| 12 | class IWikiSyntaxProvider(Interface): |
| 13 | |
| 14 | def get_wiki_syntax(): |
| 15 | """Return an iterable that provides additional wiki syntax. |
| 16 | |
| 17 | Additional wiki syntax correspond to a pair of (regexp, cb), |
| 18 | the `regexp` for the additional syntax and the callback `cb` |
| 19 | which will be called if there's a match. |
| 20 | That function is of the form cb(formatter, ns, match). |
| 21 | """ |
| 22 | |
| 23 | def get_link_resolvers(): |
| 24 | """Return an iterable over (namespace, formatter) tuples. |
| 25 | |
| 26 | Each formatter should be a function of the form |
| 27 | fmt(formatter, ns, target, label), and should |
| 28 | return some HTML fragment. |
| 29 | The `label` is already HTML escaped, whereas the `target` is not. |
| 30 | """ |
| 31 | }}} |
| 32 | Ok, lets try to implement the following example wiki syntax: |
| 33 | |
| 34 | We want to be able to use something like ''s:w:someword'' to generate a link to a search in the wiki pages for ''someword''. |
| 35 | Searching in Tickets and Milestones should work equally by using ''s:t:someword'' and ''s:m:someword''. |
| 36 | We need to implement the method '''get_link_resolvers()''' to make that happen. |
| 37 | |
| 38 | I know you could also use the search:?q=searchword&wiki=on syntax, but i think its a good example for demonstration anyway. |
| 39 | |
| 40 | As you can see in the source above, there are just 2 methods you must implement to get your own wiki syntax up and running. |
| 41 | |
| 42 | The easiest one is '''get_link_resolvers()'''. |
| 43 | You need to return an iterable of tuples consisting of a string used as the prefix for a new linktype in trac and a callback, to be called when the prefix matches. In our example the string will be ''s'' shorthand for search. The method is really simple: |
| 44 | {{{ |
| 45 | #!python |
| 46 | def get_link_resolvers(self): |
| 47 | return [ ('s', self._format_search_link) ] |
| 48 | |
| 49 | }}} |
| 50 | |
| 51 | It just returns one tuple, but you could return more if you want. This tuple tells trac that it should call our '''_format_search_link()''' method every time it finds the string s:''anystring'' in some wiki content. |
| 52 | |
| 53 | The real work will be done in our next method: '''_format_search_link'''. This method is called anytime trac encounters one of our prefixes (inside wiki context), we registered through '''get_link_resolvers()'''. |
| 54 | {{{ |
| 55 | #!python |
| 56 | def _format_search_link(self, formatter, ns, target, label): |
| 57 | self.env.log.debug('formatter: %s ns: %s' % (formatter, ns)) |
| 58 | domains = {'w': 'wiki', 't': 'ticket', 'm': 'milestone'} |
| 59 | domain = 'wiki' |
| 60 | searchword = None |
| 61 | if ":" in target: |
| 62 | domain, searchword = target.split(':') |
| 63 | else: |
| 64 | searchword = target |
| 65 | domain = domains.get(domain, 'wiki') |
| 66 | return tag.a(label, href=formatter.href.search() + |
| 67 | '?q=' + searchword + '&' + domain + |
| 68 | '=on', title="Search for %s in %s(s)" % (searchword, domain)) |
| 69 | }}} |
| 70 | The method gets 4 Arguments: |
| 71 | * a formatter object. |
| 72 | * a namespace, this string is our prefix for the linktype, in this case ''s'' |
| 73 | * a target, this is the string which followed the namespace prefix of the link without the trailing '':'' after namespace. |
| 74 | * a label, this the full string of the link, e.g. ''s:searchword'' |
| 75 | |
| 76 | The return value should be some html, in our case it is a link to the specified search query. |
| 77 | The first thing the method does is splitting the target into the domain and searchword |
| 78 | {{{ |
| 79 | #!python |
| 80 | domain, searchword = target.split(':') |
| 81 | |
| 82 | }}} |
| 83 | |
| 84 | Then we return a valid link to a search page with our given searchquery. If there is no domain specified we use default to ''wiki'' and use the ''target'' string as ''searchword''. |
| 85 | |
| 86 | === Part two === |
| 87 | |
| 88 | In the second part we implement the following wiki syntax using the second interface method '''get_wiki_syntax()''': |
| 89 | |
| 90 | * ?t_searchword? Creates a link to a search for '''searchword''' in the Ticket domain. |
| 91 | * ?m_searchword? Creates a link to a search for '''searchword''' in the Milestone domain. |
| 92 | * ?w_searchword? Creates a link to a search for '''searchword''' in the wiki domain. |
| 93 | |
| 94 | You are not limited in creating new link types with this Interface but mostly you would use WikiMacros instead. |
| 95 | |
| 96 | We have to implement get_wiki_syntax() to be able to use regular expressions in our new wiki syntax. |
| 97 | {{{ |
| 98 | #!python |
| 99 | def get_wiki_syntax(self): |
| 100 | #group numbers dont work, why? Must use group names inside the regex. |
| 101 | yield ( r'\?(?P<domain>[tmw])_(?P<searchword>.+?)\?', self._format_regex_link) |
| 102 | |
| 103 | }}} |
| 104 | |
| 105 | All this method does is telling trac that whenever it encounters a hit with our given regular expression(s) (as in '''get_link_resolvers()''' you can have more than one) it should call the given callback method. |
| 106 | |
| 107 | == Attention == |
| 108 | Using group numbers instead of group names inside the regular expression didn't work here. '''Anyone have a clue?''' |
| 109 | |
| 110 | The given callback method must take 3 Arguments: |
| 111 | * a formatter object |
| 112 | * a namespace, this is full match of the regex as string, same as match.group(0) |
| 113 | * a match, this is a regex match object. |
| 114 | |
| 115 | Here is the callback method: |
| 116 | {{{ |
| 117 | #!python |
| 118 | def _format_regex_link(self, formatter, ns, match): |
| 119 | domains = {'w': 'wiki', 't': 'ticket', 'm': 'milestone'} |
| 120 | searchword = match.group('searchword') or 'test' |
| 121 | label= match.group(0) |
| 122 | domain= match.group('domain') |
| 123 | domain = domains.get(domain,'wiki') |
| 124 | return tag.a(label, href=formatter.href.search() + |
| 125 | '?q=' + searchword + '&' + domain + '=on', |
| 126 | title="Search for %s in %s(s)" % (searchword, domain)) |
| 127 | |
| 128 | }}} |
| 129 | |
| 130 | All it does is extracting the ''domain'' and ''searchword'' out of the match object and then returning a link to the search page. |
| 131 | |
| 132 | |
| 133 | |
| 134 | Here the full source for our little IWikiSyntaxProvider Test |
| 135 | {{{ |
| 136 | #!python |
| 137 | # encoding: utf-8 |
| 138 | """ |
| 139 | WikiSyntaxProviderTest.py |
| 140 | |
| 141 | Created by Karsten Fuhrmann on 2009-12-14. |
| 142 | Copyright (c) 2009 Karsten Fuhrmann (karsten_fuhrmann AT web.de) All rights reserved. |
| 143 | """ |
| 144 | |
| 145 | from trac.core import * |
| 146 | from trac.wiki import IWikiSyntaxProvider |
| 147 | from genshi.builder import tag |
| 148 | |
| 149 | class WikiSyntaxProviderTest(Component): |
| 150 | '''This is a test Component to demonstrate the use of the IWikiSyntaxProvider Interface of Trac.''' |
| 151 | implements(IWikiSyntaxProvider) |
| 152 | |
| 153 | # IWikiSyntaxProvider methods |
| 154 | |
| 155 | def get_link_resolvers(self): |
| 156 | return [ ('s', self._format_search_link) ] |
| 157 | |
| 158 | def get_wiki_syntax(self): |
| 159 | #group numbers dont work, why? Must use group names inside the regex. |
| 160 | yield ( r'\?(?P<domain>[tmw])_(?P<searchword>.+?)\?', self._format_regex_link) |
| 161 | |
| 162 | def _format_regex_link(self, formatter, ns, match): |
| 163 | self.env.log.debug('formatter: %s ns: %s' % (formatter, ns)) |
| 164 | domains = {'w': 'wiki', 't': 'ticket', 'm': 'milestone'} |
| 165 | searchword = match.group('searchword') or 'test' |
| 166 | label= match.group(0) |
| 167 | domain= match.group('domain') |
| 168 | domain = domains.get(domain,'wiki') |
| 169 | return tag.a(label, href=formatter.href.search() + |
| 170 | '?q=' + searchword + '&' + domain + |
| 171 | '=on', title="Search for %s in %s(s)" % (searchword, domain)) |
| 172 | |
| 173 | def _format_search_link(self, formatter, ns, target, label): |
| 174 | self.env.log.debug('formatter: %s ns: %s' % (formatter, ns)) |
| 175 | domains = {'w': 'wiki', 't': 'ticket', 'm': 'milestone'} |
| 176 | domain = 'wiki' |
| 177 | searchword = None |
| 178 | if ":" in target: |
| 179 | domain, searchword = target.split(':') |
| 180 | else: |
| 181 | searchword = target |
| 182 | domain = domains.get(domain, 'wiki') |
| 183 | return tag.a(label, href=formatter.href.search() + |
| 184 | '?q=' + searchword + '&' + domain + |
| 185 | '=on', title="Search for %s in %s(s)" % (searchword, domain)) |
| 186 | |
| 187 | }}} |