""" MoinMoin - SearchInPagesAndSort Macro A line-oriented search macro over multiple pages, with sorting Pascal Bauermeister <pascal.bauermeister@smartdata.ch> [[SearchInPages(PageRegex,TextRegex,SortKey)]] Search for TextRegex in pages marching PagesRegex, and sort on 1) SortKey 2) TextRegex 3) found line Original version: * 2003/04/24 10:32:04 Updates: * Wed Oct 29 14:48:02 CET 2003 works with MoinMoin Release 1.1 [Revision 1.173] and Python 2.3.2 """ # Imports import cgi, os, re, string, sys, time, urllib, cStringIO from MoinMoin import config, user, util, wikiutil from MoinMoin.Page import Page, webapi from MoinMoin.parser.wiki import Parser _arg_page = r'(?P<hquote1>[\'"])(?P<hpage>.+?)(?P=hquote1)' _arg_text = r'(?P<hquote2>[\'"])(?P<htext>.+?)(?P=hquote2)' _arg_key = r'(?P<hquote3>[\'"])(?P<hkey>.+?)(?P=hquote3)' _args_re = re.compile(r'^(%s(,%s(,%s)?)?)?$' % (_arg_page, _arg_text, _arg_key)) def execute(macro, text, args_re=_args_re): result = "" # parse and check arguments args = args_re.match(text) if text is None or not args: return ( '<p><strong class="error">Invalid SearchInPages arguments "%s"!' + '</strong></p>' ) % text text = args.group('htext') pages = args.group('hpage') key = args.group('hkey') # get a list of pages matching the PageRegex try: pages_re = re.compile(pages, re.IGNORECASE) except re.error: pages_re = re.compile(re.escape(pages), re.IGNORECASE) all_pages = wikiutil.getPageList(config.text_dir) hits = filter(pages_re.search, all_pages) hits.sort() if len(hits) == 0: return '<p><strong class="error">No page matching "%s"!</strong></p>' % (pages) # compile all regex try: text_re = re.compile(text, re.IGNORECASE) except: text_re = re.compile(re.escape(text), re.IGNORECASE) if key != None: try: key_re = re.compile(key, re.IGNORECASE) except: key_re = re.compile(re.escape(key), re.IGNORECASE) # we will collect matching lines in each matching page all_matches = [] for page_name in hits: body = Page(page_name).get_raw_body() pos = 0 last_start = -1 last_end = -1 while 1: keep_line = 1 # search text match = text_re.search(body, pos) if not match: break pos = match.end()+1 # cut before start of line start_pos = match.start() rev = 0 while body[start_pos] != '\n' and start_pos: start_pos = start_pos - 1 rev = 1 if rev: start_pos = start_pos + 1 # cut at end of line end_pos = body.find("\n", match.end()) # extract line line = body[start_pos:end_pos].strip() # store this record if it differs from previous one if start_pos == last_start or end_pos == last_end: keep_line = 0 # store this record if it it is not a comment if line.startswith("##"): keep_line = 0 # store this record if it differs from previous one if keep_line: # remove possible list item leaders for heading in ["*", "1.", "a.", "A.", "i.", "I.", "1.#n"]: if line.startswith(heading): line = line.replace(heading, "", 1) line = line.strip() # find the sort key nbmatches = 0 keypos = 0 found = 0 while 1: if key == None: keyval = "" else: keymatch = key_re.search(line, keypos) if keymatch: keyval = line[keymatch.start():keymatch.end()] keypos = keymatch.end() nbmatches = nbmatches + 1 found = 1 else: if nbmatches>0: break keyval = "[unassigned]" # store info item = [] item.append(keyval) # key text item.append(body[match.start():match.end()]) # search text item.append(line) # line text item.append(page_name) # page name all_matches.append(item) if found == 0: break last_start = start_pos last_end = end_pos # sort and format records bullet_list_open = macro.formatter.bullet_list(1) bullet_list_close = macro.formatter.bullet_list(0) listitem_open = macro.formatter.listitem(1) listitem_close = macro.formatter.listitem(0) all_matches.sort() result = "" result = result+"\n" + bullet_list_open keyval = "" head_count = 0 # treat records for output for item in all_matches: pagename = item[3] text = item[2] if key != None and item[0] != keyval: # this is a new heading keyval = item[0] if head_count: result = result+"\n " + bullet_list_close result = result+"\n " + listitem_close head_count = head_count +1 result = result+"\n " + listitem_open result = result+ keyval result = result+"\n " + bullet_list_open # parse the text in wiki source format stdout = sys.stdout sys.stdout = cStringIO.StringIO() Parser(text, macro.request).format(macro.formatter) text_fmt = sys.stdout.getvalue() sys.stdout = stdout # correct the format if text_fmt.startswith("\n<p>"): text_fmt = text_fmt[4:] # insert text result = result+"\n " + listitem_open result = result + text_fmt result = result + " <font size=-1>" result = result + wikiutil.link_tag(pagename) result = result + "</font>" result = result+"\n " + listitem_close # all items done, close (hopefully) gracefully if head_count: result = result+"\n " + listitem_close result = result+"\n " + bullet_list_close if key != None: result = result+"\n " + listitem_close result = result+"\n" + bullet_list_close # done return result