1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - CategoryMenu Macro 4 5 @copyright: 2008, 2009 by Paul Boddie <paul@boddie.org.uk> 6 @copyright: 2000-2004 Juergen Hermann <jh@web.de>, 7 2005-2008 MoinMoin:ThomasWaldmann. 8 @license: GNU GPL (v2 or later), see COPYING.txt for details. 9 """ 10 11 from MoinMoin.Page import Page 12 from MoinMoin import wikiutil, search, version 13 import re 14 15 __version__ = "0.3" 16 17 Dependencies = ['pages'] 18 19 # Regular expressions where MoinMoin does not provide the required support. 20 21 category_regexp = None 22 23 # From MoinMoin.search.queryparser... 24 25 category_membership_regexp_str = r'(?m)(^-----*\s*\r?\n)(^##.*\r?\n)*^(?!##)(.*)\b%s\b' 26 27 # Utility functions. 28 29 def isMoin15(): 30 return version.release.startswith("1.5.") 31 32 def getCategoryPattern(request): 33 global category_regexp 34 35 try: 36 return request.cfg.cache.page_category_regexact 37 except AttributeError: 38 39 # Use regular expression from MoinMoin 1.7.1 otherwise. 40 41 if category_regexp is None: 42 category_regexp = re.compile(u'^%s$' % ur'(?P<all>Category(?P<key>(?!Template)\S+))', re.UNICODE) 43 return category_regexp 44 45 # The main activity functions. 46 47 def getCategories(request): 48 49 """ 50 From the AdvancedSearch macro, return a list of category page names using 51 the given 'request'. 52 """ 53 54 # This will return all pages with "Category" in the title. 55 56 cat_filter = getCategoryPattern(request).search 57 return request.rootpage.getPageList(filter=cat_filter) 58 59 def getCategoryMapping(category_pagenames, request): 60 61 """ 62 For the given 'category_pagenames' return a list of tuples of the form 63 (category name, category page name) using the given 'request'. 64 """ 65 66 cat_pattern = getCategoryPattern(request) 67 mapping = [] 68 for pagename in category_pagenames: 69 name = cat_pattern.match(pagename).group("key") 70 if name != "Category": 71 mapping.append((name, pagename)) 72 mapping.sort() 73 return mapping 74 75 def getPages(pagename, request): 76 77 "Return the links minus category links for 'pagename' using the 'request'." 78 79 query = search.QueryParser().parse_query('category:%s' % pagename) 80 if isMoin15(): 81 results = search.searchPages(request, query) 82 results.sortByPagename() 83 else: 84 results = search.searchPages(request, query, "page_name") 85 86 cat_pattern = getCategoryPattern(request) 87 pages = [] 88 for page in results.hits: 89 if not cat_pattern.match(page.page_name): 90 pages.append(page) 91 return pages 92 93 def getPrettyPageName(request, page): 94 95 """ 96 Using 'request', return a nicely formatted title/name for the given 'page'. 97 """ 98 99 if isMoin15(): 100 title = page.split_title(request, force=1) 101 else: 102 title = page.split_title(force=1) 103 104 return title.replace("_", " ").replace("/", u" ? ") 105 106 def linkToPage(request, page, text, query_string=None): 107 108 """ 109 Using 'request', return a link to 'page' with the given link 'text' and 110 optional 'query_string'. 111 """ 112 113 text = wikiutil.escape(text) 114 115 if isMoin15(): 116 url = wikiutil.quoteWikinameURL(page.page_name) 117 if query_string is not None: 118 url = "%s?%s" % (url, query_string) 119 return wikiutil.link_tag(request, url, text, getattr(page, "formatter", None)) 120 else: 121 return page.link_to_raw(request, text, query_string) 122 123 def execute(macro, args): 124 125 """ 126 Execute the 'macro' with the given 'args': an optional list of selected 127 category names (categories whose pages are to be shown). 128 """ 129 130 request = macro.request 131 fmt = macro.formatter 132 page = fmt.page 133 134 # Interpret the arguments. 135 136 try: 137 selected_category_names = args and wikiutil.parse_quoted_separated(args, name_value=False) or [] 138 except AttributeError: 139 selected_category_names = args.split(",") 140 141 selected_category_names = [arg for arg in selected_category_names if arg] 142 143 # Get the categories. 144 145 categories = getCategoryMapping(getCategories(request), request) 146 147 # Generate a menu with the categories, together with expanded submenus for 148 # the categories employed by the current page, the category represented by 149 # the current page, or for those categories specified in the macro 150 # arguments. 151 152 output = [] 153 output.append(fmt.bullet_list(on=1, attr={"class" : "category-menu"})) 154 155 for category in categories: 156 category_name, category_pagename = category 157 158 # Work out whether the current page belongs to this category. 159 160 page_is_category = page.page_name == category_pagename 161 category_membership_regexp = re.compile(category_membership_regexp_str % category_pagename) 162 page_is_in_category = category_membership_regexp.search(page.get_raw_body()) 163 164 # Generate the submenu where appropriate. 165 166 if selected_category_names and category_name in selected_category_names or \ 167 not selected_category_names and (page_is_category or page_is_in_category): 168 169 if page_is_category: 170 output.append(fmt.listitem(on=1, attr={"class" : "selected current"})) 171 output.append(fmt.text(category_name)) 172 else: 173 output.append(fmt.listitem(on=1, attr={"class" : "selected"})) 174 output.append(fmt.pagelink(on=1, pagename=category_pagename)) 175 output.append(fmt.text(category_name)) 176 output.append(fmt.pagelink(on=0, pagename=category_pagename)) 177 178 output.append(fmt.bullet_list(on=1, attr={"class" : "category-submenu"})) 179 180 # Get the pages and page names in the category. 181 182 pages_in_category = getPages(category_pagename, request) 183 184 # Visit each page in the category. 185 186 last_parts = [] 187 188 for page_in_category in pages_in_category: 189 pagename = page_in_category.page_name 190 191 # Get a real page, not a result page. 192 193 real_page_in_category = Page(request, pagename) 194 195 # Get a pretty version of the page name. 196 # NOTE: MoinMoin 1.5: request supplied. 197 198 pretty_pagename = getPrettyPageName(request, real_page_in_category) 199 200 if page.page_name == pagename: 201 output.append(fmt.listitem(on=1, attr={"class" : "selected"})) 202 else: 203 output.append(fmt.listitem(on=1)) 204 205 # Abbreviate long hierarchical names. 206 207 parts = pretty_pagename.split(u" ? ") 208 common = 0 209 for last, current in map(None, last_parts, parts): 210 if last == current: 211 common += 1 212 else: 213 break 214 215 # Use the arrows to indicate subpages. 216 217 prefix = u" ? " * common 218 suffix = u" ? ".join(parts[common:]) 219 220 output.append(fmt.text(prefix)) 221 222 # Link to the page using the pretty name. 223 224 output.append(linkToPage(request, real_page_in_category, suffix)) 225 output.append(fmt.listitem(on=0)) 226 227 last_parts = parts 228 229 output.append(fmt.bullet_list(on=0)) 230 output.append(fmt.listitem(on=0)) 231 232 # Otherwise generate a simple link. 233 234 else: 235 output.append(fmt.listitem(on=1)) 236 output.append(fmt.pagelink(on=1, pagename=category_pagename)) 237 output.append(fmt.text(category_name)) 238 output.append(fmt.pagelink(on=0, pagename=category_pagename)) 239 output.append(fmt.listitem(on=0)) 240 241 output.append(fmt.bullet_list(on=0)) 242 243 return ''.join(output) 244 245 # vim: tabstop=4 expandtab shiftwidth=4