1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - ImprovedMoinSearch library 4 5 @copyright: 2010 Paul Boddie <paul@boddie.org.uk> 6 @license: GNU GPL (v2 or later), see COPYING.txt for details. 7 """ 8 9 from MoinMoin.search import searchPages 10 from MoinMoin.Page import Page 11 from MoinMoin import wikiutil 12 import re 13 14 heading_regexp = re.compile(r"^(?P<level>=+)(?P<heading>.*?)(?P=level)$", re.UNICODE | re.MULTILINE) 15 paragraph_regexp = re.compile(r"(?P<paragraph>(?:^[^#=\s].*$\n)+)", re.UNICODE | re.MULTILINE) 16 17 def range_groups(min_name, max_name): 18 return r"(?P<%s>\d+)?(?:\s*-\s*(?P<%s>\d+))?" % (min_name, max_name) 19 20 format_options_regexp = re.compile(r"(" 21 "(?P<heading>(heading|title|h)\s*" + range_groups("min_heading", "max_heading") + ")" 22 "|(?P<paragraph>(paragraph|para|p)\s*(?P<paragraph_number>\d+)?)" 23 ")", re.UNICODE) 24 25 def getSearchResultPages(request, query, **kw): 26 27 """ 28 Return matching pages using the given 'request' and search 'query'. Optional 29 keyword arguments are passed to the underlying search infrastructure. 30 """ 31 32 results = searchPages(request, query, **kw) 33 return results.hits 34 35 def getFirstPageHeading(request, page, start=0, min_level=None, max_level=None): 36 37 """ 38 Using the given 'request', return the first heading in the given 'page' 39 from the given 'start' point (optional, defaulting to the start of the page) 40 having a heading level of at least 'min_level' (which is undefined if not 41 specified) and at most 'max_level' (which is undefined if not specified). 42 43 A tuple containing the heading and the span (the start offset and the end 44 offset as a tuple) is returned for a successful retrieval. Otherwise, None 45 is returned. 46 """ 47 48 full_page = Page(request, page.page_name) 49 body = full_page.get_raw_body() 50 if start != 0: 51 body = body[start:] 52 53 for match in heading_regexp.finditer(body): 54 level = len(match.group("level")) 55 56 if (min_level is None or level >= min_level) and \ 57 (max_level is None or level <= max_level): 58 59 return match.group("heading"), match.span() 60 61 return None 62 63 def getParagraph(request, page, start=0, number=None): 64 65 full_page = Page(request, page.page_name) 66 body = full_page.get_raw_body() 67 if start != 0: 68 body = body[start:] 69 70 for i, match in enumerate(paragraph_regexp.finditer(body)): 71 if number is None or i == max(0, number - 1): 72 return match.group("paragraph"), match.span() 73 74 return None 75 76 def getPageName(request, page): 77 return page.page_name 78 79 def formatResultPages(request, formatter, pages, paging, format, page_from=0): 80 81 """ 82 Using the given 'request' and 'formatter', return a formatted string showing 83 the result 'pages', providing paging controls when 'paging' is set to a true 84 value, and providing page details according to the given 'format'. 85 86 If the optional 'pages_from' parameter is set, the result pages from the 87 given result (specified within a range from 0 to the length of the 'pages' 88 collection) will be shown. 89 """ 90 91 actions = [] 92 93 if format: 94 for match in format_options_regexp.finditer(format): 95 if match.group("heading"): 96 actions.append((getFirstPageHeading, map(int_or_none, (match.group("min_heading"), match.group("max_heading"))))) 97 elif match.group("paragraph"): 98 actions.append((getParagraph, map(int_or_none, (match.group("paragraph_number"),)))) 99 else: 100 actions.append((getPageName, ())) 101 102 # Use paging only when there are enough results. 103 104 results_per_page = request.cfg.search_results_per_page 105 paging = paging and len(pages) > results_per_page 106 107 if paging: 108 pages_to_show = pages[page_from:page_from + results_per_page] 109 else: 110 pages_to_show = pages 111 112 # Prepare the output. 113 114 output = [] 115 output.append(formatter.number_list(on=1, start=page_from + 1)) 116 117 for page in pages_to_show: 118 output.append(formatter.listitem(on=1)) 119 120 start = 0 121 first = 1 122 for action, args in actions: 123 result = action(request, page, start, *args) 124 125 if result is not None: 126 if first: 127 output.append(formatter.pagelink(on=1, pagename=page.page_name)) 128 else: 129 output.append(" ") 130 131 text, span = result 132 output.append(formatter.text(text)) 133 134 # Position the search for the next action. 135 136 _start, _end = span 137 start = _end + 1 138 139 if first: 140 output.append(formatter.pagelink(on=0)) 141 142 first = 0 143 144 output.append(formatter.listitem(on=0)) 145 146 output.append(formatter.number_list(on=0)) 147 148 # Show paging navigation. 149 150 if paging: 151 output.append(formatPagingNavigation(request, formatter, pages, page_from)) 152 153 return "".join(output) 154 155 def formatPagingNavigation(request, formatter, pages, page_from=0): 156 157 """ 158 Using the given 'request' and 'formatter', return a formatted string showing 159 the paging navigation for the result 'pages', according to the 'page_from' 160 indicator which provides the current position in the result set. 161 """ 162 163 page = formatter.page 164 pagename = page.page_name 165 _ = request.getText 166 167 output = [] 168 169 results_per_page = request.cfg.search_results_per_page 170 number_of_results = len(pages) 171 172 pages_total = number_of_results / results_per_page 173 pages_before = page_from / results_per_page 174 pages_after = ((number_of_results - page_from) / results_per_page) - 1 175 176 querydict = wikiutil.parseQueryString(request.query_string) 177 178 output.append(formatter.paragraph(on=1)) 179 output.append(formatter.text(_("Result pages:"))) 180 output.append(formatter.text(" ")) 181 182 n = 0 183 while n < pages_before: 184 output.append(formatter.pagelink(on=1, pagename=pagename, querystr=getPagingQueryString(querydict, n * results_per_page))) 185 output.append(formatter.text(str(n + 1))) 186 output.append(formatter.pagelink(on=0)) 187 output.append(formatter.text(" ")) 188 n += 1 189 190 output.append(formatter.text(str(n + 1))) 191 output.append(formatter.text(" ")) 192 n += 1 193 194 while n <= pages_total: 195 output.append(formatter.pagelink(on=1, pagename=pagename, querystr=getPagingQueryString(querydict, n * results_per_page))) 196 output.append(formatter.text(str(n + 1))) 197 output.append(formatter.pagelink(on=0)) 198 output.append(formatter.text(" ")) 199 n += 1 200 201 output.append(formatter.paragraph(on=0)) 202 203 return "".join(output) 204 205 def getPagingQueryString(querydict, page_from): 206 querydict["from"] = page_from 207 return wikiutil.makeQueryString(querydict) 208 209 def int_or_none(x): 210 if x is None: 211 return x 212 else: 213 return int(x) 214 215 # vim: tabstop=4 expandtab shiftwidth=4