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(page): 94 95 "Return a nicely formatted title/name for the given 'page'." 96 97 if isMoin15(): 98 title = page.split_title(page.request, force=1) 99 else: 100 title = page.split_title(force=1) 101 102 return title.replace("_", " ").replace("/", u" ? ") 103 104 def linkToPage(request, page, text, query_string=None): 105 106 """ 107 Using 'request', return a link to 'page' with the given link 'text' and 108 optional 'query_string'. 109 """ 110 111 text = wikiutil.escape(text) 112 113 if isMoin15(): 114 url = wikiutil.quoteWikinameURL(page.page_name) 115 if query_string is not None: 116 url = "%s?%s" % (url, query_string) 117 return wikiutil.link_tag(request, url, text, getattr(page, "formatter", None)) 118 else: 119 return page.link_to_raw(request, text, query_string) 120 121 def execute(macro, args): 122 123 """ 124 Execute the 'macro' with the given 'args': an optional list of selected 125 category names (categories whose pages are to be shown). 126 """ 127 128 request = macro.request 129 fmt = macro.formatter 130 page = fmt.page 131 132 # Interpret the arguments. 133 134 try: 135 selected_category_names = args and wikiutil.parse_quoted_separated(args, name_value=False) or [] 136 except AttributeError: 137 selected_category_names = args.split(",") 138 139 selected_category_names = [arg for arg in selected_category_names if arg] 140 141 # Get the categories. 142 143 categories = getCategoryMapping(getCategories(request), request) 144 145 # Generate a menu with the categories, together with expanded submenus for 146 # the categories employed by the current page, the category represented by 147 # the current page, or for those categories specified in the macro 148 # arguments. 149 150 output = [] 151 output.append(fmt.bullet_list(on=1, attr={"class" : "category-menu"})) 152 153 for category in categories: 154 category_name, category_pagename = category 155 156 # Work out whether the current page belongs to this category. 157 158 page_is_category = page.page_name == category_pagename 159 category_membership_regexp = re.compile(category_membership_regexp_str % category_pagename) 160 page_is_in_category = category_membership_regexp.search(page.get_raw_body()) 161 162 # Generate the submenu where appropriate. 163 164 if selected_category_names and category_name in selected_category_names or \ 165 not selected_category_names and (page_is_category or page_is_in_category): 166 167 if page_is_category: 168 output.append(fmt.listitem(on=1, attr={"class" : "selected current"})) 169 output.append(fmt.text(category_name)) 170 else: 171 output.append(fmt.listitem(on=1, attr={"class" : "selected"})) 172 output.append(fmt.pagelink(on=1, pagename=category_pagename)) 173 output.append(fmt.text(category_name)) 174 output.append(fmt.pagelink(on=0, pagename=category_pagename)) 175 176 output.append(fmt.bullet_list(on=1, attr={"class" : "category-submenu"})) 177 178 # Get the pages and page names in the category. 179 180 pages_in_category = getPages(category_pagename, request) 181 182 # Visit each page in the category. 183 184 last_parts = [] 185 186 for page_in_category in pages_in_category: 187 pagename = page_in_category.page_name 188 189 # Get a real page, not a result page. 190 191 real_page_in_category = Page(request, pagename) 192 193 # Get a pretty version of the page name. 194 195 pretty_pagename = getPrettyPageName(real_page_in_category) 196 197 if page.page_name == pagename: 198 output.append(fmt.listitem(on=1, attr={"class" : "selected"})) 199 else: 200 output.append(fmt.listitem(on=1)) 201 202 # Abbreviate long hierarchical names. 203 204 parts = pretty_pagename.split(u" ? ") 205 common = 0 206 for last, current in map(None, last_parts, parts): 207 if last == current: 208 common += 1 209 else: 210 break 211 212 # Use the arrows to indicate subpages. 213 214 prefix = u" ? " * common 215 suffix = u" ? ".join(parts[common:]) 216 217 output.append(fmt.text(prefix)) 218 219 # Link to the page using the pretty name. 220 221 output.append(linkToPage(request, real_page_in_category, suffix)) 222 output.append(fmt.listitem(on=0)) 223 224 last_parts = parts 225 226 output.append(fmt.bullet_list(on=0)) 227 output.append(fmt.listitem(on=0)) 228 229 # Otherwise generate a simple link. 230 231 else: 232 output.append(fmt.listitem(on=1)) 233 output.append(fmt.pagelink(on=1, pagename=category_pagename)) 234 output.append(fmt.text(category_name)) 235 output.append(fmt.pagelink(on=0, pagename=category_pagename)) 236 output.append(fmt.listitem(on=0)) 237 238 output.append(fmt.bullet_list(on=0)) 239 240 return ''.join(output) 241 242 # vim: tabstop=4 expandtab shiftwidth=4