Edgewall Software

Ticket #2041: intertrac-for-trac-0.9.4pre-r2830.patch

File intertrac-for-trac-0.9.4pre-r2830.patch, 34.8 kB (added by cboos, 3 years ago)

Adds InterTrac and InterWiki on 0.9-stable (applies also on 0.9.3)

  • wiki-default/checkwiki.py

     
    1515# Pages to include in distribution 
    1616wiki_pages = [ 
    1717 "CamelCase", 
     18 "InterMapTxt", 
     19 "InterTrac", 
     20 "InterWiki", 
    1821 "RecentChanges", 
    1922 "TitleIndex", 
    2023 "TracAccessibility", 
  • trac/env.py

     
    7474        ComponentManager.__init__(self) 
    7575 
    7676        self.path = path 
     77        self.siblings = {} 
    7778        self.__cnx_pool = None 
    7879        if create: 
    7980            self.create(db_str) 
  • trac/ticket/api.py

     
    1919from trac import util 
    2020from trac.core import * 
    2121from trac.perm import IPermissionRequestor 
    22 from trac.wiki import IWikiSyntaxProvider 
     22from trac.wiki import IWikiSyntaxProvider, Formatter 
    2323from trac.Search import ISearchSource, query_to_sql, shorten_result 
    2424 
    2525 
     
    140140                ('ticket', self._format_link)] 
    141141 
    142142    def get_wiki_syntax(self): 
    143         yield (r"!?(?<!&)#\d+", # #123 but not &#123; (HTML entity) 
    144                lambda x, y, z: self._format_link(x, 'ticket', y[1:], y)) 
     143        yield ( 
     144            # matches #... but not &#... (HTML entity) 
     145            r"!?(?<!&)#" 
     146            # optional intertrac shorthand #T... + digits 
     147            r"(?P<it_ticket>%s)?\d+" % Formatter.INTERTRAC_SCHEME, 
     148            lambda x, y, z: self._format_link(x, 'ticket', y[1:], y, z)) 
    145149 
    146     def _format_link(self, formatter, ns, target, label): 
     150    def _format_link(self, formatter, ns, target, label, fullmatch=None): 
     151        intertrac = formatter.shorthand_intertrac_helper(ns, target, label, 
     152                                                         fullmatch) 
     153        if intertrac: 
     154            return intertrac 
    147155        cursor = formatter.db.cursor() 
    148156        cursor.execute("SELECT summary,status FROM ticket WHERE id=%s", 
    149157                       (target,)) 
  • trac/ticket/report.py

     
    2525from trac.perm import IPermissionRequestor 
    2626from trac.web import IRequestHandler 
    2727from trac.web.chrome import add_link, add_stylesheet, INavigationContributor 
    28 from trac.wiki import wiki_to_html, IWikiSyntaxProvider 
     28from trac.wiki import wiki_to_html, IWikiSyntaxProvider, Formatter 
    2929 
    3030 
    3131dynvars_re = re.compile('\$([A-Z]+)') 
     
    501501        yield ('report', self._format_link) 
    502502 
    503503    def get_wiki_syntax(self): 
    504         yield (r"!?\{\d+\}", lambda x, y, z: self._format_link(x, 'report', y[1:-1], y)) 
     504        yield (r"!?\{(?P<it_report>%s\s*)?\d+\}" % Formatter.INTERTRAC_SCHEME, 
     505               lambda x, y, z: self._format_link(x, 'report', y[1:-1], y, z)) 
    505506 
    506     def _format_link(self, formatter, ns, target, label): 
     507    def _format_link(self, formatter, ns, target, label, fullmatch=None): 
     508        intertrac = formatter.shorthand_intertrac_helper(ns, target, label, 
     509                                                         fullmatch) 
     510        if intertrac: 
     511            return intertrac 
    507512        report, args = target, '' 
    508513        if '?' in target: 
    509514            report, args = target.split('?') 
  • trac/versioncontrol/web_ui/changeset.py

     
    3030from trac.versioncontrol.diff import get_diff_options, hdf_diff, unified_diff 
    3131from trac.web import IRequestHandler 
    3232from trac.web.chrome import add_link, add_stylesheet, INavigationContributor 
    33 from trac.wiki import wiki_to_html, wiki_to_oneliner, IWikiSyntaxProvider 
     33from trac.wiki import wiki_to_html, wiki_to_oneliner, IWikiSyntaxProvider, \ 
     34                      Formatter 
    3435 
    3536 
    3637class ChangesetModule(Component): 
     
    353354    # IWikiSyntaxProvider methods 
    354355     
    355356    def get_wiki_syntax(self): 
    356         yield (r"!?\[\d+\]|(?:\b|!)r\d+\b(?!:\d)", 
    357                lambda x, y, z: self._format_link(x, 'changeset', 
    358                                                  y[0] == 'r' and y[1:] 
    359                                                  or y[1:-1], y)) 
     357        yield ( 
     358            # [...] form: start with optional intertrac: [T... or [trac ...  
     359            r"!?\[(?P<it_changeset>%s\s*)?" % Formatter.INTERTRAC_SCHEME + 
     360            #  digits 
     361            r"\d+\]|"    
     362            # r... form: allow r1 but not r1:2 (handled by the log syntax) 
     363            r"(?:\b|!)r\d+\b(?!:\d)", 
     364            lambda x, y, z: 
     365            self._format_link(x, 'changeset', 
     366                              y[0] == 'r' and y[1:] or y[1:-1], 
     367                              y, z)) 
    360368 
    361369    def get_link_resolvers(self): 
    362370        yield ('changeset', self._format_link) 
    363371 
    364     def _format_link(self, formatter, ns, rev, label): 
     372    def _format_link(self, formatter, ns, rev, label, fullmatch=None): 
     373        intertrac = formatter.shorthand_intertrac_helper(ns, rev, label, 
     374                                                         fullmatch) 
     375        if intertrac: 
     376            return intertrac 
    365377        cursor = formatter.db.cursor() 
    366378        cursor.execute('SELECT message FROM revision WHERE rev=%s', (rev,)) 
    367379        row = cursor.fetchone() 
  • trac/wiki/api.py

     
    187187    def get_wiki_syntax(self): 
    188188        ignore_missing = self.config.getbool('wiki', 'ignore_missing_pages') 
    189189        yield (r"!?(?<!/)\b[A-Z][a-z]+(?:[A-Z][a-z]*[a-z/])+" 
    190                 "(?:#[A-Za-z0-9]+)?(?=\Z|\s|[.,;:!?\)}\]])", 
     190                "(?:#[A-Za-z0-9]+)?(?=:?\Z|:?\s|[.,;!?\)}\]])", 
    191191               lambda x, y, z: self._format_link(x, 'wiki', y, y, 
    192192                                                 ignore_missing)) 
    193193 
  • trac/wiki/tests/wiki-tests.txt

     
    148148</p> 
    149149------------------------------ 
    150150============================== 
    151 CamelCase,CamelCase.CamelCase:CamelCase 
     151CamelCase,CamelCase.CamelCase: CamelCase 
    152152------------------------------ 
    153153<p> 
    154 <a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a>,<a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a>.CamelCase:CamelCase 
     154<a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a>,<a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a>.<a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a>: <a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a> 
    155155</p> 
    156156------------------------------ 
    157157============================== 
     
    872872------------------------------ 
    873873|| a ||  
    874874|| b || 
     875============================== 
     876t:wiki:InterTrac 
     877trac:wiki:InterTrac 
     878[t:wiki:InterTrac intertrac] 
     879[trac:wiki:InterTrac intertrac] 
     880------------------------------ 
     881<p> 
     882<a class="ext-link" href="http://projects.edgewall.com/trac/wiki/InterTrac" title="wiki:InterTrac in Trac's Trac"><span class="icon"></span>t:wiki:InterTrac</a> 
     883<a class="ext-link" href="http://projects.edgewall.com/trac/wiki/InterTrac" title="wiki:InterTrac in Trac's Trac"><span class="icon"></span>trac:wiki:InterTrac</a> 
     884<a class="ext-link" href="http://projects.edgewall.com/trac/wiki/InterTrac" title="wiki:InterTrac in Trac's Trac"><span class="icon"></span>intertrac</a> 
     885<a class="ext-link" href="http://projects.edgewall.com/trac/wiki/InterTrac" title="wiki:InterTrac in Trac's Trac"><span class="icon"></span>intertrac</a> 
     886</p> 
     887------------------------------ 
     888============================== 
     889trac:ticket:2041 
     890[trac:ticket:2041 Trac #2041] 
     891#T2041 
     892#trac2041 
     893------------------------------ 
     894<p> 
     895<a class="ext-link" href="http://projects.edgewall.com/trac/ticket/2041" title="ticket:2041 in Trac's Trac"><span class="icon"></span>trac:ticket:2041</a> 
     896<a class="ext-link" href="http://projects.edgewall.com/trac/ticket/2041" title="ticket:2041 in Trac's Trac"><span class="icon"></span>Trac #2041</a> 
     897<a class="ext-link" href="http://projects.edgewall.com/trac/ticket/2041" title="ticket:2041 in Trac's Trac"><span class="icon"></span>#T2041</a> 
     898<a class="ext-link" href="http://projects.edgewall.com/trac/ticket/2041" title="ticket:2041 in Trac's Trac"><span class="icon"></span>#trac2041</a> 
     899</p> 
     900------------------------------ 
     901============================== 
     902trac:changeset:2081 
     903[trac:changeset:2081 Trac r2081] 
     904[T2081] 
     905[trac2081] 
     906[trac 2081] 
     907------------------------------ 
     908<p> 
     909<a class="ext-link" href="http://projects.edgewall.com/trac/changeset/2081" title="changeset:2081 in Trac's Trac"><span class="icon"></span>trac:changeset:2081</a> 
     910<a class="ext-link" href="http://projects.edgewall.com/trac/changeset/2081" title="changeset:2081 in Trac's Trac"><span class="icon"></span>Trac r2081</a> 
     911<a class="ext-link" href="http://projects.edgewall.com/trac/changeset/2081" title="changeset:2081 in Trac's Trac"><span class="icon"></span>[T2081]</a> 
     912<a class="ext-link" href="http://projects.edgewall.com/trac/changeset/2081" title="changeset:2081 in Trac's Trac"><span class="icon"></span>[trac2081]</a> 
     913<a class="ext-link" href="http://projects.edgewall.com/trac/changeset/2081" title="changeset:2081 in Trac's Trac"><span class="icon"></span>[trac 2081]</a> 
     914</p> 
     915------------------------------ 
     916============================== 
     917trac:report:1 
     918[trac:report:1 Trac r1] 
     919{T1} 
     920{trac1} 
     921{trac 1} 
     922------------------------------ 
     923<p> 
     924<a class="ext-link" href="http://projects.edgewall.com/trac/report/1" title="report:1 in Trac's Trac"><span class="icon"></span>trac:report:1</a> 
     925<a class="ext-link" href="http://projects.edgewall.com/trac/report/1" title="report:1 in Trac's Trac"><span class="icon"></span>Trac r1</a> 
     926<a class="ext-link" href="http://projects.edgewall.com/trac/report/1" title="report:1 in Trac's Trac"><span class="icon"></span>{T1}</a> 
     927<a class="ext-link" href="http://projects.edgewall.com/trac/report/1" title="report:1 in Trac's Trac"><span class="icon"></span>{trac1}</a> 
     928<a class="ext-link" href="http://projects.edgewall.com/trac/report/1" title="report:1 in Trac's Trac"><span class="icon"></span>{trac 1}</a> 
     929</p> 
     930------------------------------ 
     931============================== 
     932t:InterTrac 
     933trac:InterTrac 
     934[t:InterTrac intertrac] 
     935[trac:InterTrac intertrac] 
     936T:r2081 
     937T:#2041 
     938trac:#2041 
     939------------------------------ 
     940<p> 
     941<a class="ext-link" href="http://projects.edgewall.com/trac/search?q=InterTrac" title="InterTrac in Trac's Trac"><span class="icon"></span>t:InterTrac</a> 
     942<a class="ext-link" href="http://projects.edgewall.com/trac/search?q=InterTrac" title="InterTrac in Trac's Trac"><span class="icon"></span>trac:InterTrac</a> 
     943<a class="ext-link" href="http://projects.edgewall.com/trac/search?q=InterTrac" title="InterTrac in Trac's Trac"><span class="icon"></span>intertrac</a> 
     944<a class="ext-link" href="http://projects.edgewall.com/trac/search?q=InterTrac" title="InterTrac in Trac's Trac"><span class="icon"></span>intertrac</a> 
     945<a class="ext-link" href="http://projects.edgewall.com/trac/search?q=r2081" title="r2081 in Trac's Trac"><span class="icon"></span>T:r2081</a> 
     946<a class="ext-link" href="http://projects.edgewall.com/trac/search?q=%232041" title="#2041 in Trac's Trac"><span class="icon"></span>T:#2041</a> 
     947<a class="ext-link" href="http://projects.edgewall.com/trac/search?q=%232041" title="#2041 in Trac's Trac"><span class="icon"></span>trac:#2041</a> 
     948</p> 
     949------------------------------ 
  • trac/wiki/tests/formatter.py

     
    1 from __future__ import generators 
    2 import os 
    3 import inspect 
    4 import StringIO 
    5 import unittest 
    6  
    7 from trac.core import * 
    8 from trac.wiki.formatter import Formatter, OneLinerFormatter 
    9 from trac.wiki.api import IWikiMacroProvider 
    10  
    11  
    12 class DummyHelloWorldMacro(Component): 
    13     """ 
    14     A dummy macro used by the unit test. We need to supply our own macro 
    15     because the real HelloWorld-macro can not be loaded using our 
    16     'fake' environment. 
    17     """ 
    18     implements(IWikiMacroProvider) 
    19  
    20     def get_macros(self): 
    21         yield 'HelloWorld' 
    22  
    23     def get_macro_description(self, name): 
    24         return inspect.getdoc(MacroListMacro) 
    25  
    26     def render_macro(self, req, name, content): 
    27         return 'Hello World, args = ' + content 
    28  
    29  
    30 class WikiTestCase(unittest.TestCase): 
    31  
    32     def __init__(self, input, correct, file, line): 
    33         unittest.TestCase.__init__(self, 'test') 
    34         self.input = input 
    35         self.correct = correct 
    36         self.file = file 
    37         self.line = line 
    38      
    39     def test(self): 
    40         """Testing WikiFormatter""" 
    41  
    42         # Environment stub 
    43         from trac.core import ComponentManager 
    44         from trac.config import Configuration 
    45         from trac.log import logger_factory 
    46         from trac.test import InMemoryDatabase 
    47         from trac.web.href import Href 
    48  
    49         db = InMemoryDatabase() 
    50  
    51         class DummyEnvironment(ComponentManager): 
    52             def __init__(self): 
    53                 ComponentManager.__init__(self) 
    54                 self.log = logger_factory('null') 
    55                 self.config = Configuration(None) 
    56                 self.href = Href('/') 
    57                 self.abs_href = Href('http://www.example.com/') 
    58                 self._wiki_pages = {} 
    59                 self.path = '' 
    60             def component_activated(self, component): 
    61                 component.env = self 
    62                 component.config = self.config 
    63                 component.log = self.log 
    64             def get_db_cnx(self): 
    65                 return db 
    66  
    67         # Load all the components that provide IWikiSyntaxProvider 
    68         # implementations that are tested. Ideally those should be tested 
    69         # in separate unit tests. 
    70         import trac.versioncontrol.web_ui.browser 
    71         import trac.versioncontrol.web_ui.changeset 
    72         import trac.ticket.query 
    73         import trac.ticket.report 
    74         import trac.ticket.roadmap 
    75         import trac.Search 
    76  
    77         env = DummyEnvironment() 
    78  
    79         out = StringIO.StringIO() 
    80         formatter = self.formatter(env) 
    81         formatter.format(self.input, out) 
    82         v = out.getvalue().replace('\r','') 
    83         try: 
    84             self.assertEquals(self.correct, v) 
    85         except AssertionError, e: 
    86             raise AssertionError('%s\n\n%s:%s: for the input ' 
    87                                  '(formatter flavor was "%s")' \ 
    88                                  % (str(e), self.file, self.line, 
    89                                     formatter.flavor)) 
    90          
    91     def formatter(self, env): 
    92         return Formatter(env) 
    93  
    94  
    95 class OneLinerTestCase(WikiTestCase): 
    96     def formatter(self, env): 
    97         return OneLinerFormatter(env) 
    98  
    99  
    100 def suite(): 
    101     suite = unittest.TestSuite() 
    102     file = os.path.join(os.path.split(__file__)[0], 'wiki-tests.txt') 
    103     data = open(file, 'r').read() 
    104     tests = data.split('=' * 30 + '\n') 
    105     line = 1 
    106     for test in tests: 
    107         input, page, oneliner = test.split('-' * 30 + '\n') 
    108         suite.addTest(WikiTestCase(input, page, file, line)) 
    109         if oneliner: 
    110             suite.addTest(OneLinerTestCase(input, oneliner[:-1], file, line)) 
    111         line += len(test.split('\n')) 
    112     return suite 
    113  
    114 if __name__ == '__main__': 
    115     runner = unittest.TextTestRunner() 
    116     runner.run(suite()) 
     1from __future__ import generators 
     2import os 
     3import inspect 
     4import StringIO 
     5import unittest 
     6 
     7from trac.core import * 
     8from trac.wiki.formatter import Formatter, OneLinerFormatter 
     9from trac.wiki.api import IWikiMacroProvider 
     10 
     11 
     12class DummyHelloWorldMacro(Component): 
     13    """ 
     14    A dummy macro used by the unit test. We need to supply our own macro 
     15    because the real HelloWorld-macro can not be loaded using our 
     16    'fake' environment. 
     17    """ 
     18    implements(IWikiMacroProvider) 
     19 
     20    def get_macros(self): 
     21        yield 'HelloWorld' 
     22 
     23    def get_macro_description(self, name): 
     24        return inspect.getdoc(MacroListMacro) 
     25 
     26    def render_macro(self, req, name, content): 
     27        return 'Hello World, args = ' + content 
     28 
     29 
     30class WikiTestCase(unittest.TestCase): 
     31 
     32    def __init__(self, input, correct, file, line): 
     33        unittest.TestCase.__init__(self, 'test') 
     34        self.input = input 
     35        self.correct = correct 
     36        self.file = file 
     37        self.line = line 
     38     
     39    def test(self): 
     40        """Testing WikiFormatter""" 
     41 
     42        # Environment stub 
     43        from trac.core import ComponentManager 
     44        from trac.config import Configuration 
     45        from trac.log import logger_factory 
     46        from trac.test import InMemoryDatabase 
     47        from trac.web.href import Href 
     48 
     49        db = InMemoryDatabase() 
     50 
     51        class DummyEnvironment(ComponentManager): 
     52            def __init__(self): 
     53                ComponentManager.__init__(self) 
     54                self.log = logger_factory('null') 
     55                self.config = Configuration(None) 
     56                self.href = Href('/') 
     57                self.abs_href = Href('http://www.example.com/') 
     58                self.path = '' 
     59                # -- intertrac support 
     60                self.siblings = {} 
     61                self.config.set('intertrac', 'trac.title', "Trac's Trac") 
     62                self.config.set('intertrac', 'trac.url', 
     63                                "http://projects.edgewall.com/trac") 
     64                self.config.set('intertrac', 't', 'trac') 
     65            def component_activated(self, component): 
     66                component.env = self 
     67                component.config = self.config 
     68                component.log = self.log 
     69            def get_db_cnx(self): 
     70                return db 
     71 
     72        # Load all the components that provide IWikiSyntaxProvider 
     73        # implementations that are tested. Ideally those should be tested 
     74        # in separate unit tests. 
     75        import trac.versioncontrol.web_ui.browser 
     76        import trac.versioncontrol.web_ui.changeset 
     77        import trac.ticket.query 
     78        import trac.ticket.report 
     79        import trac.ticket.roadmap 
     80        import trac.Search 
     81 
     82        env = DummyEnvironment() 
     83 
     84        out = StringIO.StringIO() 
     85        formatter = self.formatter(env) 
     86        formatter.format(self.input, out) 
     87        v = out.getvalue().replace('\r','') 
     88        try: 
     89            self.assertEquals(self.correct, v) 
     90        except AssertionError, e: 
     91            raise AssertionError('%s\n\n%s:%s: for the input ' 
     92                                 '(formatter flavor was "%s")' \ 
     93                                 % (str(e), self.file, self.line, 
     94                                    formatter.flavor)) 
     95         
     96    def formatter(self, env): 
     97        return Formatter(env) 
     98 
     99 
     100class OneLinerTestCase(WikiTestCase): 
     101    def formatter(self, env): 
     102        return OneLinerFormatter(env) 
     103 
     104 
     105def suite(): 
     106    suite = unittest.TestSuite() 
     107    file = os.path.join(os.path.split(__file__)[0], 'wiki-tests.txt') 
     108    data = open(file, 'r').read() 
     109    tests = data.split('=' * 30 + '\n') 
     110    line = 1 
     111    for test in tests: 
     112        input, page, oneliner = test.split('-' * 30 + '\n') 
     113        suite.addTest(WikiTestCase(input, page, file, line)) 
     114        if oneliner: 
     115            suite.addTest(OneLinerTestCase(input, oneliner[:-1], file, line)) 
     116        line += len(test.split('\n')) 
     117    return suite 
     118 
     119if __name__ == '__main__': 
     120    runner = unittest.TextTestRunner() 
     121    runner.run(suite()) 
  • trac/wiki/formatter.py

     
    2828    from StringIO import StringIO 
    2929 
    3030from trac import util 
     31from trac.core import * 
    3132from trac.mimeview import * 
    32 from trac.wiki.api import WikiSystem 
     33from trac.wiki.api import WikiSystem, IWikiChangeListener, IWikiMacroProvider 
    3334 
    34 __all__ = ['wiki_to_html', 'wiki_to_oneliner', 'wiki_to_outline'] 
     35__all__ = ['wiki_to_html', 'wiki_to_oneliner', 'wiki_to_outline', 'Formatter' ] 
    3536 
    3637 
    3738def system_message(msg, text): 
     
    132133    INLINE_TOKEN = "`" 
    133134 
    134135    LINK_SCHEME = r"[\w.+-]+" # as per RFC 2396 
     136    INTERTRAC_SCHEME = r"[a-zA-Z.+-]+?" # no digits (support for shorthand links) 
    135137 
    136138    QUOTED_STRING = r"'[^']+'|\"[^\"]+\"" 
    137139 
     
    290292            return self._make_link(ns, target, match, label) 
    291293 
    292294    def _make_link(self, ns, target, match, label): 
     295        # check first for an alias defined in trac.ini 
     296        ns = self.env.config.get('intertrac', ns.upper(), ns) 
    293297        if ns in self.link_resolvers: 
    294298            return self.link_resolvers[ns](self, ns, target, 
    295299                                           util.escape(label, False)) 
    296300        elif target.startswith('//') or ns == "mailto": 
    297301            return self._make_ext_link(ns+':'+target, label) 
    298302        else: 
    299             return util.escape(match) 
     303            return self._make_intertrac_link(ns, target, label) or \ 
     304                   self._make_interwiki_link(ns, target, label) or \ 
     305                   match 
    300306 
     307    def _make_intertrac_link(self, ns, target, label): 
     308        if self.env.siblings.has_key(ns): 
     309            sibling = self.env.siblings[ns] 
     310            # The following is currently needed because env.href is set 
     311            # in trac.web.main.dispatch_request: for an environment which 
     312            # has not yet been queried by a client, .href is not defined. 
     313            if not hasattr(sibling, 'href'): 
     314                from trac.web.href import Href 
     315                def xchg_base(base): 
     316                    return '/'.join(base.split('/')[:-1] + [ns]) 
     317                sibling.href = Href(xchg_base(self.env.href.base)) 
     318                sibling.abs_href = Href(xchg_base(self.env.abs_href.base)) 
     319            # EOKludge 
     320            ref = wiki_to_oneliner(target, sibling) 
     321            return ref.replace('>%s' % target, '>%s' % label) 
     322        url = self.env.config.get('intertrac', ns.upper()+'.url') 
     323        if url: 
     324            name = self.env.config.get('intertrac', ns.upper()+'.title', 
     325                                       'Trac project %s' % ns) 
     326            sep = target.find(':') 
     327            if sep != -1: 
     328                url = '%s/%s/%s' % (url, target[:sep], target[sep+1:]) 
     329            else:  
     330                url = '%s/search?q=%s' % (url, urllib.quote_plus(target)) 
     331            return self._make_ext_link(url, label, '%s in %s' % (target, name)) 
     332        else: 
     333            return None 
     334 
     335    def shorthand_intertrac_helper(self, ns, target, label, fullmatch): 
     336        if fullmatch: # short form 
     337            it_group = fullmatch.group('it_%s' % ns) 
     338            if it_group: 
     339                alias = it_group.strip() 
     340                intertrac = self.env.config.get('intertrac', alias.upper(), 
     341                                                alias) 
     342                target = '%s:%s' % (ns, target[len(it_group):]) 
     343                return self._make_intertrac_link(intertrac, target, label) or \ 
     344                       label 
     345        return None 
     346 
     347    def _make_interwiki_link(self, ns, target, label): 
     348        interwiki = InterWikiMap(self.env) 
     349        if interwiki.has_key(ns): 
     350            url, title = interwiki.url(ns, target) 
     351            return self._make_ext_link(url, label, '%s in %s' % (target, title)) 
     352        else: 
     353            return None 
     354 
    301355    def _make_ext_link(self, url, text, title=''): 
    302356        url = util.escape(url) 
    303357        text, title = util.escape(text), util.escape(title) 
     
    755809    OutlineFormatter(env, absurls, db).format(wikitext, out, max_depth, 
    756810                                              min_depth) 
    757811    return util.Markup(out.getvalue()) 
     812 
     813 
     814# -- InterWiki support 
     815 
     816class InterWikiMap(Component): 
     817 
     818    implements(IWikiChangeListener, IWikiMacroProvider) 
     819 
     820    _page_name = 'InterMapTxt' 
     821    _interwiki_re = re.compile(r"(\w+)[ \t]+([^ \t]+)(?:[ \t]+#(.*))?", 
     822                               re.UNICODE) 
     823    _argspec_re = re.compile(r"\$\d") 
     824 
     825    def __init__(self): 
     826        self._interwiki_map = None 
     827        # This dictionary maps upper-cased namespaces 
     828        # to (namespace, prefix, title) values 
     829 
     830    def has_key(self, ns): 
     831        if not self._interwiki_map: 
     832            self._update() 
     833        return self._interwiki_map.has_key(ns.upper()) 
     834 
     835    def url(self, ns, target): 
     836        ns, url, title = self._interwiki_map[ns.upper()] 
     837        args = target.split(':') 
     838        def setarg(match): 
     839            num = int(match.group()[1:]) 
     840            return 0 < num <= len(args) and args[num-1] or '' 
     841        url_with_args = re.sub(InterWikiMap._argspec_re, setarg, url) 
     842        if url_with_args == url:  
     843            return url + target, title 
     844        else: 
     845            return url_with_args, title 
     846 
     847    # IWikiChangeListener methods 
     848 
     849    def wiki_page_added(self, page): 
     850        if page.name == InterWikiMap._page_name: 
     851            self._update() 
     852 
     853    def wiki_page_changed(self, page, version, t, comment, author, ipnr): 
     854        if page.name == InterWikiMap._page_name: 
     855            self._update() 
     856 
     857    def wiki_page_deleted(self, page): 
     858        if page.name == InterWikiMap._page_name: 
     859            self._interwiki_map.clear() 
     860 
     861    def _update(self): 
     862        from trac.wiki.model import WikiPage 
     863        self._interwiki_map = {} 
     864        content = WikiPage(self.env, InterWikiMap._page_name).text 
     865        in_map = False 
     866        for line in content.split('\n'): 
     867            if in_map: 
     868                if line.startswith('----'): 
     869                    in_map = False 
     870                else: 
     871                    m = re.match(InterWikiMap._interwiki_re, line) 
     872                    if m: 
     873                        prefix, url, title = m.groups() 
     874                        url = url.strip() 
     875                        title = title and title.strip() or prefix 
     876                        self._interwiki_map[prefix.upper()] = (prefix, url, 
     877                                                               title) 
     878            elif line.startswith('----'): 
     879                in_map = True 
     880 
     881    # IWikiMacroProvider 
     882 
     883    def get_macros(self): 
     884        yield 'InterWiki' 
     885 
     886    def get_macro_description(self, name):  
     887        return "Provide a description list for the known InterWiki prefixes." 
     888 
     889    def render_macro(self, req, name, content): 
     890        if not self._interwiki_map: 
     891            self._update() 
     892        keys = self._interwiki_map.keys() 
     893        keys.sort() 
     894        buf = StringIO() 
     895        buf.write('<table><tr><th>Prefix</th><td>Site</td></tr>\n') 
     896        for k in keys: 
     897            prefix, url, title = self._interwiki_map[k] 
     898            shortened_url = url and url[:-1] 
     899            description = title == prefix and shortened_url or title 
     900            buf.write('<tr>\n' + 
     901                      ('<td><a href="%sRecentChanges">%s</a></td>' 
     902                       '<td><a href="%s">%s</a></td>\n') \ 
     903                      % (url, prefix, shortened_url, description) + 
     904                      '</tr>\n') 
     905        buf.write('</table>\n') 
     906        return buf.getvalue() 
  • trac/web/standalone.py

     
    2424from trac.web.api import Request 
    2525from trac.web.cgi_frontend import TracFieldStorage 
    2626from trac.web.main import dispatch_request, get_environment, \ 
     27                          setup_sibling_environments, \ 
    2728                          send_pretty_error, send_project_index 
    2829from trac.util import md5crypt 
    2930 
     
    201202        else: 
    202203            self.http_host = '%s:%d' % (self.server_name, self.server_port) 
    203204 
    204         self.env_parent_dir = env_parent_dir and {'TRAC_ENV_PARENT_DIR': 
    205                                                   env_parent_dir} 
     205        self.env_paths = env_paths 
    206206        self.auths = auths 
     207        self.options = os.environ.copy() 
     208        if env_parent_dir: 
     209            self.options['TRAC_ENV_PARENT_DIR'] = env_parent_dir 
     210        self.projects = setup_sibling_environments(self.options, self.env_paths) 
    207211 
    208         self.projects = {} 
    209         for env_path in env_paths: 
    210             # Remove trailing slashes 
    211             while env_path and not os.path.split(env_path)[1]: 
    212                 env_path = os.path.split(env_path)[0] 
    213       &