Attachment 'LookupDictPagesAndSort.py'

Download

   1 """
   2 MoinMoin - LookupDictPagesAndSort Macro
   3 A line-oriented search macro over multiple pages, with sorting
   4 
   5 @copyright: Jonas Smedegaard <dr@jones.dk>
   6 @license: GPL
   7 
   8 Based heavily on SearchInPagesAndSort
   9 by Pascal Bauermeister <pascal DOT bauermeister AT hispeed DOT ch>
  10 
  11 Updates:
  12   * [v0.3.4.4] Jonas Sun Nov 12 19:00:39 UTC 2006
  13     * Rename LookupPagesAndSort -> LookupDictPagesAndSort everywhere.
  14     * New feature: SortKey can now be a dict lookup too. Same syntax: sk="@PN?Definition@"
  15     * Today is my birthday: Congratulations! :-)
  16 
  17   * [v0.3.4.3] Jonas Wed Nov 23 15:22:27 CET 2005
  18     * Ignore empty LookupText values (continue loop rather than break).
  19     * Adjust a variable name for a smaller SearchInPlacesAndSort diff.
  20     * Allow caching of the page (let's see if it causes any trouble).
  21     * Correct references to name of script itself.
  22     * Replace examples with ones that make better sense.
  23     * New feature: Pages can now be a group-page lookup: Prepend a "+".
  24 
  25   * [v0.3.4.2] Jonas Fri Nov 18 17:27:01 CET 2005
  26     * Replace SearchString (regexp) with LookupString (dict)
  27     * Simplify heading_text and keyval loops (always one per page now)
  28     * Decode UTF-8 input in regexp
  29 
  30   * [v0.3.4.1] Jonas Fri Nov 18 17:03:26 CET 2005
  31     * Add dict lookup. Syntax: @PN?Definition@
  32     * Drop NbSubs and MoreSubsText support
  33 
  34   * [v0.3.4] Pascal Sat Mar  5 17:53:08 CET 2005
  35     * MoinMoin 1.3.x _and_ 1.2.x compatible
  36     * Added arguments: Format, HeaderFormat and FormatSort
  37     
  38   * [v0.3.3] Pascal
  39     * Fixed a security hole (eval used for arguments parsing)
  40     * Added argument: ExcludePages=regex
  41 
  42   * [v0.3.2] Pascal
  43     * Use StringIO instead of cStringIO, for unicode compatibility
  44 
  45   * [v0.3.1] Pascal Sat Nov  6 16:03:01 CET 2004
  46     * Added NoText, RawText, NbSubs and MoreSubsText arguments
  47 
  48   * [v0.3.1] Pascal Mon Aug 30 21:27:36 CEST 2004
  49     * Corrected bug: did not work well with multiple pages hit.
  50       Bug reported by Craig Johnson.
  51       It worked in 0.2.x because one bug corrected another one...
  52     * If args are not a kw list (e.g. old macro form) inserts usage in html
  53       page (brutal, but we really don't want to support the old form any more)
  54 
  55   * [v0.3.0] Pascal Wed Aug 18 15:39:54 CEST 2004
  56     * macro arguments are now passed as a list of KEYWORD=VALUE
  57     * ACL is handled
  58     * new options: Reverse and NoHeader
  59 
  60   * [v0.2.4] Pascal Mon Jul 19 23:40:54 CEST 2004
  61     * Comparisons to None use the 'is' and 'is not' operator (nicer)
  62     * Use get() for dict lookup w/ default value
  63     * Do not quote args and retry to compile if they are not valid regexes
  64     * Corrected usage samples in the comment below
  65 
  66   * [v0.2.3] Pascal Sun Jul 18 13:45:46 CEST 2004
  67     Avoid endless recursion when matching page contains this macro
  68 
  69   * [v0.2.2] Fri Jul 16 14:43:23 CEST 2004
  70     * Use Request.redirect(). Thanks to Craig Johnson <cpjohnson AT edcon DOT
  71       co DOT za>
  72       and Thomas Waldmann <tw DASH public AT g m x DOT d e>.
  73     * No more unused imports.
  74     * Catch only expected exceptions.
  75 
  76   * [v0.2.1] Mon Jun  7 11:54:52 CEST 2004
  77     * options: links, heading
  78     * works now with MoinMoin Release 1.2 too
  79 
  80   * [v0.1.1] Wed Oct 29 14:48:02 CET 2003
  81     works with MoinMoin Release 1.1 [Revision 1.173] and Python 2.3.2
  82 
  83   * [v0.1.0] 2003/04/24 10:32:04
  84     Original version
  85 
  86 ----
  87 
  88 Usage:
  89   [[ LookupDictPagesAndSort ]]
  90   [[ LookupDictPagesAndSort (KEYWORD=VALUE [, ...] ) ]]
  91 
  92 Lookup 'lookuptext' dict definitions in pages matching 'pages' regex, and
  93 sort the found lines (=hits) in this order:
  94   1) substring of the hit matching 'sortkey'; group same matches of
  95      'sortkey' by a header
  96   2) substring of the hit matching 'lookuptext'
  97   3) the hit itself
  98 
  99 If no arguments are given, the usage is inserted in the HTML result.
 100 Possible keywords:
 101 
 102   Help           = 0, 1, 2         Displays 1:short or 2:full help in the page.
 103                                    Default: 0 (i.e. no help).
 104 
 105   Pages          = 'PAGES REGEX'   Pages in which the text is sought. If
 106                    or              empty (default) search in the current page
 107                    '+PageGroup'    and defaults 'NoLinks' to 1. If starting with
 108                                    "+" then a single PageGroup page is looked up.
 109                                    Default: empty (i.e. current page).
 110 
 111   ExcludePages   = 'PAGES REGEX'   Exclude these pages (i.e. remove these pages
 112                                    from the list collected by 'Pages').
 113                                    Default: empty (i.e. don't exclude any).
 114 
 115   LookupText     = 'TEXT DICT'     To lookup definition in matching pages.
 116                                    Mandatory!
 117 
 118   SortKey        = 'TEXT REGEX'    Criterion to sort matching lines (=hits).
 119                    or              Default: empty (i.e. no sorting).
 120                    '@PN?Definition@'
 121 
 122   Heading        = 'TEXT REGEX'    Follow each hit by the text maching Regex,
 123                                    that preceeds the hit in its source page.
 124                                    Default: empty (i.e. no headings).
 125 
 126   UnassignedText = 'WIKI TEXT'     Header for hits not matching the sort key.
 127                                    Default: '[unassigned]'.
 128 
 129   Reverse        = 0 or 1          Reverse-sort the hits.
 130                                    Default: 0 (i.e. forward sort).
 131 
 132   RawText        = 0 or 1          Do not format found text.
 133                                    Default: 0 (i.e. formatted).
 134 
 135   Format         = 'STRING'        Explicitely format the output using this
 136                                    string, which can contain wiki formatting
 137                                    as well as these tokens:
 138                                      @KT@ : text matching 'SortKey'
 139                                      @LT@ : text matching 'LookupText'
 140                                      @FT@ : line of text
 141                                      @PN@ : page name
 142                                      @HT@ : heading text
 143                                      @@   : the '@' character
 144                                      \\n  : newline (of wiki source text).
 145 
 146                                    Each token can contain a regex acting as
 147                                    a filter for displaying the value, e.g:
 148                                      @FT:{[123]}@      displays the prio smiley
 149 
 150                                    Multiple groups can be defined, in which
 151                                    case the text matching them will be
 152                                    displayed, e.g:
 153                                      @FT:{[123]}(.*)@  displays text after prio
 154 
 155                                    Default: '' (i.e. auto-formatting).
 156 
 157   HeaderFormat   = 'STRING'        If specified, use this instead of 'Format'
 158                                    for headers.
 159                                    Default: '' (i.e. do not display headers).
 160 
 161   FormatSort     = 0 or 1          If 1, sort the output generated by 'Format'
 162                                    (if 'Reverse' is 1, reverse-sort). If 0,
 163                                    leave the output sorted by the 'SortKey'
 164                                    criterion (if specified).
 165                                    Default: 0 (i.e. no sorting).
 166 
 167   NoHeader       = 0 or 1          Disable showing the headers as subtitles.
 168                                    Default: 0 (i.e. show headers).
 169 
 170   NoLinks        = 0 or 1          Disable following each hit by a link to its
 171                                    page.
 172                                    Default: 0 (i.e. show links) or 1 if
 173                                    'Pages' is omitted.
 174 
 175   NoPageText     = 'HTML TEXT'     Text displayed if no page match 'Pages'.
 176                                    Default: an error message w/ Page regex
 177 
 178   NoText         = 0 or 1          Disables showing the found text.
 179                                    Default: 0 (i.e. show found text).
 180 
 181 Keywords can be also given in upper or lower cases, or abbreviated.
 182 Example: LookupText, lookuptext, LOOKUPTEXT, lt, LT, Pages, p, etc.
 183 
 184 ----
 185 
 186 Sample 1:
 187 
 188   Given a page named 'AnInterestingBook':
 189         = A rather interesting Book =
 190         == Bibliographical facts ==
 191          Title:: A rather interesting Book
 192          Author:: A. Man
 193          Publisher:: Cool Publishing Corp.
 194         == Comments ==
 195         I really think that this book is worth a read.
 196 
 197         I'd even wanna lend out my copy if needed!
 198 
 199         == Status ==
 200          Owner:: Jonas Smedegaard
 201          Availability:: Lend out to Jack the Ripper
 202 
 203   ...and a page named 'AnotherInterestingBook':
 204         = Another interesting Book =
 205         == Bibliographical facts ==
 206          Title:: Another interesting Book
 207          Author:: A. Man
 208          Publisher:: Cool Publishing Corp.
 209         == Comments ==
 210         This is the sequel to AnInterestingBook - also worth a read.
 211 
 212         == Status ==
 213          Owner:: Jonas Smedegaard
 214          Availability:: Available - call me if interested in lending it
 215 
 216   ...and a page named 'AnotherBoringBook':
 217         = A boring Book =
 218         == Bibliographical facts ==
 219          Title:: A pretty boring Book
 220          Author:: Some Fool
 221          Publisher:: Lousy Publishing Corp.
 222         == Comments ==
 223         Don't waste time on this book.
 224 
 225         I was stupid enough to buy it once, but won't even lend it out!
 226 
 227         == Status ==
 228          Owner:: Jonas Smedegaard
 229          Availability::
 230 
 231   ...and the wiki setup to include books as dict pages:
 232         page_dict_regex =  u'[a-z0-9](Book|Dict)$'
 233 
 234   ...using the macro in a page named 'BookOverview' like this:
 235         = Known books =
 236         [[LookupDictPagesAndSort(pages=".*Book$", lookuptext="Title")]]
 237 
 238         = Book availability =
 239         [[LookupDictPagesAndSort(pages=".*Book$", lookuptext="Availability")]]
 240 
 241   ...will give this output (note: _text_ are links):
 242         Known books
 243           * A. Man
 244             * A rather interesting Book _AnInterestingBook_
 245             * A rather interesting Book _AnotherInterestingBook_
 246           * Some Fool
 247             * A pretty boring Book _AnotherBoringBook_
 248 
 249         Book Availability
 250           * Lend out to Jack the Ripper _AnInterestingBook_
 251           * Available - call me if interested in lending it _AnotherInterestingBook_
 252 
 253 
 254 Sample 2:
 255 
 256   Given a page /MyDict containing:
 257         == Contact info ==
 258          FirstName:: Jonas
 259          FullName:: Jonas "dr. Jones" Smedegaard
 260          Phone:: +45 40843136
 261          Email:: dr@jones.dk
 262         == Photo gallery ==
 263          PhotoThumbnail:: http://dr.jones.dk/images/me/kp_bricks_thumb.jpg
 264          PhotoPortrait:: http://dr.jones.dk/images/me/kp_bricks.jpg
 265 
 266   ...the following macro call in another page:
 267         [[LookupDictPagesAndSort(lookuptext="+WikiEditorsGroup", DictPage="/MyDict", LookupText="Email", Format=" * @PN?PhotoThumbnail@ [mailto:@PN?Email@ @PN?FirstName@]\\n")]])]]
 268 
 269   ...will produce a list of images and email references for me and all other editors.
 270 """
 271 
 272 # Imports
 273 import re, sys, StringIO, urllib
 274 from string import ascii_lowercase, maketrans
 275 from MoinMoin import config, wikiutil, version
 276 from MoinMoin.Page import Page
 277 from MoinMoin.parser import wiki
 278 
 279 before_1_3 = version.release < '1.3'
 280 
 281 #Dependencies = ["time"] # macro cannot be cached
 282 
 283 _recursions = 0
 284 FAKETRANS = maketrans ("","")
 285 
 286 
 287 class _Error (Exception):
 288     pass
 289 
 290 
 291 def execute (macro, text, args_re=None):
 292 
 293     global _recursions
 294     if _recursions: return ''
 295 
 296     _recursions += 1
 297     try:     res = _execute (macro, text)
 298     except _Error, msg:
 299         _recursions = 0
 300         return """
 301         <p><strong class="error">
 302         Error: macro LookupDictPagesAndSort: %s</strong> </p>
 303         """ % msg
 304 
 305     _recursions -=1
 306     return res
 307 
 308 
 309 def _delparam (keyword, params):
 310     value = params [keyword]
 311     del params [keyword]
 312     return value.decode("UTF-8")
 313 
 314 
 315 def _param_get (params, spec, default):
 316 
 317     """Returns the value for a parameter, if specified with one of
 318     several acceptable keyword names, or returns its default value if
 319     it is missing from the macro call. If the parameter is specified,
 320     it is removed from the list, so that remaining params can be
 321     signalled as unknown"""
 322 
 323     # param name is litteral ?
 324     if params.has_key (spec): return _delparam (spec, params)
 325 
 326     # param name is all lower or all upper ?
 327     lspec = spec.lower ()
 328     if params.has_key (lspec): return _delparam (lspec, params)
 329     uspec = spec.upper ()
 330     if params.has_key (uspec): return _delparam (uspec, params)
 331 
 332     # param name is abbreviated ?
 333     cspec = spec [0].upper () + spec [1:] # capitalize 1st letter
 334     cspec = cspec.translate (FAKETRANS, ascii_lowercase)
 335     if params.has_key (cspec): return _delparam (cspec, params)
 336     cspec = cspec.lower ()
 337     if params.has_key (cspec): return _delparam (cspec, params)
 338 
 339     # nope: return default value
 340     return default
 341 
 342 
 343 def _usage (full = False):
 344 
 345     """Returns the interesting part of the module's doc"""
 346 
 347     if full: return __doc__
 348 
 349     lines = __doc__.replace ('\\n', '\\\\n'). splitlines ()
 350     start = 0
 351     end = len (lines)
 352     for i in range (end):
 353         if lines [i].strip ().lower () == "usage:":
 354             start = i
 355             break
 356     for i in range (start, end):
 357         if lines [i].startswith ('--'):
 358             end = i
 359             break
 360     return '\n'.join (lines [start:end])
 361 
 362 
 363 def _re_compile (text, name):
 364     try:
 365         return re.compile (text, re.IGNORECASE)
 366     except Exception, msg:
 367         raise _Error ("%s for regex argument %s: '%s'" % (msg, name, text))
 368 
 369 
 370 last_request_h = None
 371 last_pages_list = []
 372 
 373 def _get_all_pages (request):
 374     global last_request_h
 375     global last_pages_list
 376     request_h = hash (request)
 377     if request_h != last_request_h:
 378         if before_1_3: all_pages = wikiutil.getPageList (config.text_dir)
 379         else: all_pages = request.rootpage.getPageList()
 380         last_request_h = request_h
 381         last_pages_list = all_pages
 382     return last_pages_list
 383 
 384 
 385 # The "raison d'etre" of this module
 386 def _execute (macro, text):
 387 
 388     result = ""
 389 
 390     # new args syntax
 391     try:
 392         params = eval ("(lambda **opts: opts)(%s)" % text,
 393                        {'__builtins__': []}, {})
 394     except Exception, msg:
 395         raise _Error ("""<pre>malformed arguments list:
 396         %s<br>cause:
 397         %s
 398         </pre>
 399         <br> usage:
 400         <pre>%s</pre>
 401         """ % (text, msg, _usage () ) )
 402 
 403     arg_text            = _param_get (params, 'LookupText',   None)
 404     arg_pages           = _param_get (params, 'Pages',        '')
 405     arg_excl_pages      = _param_get (params, 'ExcludePages', '')
 406     arg_dict            = _param_get (params, 'DictPage',     '')
 407     arg_key             = _param_get (params, 'SortKey',      None)
 408 
 409     opt_heading         = _param_get (params, 'Heading',      None)
 410     opt_unassigned_text = _param_get (params, 'UnassignedText',
 411                                       "[unassigned]")
 412     opt_reverse         = _param_get (params, 'Reverse',      False)
 413     opt_rawtext         = _param_get (params, 'RawText',      False)
 414 
 415     opt_format          = _param_get (params, 'Format',       '')
 416     opt_headerformat    = _param_get (params, 'HeaderFormat', '')
 417     opt_formatsort      = _param_get (params, 'FormatSort',   0)
 418     
 419     def_nolinks         = (1,0) [len (arg_pages)>0]
 420     opt_nolinks         = _param_get (params, 'NoLinks',      def_nolinks)
 421     opt_noheader        = _param_get (params, 'NoHeader',     False)
 422     opt_notext          = _param_get (params, 'NoText',       False)
 423     opt_nopage          = _param_get (params, 'NoPageText',   None)
 424     opt_help            = _param_get (params, 'Help',         0)
 425 
 426     # help ?
 427     if opt_help:
 428         return """
 429         <p>
 430         Macro LookupDictPagesAndSort usage:
 431         <pre>%s</pre></p>
 432         """ % _usage (opt_help==2)
 433 
 434     # check the args a little bit
 435     if len (params):
 436         raise _Error ("""unknown argument(s): %s
 437         <br> usage:
 438         <pre>%s</pre>
 439         """ % (`params.keys ()`, _usage () ) )
 440 
 441     if arg_text is None:
 442         raise _Error ("missing 'lookuptext' argument")
 443 
 444     # empty page means this page; subpage are also handled
 445     if len (arg_pages) == 0 or arg_pages.startswith ('/'):
 446         arg_pages = macro.formatter.page.page_name + arg_pages
 447 
 448     # get a list of pages matching the PageRegex
 449     all_pages = _get_all_pages (macro.request)
 450     if arg_pages [0]=="+":
 451         hits = macro.request.dicts.members(arg_pages [1:])
 452     else:
 453         pages_re = _re_compile (arg_pages, 'Pages')
 454         hits = filter (pages_re.search, all_pages)
 455     if arg_excl_pages:
 456         excl_pages_re = _re_compile (arg_excl_pages, 'ExcludePages')
 457         hits = filter (lambda hit: not excl_pages_re.search (hit), hits)
 458 
 459     if before_1_3:
 460         # check ACL now (since we may end up with no pages)
 461         if config.acl_enabled:
 462             me = macro.request.user.name
 463             def _check_page (page_name):
 464                 page = Page (page_name) # too bad we must instanciate...
 465                 return page.getACL ().may (macro.request, me, "read")
 466             hits = filter (_check_page, hits)
 467 
 468     # sort pages, check if we have pages
 469     if len (hits) == 0:
 470         if opt_nopage: return "%s" % opt_nopage
 471         else:
 472             raise _Error ("no page matching '%s'!" % arg_pages)
 473     else: hits.sort ()
 474 
 475     if arg_key is not None and not (arg_key.startswith('@PN?') and arg_key.endswith('@')):
 476         key_re = _re_compile (arg_key, 'SortKey')
 477 
 478     if opt_heading is not None:
 479         heading_re = _re_compile (opt_heading, 'Heading')
 480 
 481     # we will collect matching lines in each matching page
 482     all_matches = []
 483 
 484     # treat each found page
 485     for page_name in hits:
 486         heading_text = ""
 487 
 488         # Set dict page to use for lookups
 489         if len (arg_dict) == 0 or arg_dict.startswith ('/'):
 490             dict_name = page_name + arg_dict
 491         else:
 492             dict_name = arg_dict
 493 
 494         # lookup text
 495         lookuptext = macro.request.dicts.dict(dict_name).get(arg_text,'')
 496         if not lookuptext: continue
 497 
 498         # text is found; now search for heading
 499         if opt_heading is not None:
 500             heading_match = heading_re.search (lookuptext)
 501             if heading_match:
 502                 heading_text = heading_match.group (0)
 503 
 504         # find the sort key
 505         keyval = ""
 506         if arg_key is not None:
 507             if arg_key.startswith('@PN?') and arg_key.endswith('@'):
 508                 keydict = arg_key.strip ("@")
 509                 #FIXME: only cut out dict name here, and lookup after (non-hardcoded!) pagename is expanded
 510                 keyval = macro.request.dicts.dict(dict_name).get(keydict [3:],'')
 511             else:
 512                 keymatch = key_re.search (lookuptext)
 513                 if keymatch:
 514                     keyval = keymatch.group (0)
 515                 else:
 516                     keyval = opt_unassigned_text
 517 
 518         # store info
 519         item = []
 520         item.append (keyval)                          # key text
 521         item.append (lookuptext)                      # lookup text
 522         item.append (page_name)                       # page name
 523         item.append (dict_name)                       # dict name
 524         item.append (heading_text)                    # heading
 525         all_matches.append (item)
 526 
 527     # all pages handled
 528 
 529     # prepare some formatting text
 530     bullet_list_open = macro.formatter.bullet_list (1)
 531     bullet_list_close = macro.formatter.bullet_list (0)
 532     listitem_open = macro.formatter.listitem (1)
 533     listitem_close = macro.formatter.listitem (0)
 534 
 535     # now sort and format records
 536     if not opt_notext: all_matches.sort ()
 537     if opt_reverse: all_matches.reverse ()
 538 
 539     # explicitely-formatted output
 540     if opt_format:
 541         block = ""
 542         last_keytext = None
 543         rx = re.compile (r'([^@]*?)(@[^@]*?@)')
 544         pairs = re.findall (rx, opt_format+"@-@")
 545         if opt_headerformat: hpairs = re.findall (rx, opt_headerformat+"@-@")
 546         else: hpairs = None
 547         rx2d = {}
 548         for item in all_matches:
 549             keytext, text, pagename, dict_name, heading_text = item
 550             if keytext == last_keytext: plist = (pairs,)
 551             elif hpairs: plist = (hpairs, pairs)
 552             else: plist = (pairs,)
 553             last_keytext = keytext
 554             for p in plist:
 555                 for txt, token in p:
 556                     txt = txt.replace ("\\n", "\n")
 557                     if not token: continue
 558                     token = token.strip ("@")
 559                     block += txt
 560                     rx2 = None
 561                     if len (token)>2 and token [2]=="?":
 562                         #FIXME: only cut out dict name here, and lookup after (non-hardcoded!) pagename is expanded
 563                         token = macro.request.dicts.dict(dict_name).get(token [3:],'')
 564                     if len (token)>2 and token [2]==":":
 565                         token, rx2 = token [:2], token [3:]
 566                         if not rx2d.has_key (rx2): rx2d [rx2] = \
 567                            re.compile (rx2)
 568                         rx2 = rx2d [rx2]
 569                     token = token.replace ("\\n", "\n")
 570                     d = { "KT": keytext,      "LT": text,
 571                           "PN": pagename,     "HT": heading_text,
 572                           "":   "@",
 573                           "-":  "",
 574                           }
 575                     if rx2:
 576                         tx = d.get (token, None)
 577                         if tx:
 578                             tx = map ("".join, re.findall (rx2, tx)) [0]
 579                         else: tx = token
 580                         block += tx
 581                     else:
 582                         block += d.get (token, token)
 583         if opt_formatsort:
 584             lines = block.split ("\n")
 585             lines.sort ()
 586             if opt_reverse: lines.reverse ()
 587             block = "\n".join (lines)
 588         result += "\n%s\n" % _format (block, macro.request, macro.formatter)
 589 
 590     # auto-formatted output treat records for output
 591     else:
 592         head_count = 0
 593         result = result+"\n" + bullet_list_open
 594         keyval = ""
 595         last_pagename = ""
 596 
 597         for item in all_matches:
 598             keytext, text, pagename, dict_name, heading_text = item
 599 
 600             if opt_notext:
 601                 text_fmtted = ""
 602                 if last_pagename == pagename: continue
 603                 else: last_pagename = pagename
 604             elif opt_rawtext:
 605                 text_fmtted = wikiutil.escape (text)
 606             else:
 607                 # parse the text  (in wiki source format) and make HTML,
 608                 # after diverting sys.stdout to a string
 609                 text_fmtted = _format (text, macro.request, macro.formatter)
 610                 text_fmtted = text_fmtted.strip (' ') # preserve newlines
 611 
 612                 # empty text => drop this item
 613                 if len (text_fmtted)==0: continue
 614 
 615             # insert heading  (only if not yet done)
 616             if not opt_noheader \
 617                and arg_key is not None \
 618                and keytext != keyval:
 619                 # this is a new heading
 620                 keyval = keytext
 621                 if head_count:
 622                     result = result+"\n    " + bullet_list_close
 623                     result = result+"\n  " + listitem_close
 624                 head_count = head_count +1
 625                 result = result+"\n  " + listitem_open
 626                 result = result+ _format (keyval,
 627                                           macro.request, macro.formatter)
 628                 result = result+"\n    " + bullet_list_open
 629 
 630             # correct the text format (berk)
 631             if text_fmtted.startswith ("\n<p>"):
 632                  text_fmtted = text_fmtted [4:]
 633             if text_fmtted.endswith ("</p>\n"):
 634                 text_fmtted = text_fmtted [:-5]
 635                 text_trailer = "\n</p>\n"
 636             else: text_trailer = ""
 637 
 638             # insert formatted text
 639             result = result+"\n      " + listitem_open
 640             result = result + text_fmtted
 641             if not opt_nolinks:
 642                 result = result + "&nbsp;&nbsp;&nbsp;<font size=-1>"
 643                 if arg_text:
 644                     if before_1_3:
 645                         pageurl = '%s?action=highlight&value=%s' % (
 646                             pagename,
 647                             urllib.quote_plus (re.escape (text)))
 648                     else:
 649                         pageurl = '%s?highlight=%s' % (
 650                             pagename,
 651                             urllib.quote_plus (re.escape (text)))
 652 
 653                 else: pageurl = wikiutil.quoteWikiname (pagename)
 654                 link_text = wikiutil.link_tag (macro.request,
 655                                                pageurl, pagename)
 656 
 657                 result = result + link_text
 658                 result = result + "</font>"
 659             if opt_heading is not None:
 660                 result = result + "&nbsp;&nbsp;&nbsp;<font size=-1>"
 661                 result = result + heading_text
 662                 result = result + "</font>"
 663 
 664             result = result + text_trailer + "\n      " + listitem_close
 665 
 666     # all items done, close  (hopefully) gracefully
 667     if not opt_format:
 668         if head_count:
 669             result = result+"\n      " + listitem_close
 670             result = result+"\n    " + bullet_list_close
 671         if not opt_noheader and arg_key is not None:
 672             result = result+"\n  " + listitem_close
 673         result = result+"\n" + bullet_list_close
 674 
 675     # done
 676     return result
 677 
 678 def _format (src_text, request, formatter):
 679     # parse the text (in wiki source format) and make HTML,
 680     # after diverting sys.stdout to a string
 681     str_out = StringIO.StringIO ()      # create str to collect output
 682     request.redirect (str_out)          # divert output to that string
 683     # parse this line
 684     wiki.Parser (src_text, request).format (formatter)
 685     request.redirect ()                 # restore output
 686     return str_out.getvalue ()          # return what was generated

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2008-07-18 10:33:32, 25.5 KB) [[attachment:LookupDictPagesAndSort.py]]
  • [get | view] (2005-11-23 21:21:54, 24.7 KB) [[attachment:LookupPagesAndSort.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.