Attachment 'Ref-2.5.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3 MoinMoin - Ref macro, Version 2.5, 21.10.2012
   4 
   5 A macro to cite references from a dictionary page (a ' key:: value' listing).
   6 
   7 Searches one or more dictionary pages to match key (Argument 1, with or without 'key=', quoted string). Matches are included in the page as (1) bookmarks, (2) search key text followed by a footnote link, (3) search key text as the link to the footnote, (4) search key text with found reference in a popup text box on mouseover, (5) found reference text only and (6) search key text only. 
   8 
   9 The dictionary page is set the RefDict by default, or can be set for (a) the whole page by #pragma ref-dict, or (b) for individual calls by Argument 3.
  10 
  11 For options (2) and (3), search key text will have 'et al' set to Italics (which can be switched off for the whole page by setting #pragma ref-ital off). Also, the year (last word of the key assumed to be a year) can be surrounded in brackets. By default these are just ' (year)' but can be set by #pragma ref-year-bkt to give, for example ', (^)' (where ^ indicates the insertion point for year), if the desired format in the in-text citation differs from the key text in the dictionary.
  12 
  13 Exclamation marks (as WikiName escapes) can be included in the search key, they are removed for matching in dictpage(s) and restored for display under options (2) and (3).
  14 
  15 Additionally superscripting of the numerical link in options (2) and (3) can be switched off and default square brackets are used to distinguish the link from the text. The bracketing can customised using #pragma ref-link-bkt as described above for bracketing the year.
  16 
  17 Option (4) can be used to show reference text in context as a popup tip. This option can also be used for other information, such as definitions of words that might be unfamilar to the reading, with the definitiions stored in a dictionary page. 
  18 
  19 Option (5) is used to create reference (or further reading) list at any point in the the page, without hyperlinks to or from the citation. Or, just to put the reference into the text perhaps in brackets.
  20 
  21 Option (6) simply inserts the search key text into the page.  If reference text is inserted by Option (5) on the same or separate page, inserting references into the text using this macro allows them to be found by searching for the macro. Additionally, if this citation type is set at the page level (by #pragma), it can be temporarily reset to make a footnote list on the page for proof reading purposes.
  22 
  23 These options can be selected for each instance (Argument 2, with or without 'cite=' set to bkt, bktfn, bktlk, fn, key, keyfn, keylk, tip or val).
  24 
  25 If 0 or >1 match is found a warning is inserted into the page.
  26 
  27 <<Ref()>>
  28 
  29 Inserts '[citation needed]' to act as a reminder and/or placeholder.
  30 
  31 <<Ref([ref=]"key"[,[cite=]bkt|bktfn|bktlk|fn|key|keyfn|keylk|tip|val)[,[dict=]dictpage]])>>
  32 
  33 Argument labels (ref=, cite= and dict=) are optional. The first argument must be ref. Cite and dict arguments are optional and can appear alone, or together in any order. Keywords for cite should not be used for dictpage names.
  34 
  35 ref="key": search key to be matched in dictpage(s), set in single or double quotes. Setting ref="" is equivalent to <<Ref()>> (see above).
  36 
  37 cite=bkt: insert only the key (reference code with the year bracketed) into the page. No link or footnote is created.
  38 
  39 cite=bktfn: insert a footnote into the page as the search key (reference code with the year bracketed) followed by a numerical link to the found value (reference text) as a footnote.
  40 
  41 cite=bktlk: insert a footnote into the page as the search key (reference code with the year bracketed) as the link to the found value (reference text) as a footnote.
  42 
  43 cite=fn: insert a footnote into the page as a numerical link only with the found value (reference text) as the footnote.
  44 
  45 cite=key: insert only the key (reference code) into the page. No link or footnote is created.
  46 
  47 cite=keylk: insert a footnote into the page as the search key (reference code) as the link to the found value (reference text) as the footnote.
  48 
  49 cite=keyfn: insert a footnote into the page as the search key (reference code) followed by a numerical link to the found value (reference text) as the footnote (default).
  50 
  51 cite=tip: insert key (reference code) and provide found value (reference text) as a popup tip. Tips can also be turned on for all references (other than cite=val) on the page using #pragma ref-tips on. 
  52 
  53 cite=val: insert only the found value (reference text) into the page.  No link or footnote is generated.
  54 
  55 dictpage(s): dictionary page name. If it starts with ^, a regex of dictpages to include (case sensitive).
  56 
  57 #pragma ref-cite citetype: set the default cite type for the page.
  58 
  59 #pragma ref-debug wiki: shows the wiki markup without processing it.
  60 
  61 #pragma ref-debug html: shows the markup as html.
  62 
  63 #pragma ref-dict dictpage(s): dictionary to be used by default for that page, replacing the wiki-wide default of RefDict.
  64 
  65 #pragma ref-ital off: stops 'et al' in key being converted to italics.
  66 
  67 #pragma ref-link-bkt string: to distinguish the link when ref-super=off, sets string to bracket footnote forward link number for cite=fn, keyfn or bktfn, replacing the default '[^]'; where ^ indicates the insertion point of the link (only one ^ can appear in the string). Set in single or double quotes, or backslash pair.
  68 
  69 #pragma ref-super off: turns off superscripting of numerical forward link to the footnote.
  70 
  71 #pragma ref-tips on: turns on popup tips for all citation tips other than cite=val.
  72 
  73 #pragma ref-year-bkt string: string to bracket year when cite=bktfn, bktlk or bkt, replacing the default ' (^)'; where ^ indicates the insertion point of the year (only one ^ can appear in the string). Set in single or double quotes, or backslash pair.
  74 
  75 @copyright: 2010 Ian Riley <ian.riley@adelaide.edu.au>
  76 @licence: GNU GPL, see COPYING for details.
  77 
  78 History
  79 
  80 Version 2.5 - 20.20.2012: added option to display found text as popup tips.
  81 
  82 Version 2.4 - 10.10.2012: forced all search strings to be escaped for regex specials, to allow for example the inclusion of a '?' in the date of the reference key.
  83 
  84 Version 2.3.- 09.05.2011: minor bug fix
  85 
  86 Version 2.2 - 12.01.2011: streamlined the unwrap function.
  87 
  88 Version 2.1 - 27.12.2010: some html security improvements.
  89 
  90 Version 2.0 - 25.12.2010: dict specification optional (for cleaner markup), added key linking and display, added additional pragma to set cite type for the whole page (again for cleaner markup), to turn off superscripting and to add alternative demarkation of numbered links and to change year demarkation text. Macro call without arguments to raise a citation needed reminder.
  91 
  92 Version 1.0 - 19.12.2010: initial version.
  93 
  94 Developed from:
  95 
  96 (1) MoinMoin - Include macro
  97 
  98 @copyright: 2000-2004 Juergen Hermann <jh@web.de>,
  99             2000-2001 Richard Jones <richard@bizarsoftware.com.au>
 100 @license: GNU GPL, see COPYING for details.
 101 
 102 (2) http://moinmo.in/FeatureRequests/IncludeMacroInline using code of Junyx 2008-03-24 20:36:25
 103 
 104 """
 105 
 106 Dependencies = ["time"] 
 107 
 108 import re
 109 from MoinMoin import wikiutil
 110 from MoinMoin.Page import Page
 111 from MoinMoin.parser.text_moin_wiki import Parser as WikiParser
 112 
 113 # remove character bracketing 'wrapped', if it is in 'wrappers'  
 114 def unwrap(wrapped,wrappers):
 115     if (wrapped[0] in wrappers) and wrapped.endswith(wrapped[0]):
 116         wrapped = wrapped[1:-1]       
 117     return wrapped
 118 
 119 _msg = u'<strong class="error">Ref(): %s</strong>'
 120 _msg_bad_arg = _msg % (u'invalid arguments {%s}')
 121 _msg_recursion = _msg % (u'recursive use of {%s} forbidden')
 122 _msg_match_none = _msg % (u'no match for key={%s} in dict={%s}')
 123 _msg_match_many = _msg % (u'%s matches for key={%s} in dict={%s}')
 124 _msg_bad_insert = _msg % (u'Single insertion point ^ in {#pragma %s %s} obligatory')
 125 _msg_ref_needed = u'[citation needed]'
 126 
 127 _kw_cite = ['fn','keyfn','bktfn','keylk','bktlk','tip','key','val','bkt']
 128 
 129 _df_cite = u'keyfn'
 130 _df_dict = u'RefDict'
 131 
 132 _out_fn = u'<<FootNote(%s)>>'
 133 _out_keyfn = u'%s' + _out_fn
 134 _out_year_bkt = u' (%s)'
 135 _out_esc = u'{{{%s}}}'
 136 _out_link_bkt = u'[%s]'
 137 _out_tip = u'<span style="cursor: help; border-bottom: 1px dashed;" title="%s">%s</span>'
 138 
 139 _arg_ref = r'(\s*(ref=)?(?P<quote>[\'"])(?P<ref>.+)?(?P=quote))?'
 140 _arg_cite = r'(,\s*(cite=)?(?P<cite>(fn|keyfn|bktfn|keylk|bktlk|tip|key|val|bkt)))?'
 141 _arg_dict = r'(,\s*(dict=)?(?P<dict>[^,.]+?))?'
 142 _args_re_pattern = r'^%s((%s)?%s)?$' % (_arg_ref, _arg_cite, _arg_dict, )
 143 _args_re_pattern2 = r'^%s((%s)?%s)?$' % (_arg_ref, _arg_dict, _arg_cite, )
 144 
 145 def execute(macro, text, args_re=re.compile(_args_re_pattern)):
 146     request = macro.request
 147     _ = request.getText
 148 
 149     def dec_page():
 150         # decrease or remove pagelist marker
 151         if this_page._macroRef_pagelist[inc_name] > 1:
 152             this_page._macroRef_pagelist[inc_name] = this_page._macroRef_pagelist[inc_name] - 1
 153         else:
 154             del this_page._macroRef_pagelist[inc_name]
 155 
 156     # return immediately if getting links for the current page
 157     if request.mode_getpagelinks:
 158         return ('')
 159 
 160     # parse and check arguments
 161     args = text and args_re.match(text)
 162     if not args and text != '':
 163         args_re = re.compile(_args_re_pattern2)
 164         args = text and args_re.match(text)
 165         if not args and text != '':
 166             return (_msg_bad_arg % (text, ))
 167 
 168     # flag reference needed
 169     if text == '':
 170         return (_msg_ref_needed)
 171     elif args.group('ref') == None:
 172         return (_msg_ref_needed)
 173 
 174     # get cite or apply page default
 175     cite = args.group('cite')
 176     if cite == None:
 177         cite = macro.request.getPragma('ref-cite')
 178         if not (cite in _kw_cite):
 179             cite = _df_cite
 180       
 181     # prepare page
 182     result = []
 183     result_pending = []
 184     this_page = macro.formatter.page
 185     if not hasattr(this_page, '_macroRef_pagelist'):
 186         this_page._macroRef_pagelist = {}
 187 
 188     # establish dictionary page to be searched    
 189     dict_name = args.group('dict')
 190     if dict_name == None:
 191         dict_name = macro.request.getPragma('ref-dict')
 192         if dict_name == None:
 193             dict_name = _df_dict
 194 
 195     # get list of dictionary pages to search
 196     inc_name = wikiutil.AbsPageName(this_page.page_name, dict_name)
 197     pagelist = [inc_name]
 198     if inc_name.startswith("^"):
 199         try:
 200             inc_match = re.compile(inc_name)
 201         except re.error:
 202             pass # treat as plain page name
 203         else:
 204             # get user filtered readable page list
 205             pagelist = request.rootpage.getPageList(filter = inc_match.match)
 206 
 207     ref_key = ''
 208     ref_match_count = 0
 209     val_end = '\n'
 210 
 211     # iterate over pages
 212     for inc_name in pagelist:
 213         if not request.user.may.read(inc_name):
 214             continue
 215         if inc_name in this_page._macroRef_pagelist:
 216             dec_page()
 217             return (_msg_recursion % (inc_name, ))
 218 
 219         fmt = macro.formatter.__class__(request, is_included=True)
 220         fmt._base_depth = macro.formatter._base_depth
 221         inc_page = Page(request, inc_name, formatter=fmt)
 222         if not inc_page.exists():
 223             continue
 224         inc_page._macroRef_pagelist = this_page._macroRef_pagelist
 225 
 226         # prepare for ref_key search 
 227         body = inc_page.get_raw_body() + '\n'
 228         ref_pos = 0
 229         end_pos = -1
 230         ref_found = True
 231 
 232         # need to reset ref_key within loop, as it might have been modified
 233         ref_key = args.group('ref')
 234 
 235         # remove any WikiName escape character
 236         ref_key = ref_key.replace("!","")
 237 
 238         if ref_key:
 239             #try:
 240             #    ref_match = re.compile(ref_key + ':: ', re.M).search(body)
 241             #except re.error:
 242             ref_match = re.compile(re.escape(ref_key + ':: '), re.M).search(body)
 243             if ref_match:
 244                 ref_pos = ref_match.end() 
 245             else:
 246                 ref_found = False
 247 
 248         if val_end:
 249             #try:
 250             #    end_match = re.compile(val_end, re.M).search(body, ref_pos)
 251             #except re.error:
 252             end_match = re.compile(re.escape(val_end), re.M).search(body, ref_pos)
 253             if end_match:
 254                 end_pos = end_match.start()
 255             else:
 256                 ref_found = False
 257 
 258         if ref_found:
 259             inc_page.set_raw_body(body[ref_pos:end_pos], modified=True)
 260             ref_match_count = ref_match_count + 1
 261 
 262         #TODO: loop back over remainder of body to find duplicate keys to allow user to be warned.
 263         # Perhaps is would be better to use WikiDict calls to get the match, 
 264         # but I think this can only find the first match, not duplicates.
 265         
 266         # set or increment pagelist marker
 267         this_page._macroRef_pagelist[inc_name] = this_page._macroRef_pagelist.get(inc_name, 0) + 1
 268 
 269         # need to reset ref_key within loop, as it might have been modified
 270         ref_key = args.group('ref')
 271 
 272         # make et al italic unless #pragma ref-ital off
 273         if macro.request.getPragma('ref-ital') != 'off':
 274             ref_key = ref_key.replace("et al","''et al''")
 275 
 276         # bracket year (last 'word' of ref key) for cite=bktfn, bktlk or bkt
 277         if cite in ['bktfn', 'bktlk', 'bkt']:
 278             ref_key = ref_key.rstrip()
 279             ref_len = len(ref_key)
 280             year_pos = ref_key.rfind(' ')
 281             year_bkt = _out_year_bkt
 282             if macro.request.getPragma('ref-year-bkt') != None:
 283                 #TODO check if the escape call really needed or does getPragma handle the html risk
 284                 year_bkt = wikiutil.escape(macro.request.getPragma('ref-year-bkt'))
 285                 if year_bkt.count("^") != 1:                  
 286                     dec_page()
 287                     return (_msg_bad_insert % ('ref-year-bkt', year_bkt)) 
 288                 year_bkt = year_bkt.replace("_"," ").replace("^","%s",1)
 289                 year_bkt = unwrap(year_bkt,"\\\"'")
 290             year_str = year_bkt % (ref_key[year_pos+1:ref_len])
 291             ref_key = ref_key[0:year_pos] + year_str
 292 
 293         if ref_found:
 294             # insert the ref value into the page as a footnote only
 295             if cite == 'fn':
 296                 result_pending = _out_fn % (inc_page.get_body())
 297 
 298             # insert the ref key (modified) into the page followed by the ref value into the page as a footnote
 299             elif cite in ['keyfn','bktfn','keylk','bktlk']:
 300                 result_pending = _out_keyfn % (ref_key, inc_page.get_body())
 301             
 302             # do nothing now, but later insert ref key with ref value as tip    
 303             elif cite == 'tip':
 304                 result_pending = u''
 305 
 306             # insert the ref key into the page only
 307             elif cite in ['key', 'bkt']:
 308                 result_pending = ref_key
 309 
 310             # insert the ref value into the page only
 311             elif cite == 'val':
 312                 result_pending = inc_page.get_body()
 313 
 314             if macro.request.getPragma('ref-debug') == 'wiki':
 315                 result_pending = _out_esc % (result_pending)
 316 
 317             result_pending = wikiutil.renderText(request, WikiParser, result_pending).rstrip(' ')
 318 
 319             # post formating of html citation and link
 320             if cite in ('keylk','bktlk'):
 321                 ref_key = ref_key.replace("!","")
 322                 ref_key = ref_key.replace("''et al''","<em>et al</em>")
 323                 result_pending = (result_pending.replace("<sup>","")
 324                                                 .replace("</sup>","")
 325                                                 .replace(ref_key,"")
 326                                                 .replace("</a>","")
 327                                                 .rstrip(" 0123456789") + ref_key + "</a>")
 328 
 329             if cite in ('fn','keyfn','bktfn'):
 330                 if macro.request.getPragma('ref-super') == 'off':
 331                     link_bkt = _out_link_bkt
 332                     if macro.request.getPragma('ref-link-bkt') != None:
 333                         #TODO check if the escape call really needed or does getPragma handle the html risk
 334                         link_bkt = wikiutil.escape(macro.request.getPragma('ref-link-bkt'))
 335                         if link_bkt.count("^") != 1:
 336                             dec_page()
 337                             return (_msg_bad_insert % ('ref-link-bkt', link_bkt, ))
 338                         link_bkt = (unwrap(link_bkt,"\\\"'").replace("_"," ")
 339                                                             .replace("^","%s",1))
 340                     t = tuple(result_pending.replace("<sup>","<>")
 341                                             .replace("</a>","</a><>")
 342                                             .replace("</sup>","")
 343                                             .split("<>"))
 344                     result_pending = ('%s' + link_bkt + '%s') % (t[0],t[1],t[2])
 345                     
 346             # escape HTML including quotes
 347             tip = wikiutil.escape(inc_page.get_body(), quote=True)
 348                 
 349             #convert to html then remove tags to remove wiki markup
 350             tip = wikiutil.renderText(request, WikiParser, tip)
 351             tip = re.sub('<[^>]*>', '', tip)
 352                 
 353             if cite == 'tip':
 354                 #markup ref_key
 355                 result_pending = wikiutil.renderText(request, WikiParser, ref_key)
 356 
 357             if (cite == 'tip') or (cite != 'val' and macro.request.getPragma('ref-tips') == 'on'):
 358 
 359                 result_pending = _out_tip % (tip, result_pending)
 360 
 361             if macro.request.getPragma('ref-debug') == 'html':
 362                 result_pending = _out_esc % (result_pending)
 363                 result_pending = wikiutil.renderText(request, WikiParser, result_pending).rstrip(' ')
 364 
 365             result.append(result_pending)
 366 
 367         # decrease or remove pagelist marker
 368         dec_page()
 369 
 370     # no match and last page in list, so insert warning message
 371     # need to reset ref_key within loop, as it might have been modified
 372     ref_key = args.group('ref')
 373     if ref_match_count == 0:
 374         result.append(_msg_match_none % (ref_key, dict_name, ))
 375     elif ref_match_count > 1:
 376         result.append(_msg_match_many % (ref_match_count, ref_key, dict_name, ))
 377 
 378     return ''.join(result)

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