Attachment '60010_macro_showcategory.patch'
Download 1 new macro ShowCategory: display a MediaWiki style category page
2
3 diff -r 0bfe351fb737 MoinMoin/macro/ShowCategory.py
4 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5 +++ b/MoinMoin/macro/ShowCategory.py Tue Nov 25 10:17:40 2008 +0800
6 @@ -0,0 +1,210 @@
7 +# -*- coding: utf-8 -*-
8 +"""
9 + CategoryList macro
10 +
11 + List Categories in Category.
12 +
13 + @copyright: 2008 Jiang Xin <worldhello.net@gmail.com>
14 + @license: GNU GPL, see COPYING for details.
15 +"""
16 +
17 +from MoinMoin import wikiutil2
18 +from MoinMoin.Page import Page
19 +
20 +_sysmsg = '<p><strong class="%s">%s</strong></p>'
21 +
22 +
23 +def show_category(request, categorylist, column=3, show_cat=True, show_page=True,
24 + show_summary=True, show_capital=True, sort=[], reverse=False,
25 + recursive=False, items=0, skipitems=0, exclude_template=True):
26 + cc = wikiutil2.CategoryCache(request)
27 + _ = request.getText
28 +
29 + if "all" in categorylist:
30 + show_cat = True
31 + show_page = False
32 + recursive = False
33 + summary_of_subcategories = "<h2>" + _("All categories") + "</h2>\n" + \
34 + _("There are %(count)d categories in this wiki.")
35 + summary_of_subpages = ""
36 + else:
37 + summary_of_subcategories = "<h2>" + _("Subcategories of <b>%(category)s</b>") + "</h2>\n" + \
38 + _("This category has the following %(count)d subcategories.")
39 + summary_of_subpages = "<h2>" + _("Pages in category <b>%(category)s</b>") + "</h2>\n" + \
40 + _("There are %(count)d pages are in this category.")
41 +
42 + result = []
43 +
44 + # show sub category
45 + if show_cat:
46 + for category in categorylist:
47 + itemlist = []
48 + for i in sorted( cc.get_sub_categories(category,recursive=recursive, exclude_template=exclude_template) ):
49 + name = i
50 + page = Page(request, name)
51 + entries = []
52 + for s in sort:
53 + if "title".startswith(s):
54 + pagetitle = page.pi.get("pragma_page_title" ,page.page_name)
55 + entries.append( pagetitle )
56 + name = pagetitle
57 + elif "timestamp".startswith(s):
58 + edit_info = page.edit_info()
59 + timestamp = edit_info.get('timestamp', 0)
60 + entries.append(timestamp)
61 + else:
62 + return (_sysmsg % ('error', _('Invalid sort arguments "%s"!')) % (s, ))
63 + linktext = "%s(%d/%d)" % ( name, len(cc.catcatdict.get(i,[])), len(cc.catpagedict.get(i,[])) )
64 + link = page.link_to(request, text=linktext, attachment_indicator=1)
65 + entries.extend( [name, link] )
66 + itemlist.append( entries )
67 +
68 + if itemlist:
69 + itemlist.sort(reverse=reverse)
70 + if len(itemlist[0]) > 2:
71 + itemlist=[ (x[-2], x[-1]) for x in itemlist ]
72 +
73 + if show_summary:
74 + result += [ summary_of_subcategories % {'category': category, 'count': len(itemlist)} ]
75 +
76 + if skipitems:
77 + itemlist=itemlist[skipitems:]
78 + if items:
79 + itemlist=itemlist[:items]
80 + result += wikiutil2.formatList(request, itemlist, column=column, show_capital=show_capital)
81 +
82 + # show pages belong to this category
83 + if show_page:
84 + for category in categorylist:
85 + itemlist = []
86 + for i in sorted( cc.get_sub_pages(category,recursive=recursive, exclude_template=exclude_template) ):
87 + name = i
88 + page = Page(request, name)
89 + entries = []
90 + for s in sort:
91 + if "title".startswith(s):
92 + pagetitle = page.pi.get("pragma_page_title" ,page.page_name)
93 + entries.append( pagetitle )
94 + name = pagetitle
95 + elif "timestamp".startswith(s):
96 + edit_info = page.edit_info()
97 + timestamp = edit_info.get('timestamp', 0)
98 + entries.append(timestamp)
99 + else:
100 + return (_sysmsg % ('error', _('Invalid sort arguments "%s"!')) % (s, ))
101 + link = page.link_to(request, text=name, attachment_indicator=1)
102 + entries.extend( [name, link] )
103 + itemlist.append( entries )
104 +
105 + if itemlist:
106 + itemlist.sort(reverse=reverse)
107 + if len(itemlist[0]) > 2:
108 + itemlist=[ (x[-2], x[-1]) for x in itemlist ]
109 +
110 + if show_summary:
111 + result += [ summary_of_subpages % {'category': category, 'count': len(itemlist)} ]
112 +
113 + if skipitems:
114 + itemlist=itemlist[skipitems:]
115 + if items:
116 + itemlist=itemlist[:items]
117 + result += wikiutil2.formatList(request, itemlist, column=column, show_capital=show_capital)
118 +
119 + return "\n".join(result)
120 +
121 +
122 +
123 +def execute(macro, args):
124 + global category_cache
125 + request = macro.request
126 + _ = request.getText
127 + show_cat = True
128 + show_page = True
129 + show_summary = True
130 + show_capital = True
131 + exclude_template = True
132 + column = 3
133 + category = []
134 + sort = []
135 + reverse = False
136 + recursive = False
137 + items = 0
138 + skipitems = 0
139 +
140 + if args:
141 + for i in args.split(','):
142 + i=i.strip()
143 + if "=" in i:
144 + key, val = i.split('=',1)
145 + else:
146 + key = i
147 + val = "yes"
148 + boolval = (val=="1" or val.lower().startswith("t") or val.lower().startswith("y"))
149 + if key.startswith("no"):
150 + boolval = not boolval
151 +
152 + if "nocategories".startswith(key) or "categories".startswith(key):
153 + show_cat = boolval
154 + continue
155 + elif "nopages".startswith(key) or "pages".startswith(key):
156 + show_page = boolval
157 + continue
158 + elif "nosummary".startswith(key) or "summary".startswith(key):
159 + show_summary = boolval
160 + continue
161 + elif "nocapital".startswith(key) or "capital".startswith(key):
162 + show_capital = boolval
163 + continue
164 + elif "notemplates".startswith(key) or "templates".startswith(key):
165 + exclude_template = not boolval
166 + continue
167 + elif "descending".startswith(key):
168 + reverse = boolval
169 + continue
170 + elif "ascending".startswith(key):
171 + reverse = not boolval
172 + continue
173 + elif "recursive".startswith(key):
174 + recursive = boolval
175 + continue
176 + elif "sort".startswith(key):
177 + sort.append(val)
178 + continue
179 + elif "column".startswith(key):
180 + column = int(val)
181 + continue
182 + elif "items".startswith(key):
183 + items = int(val)
184 + continue
185 + elif "skipitems".startswith(key):
186 + skipitems = int(val)
187 + continue
188 + elif "=" in i:
189 + return (_sysmsg % ('error', _('Invalid ShowCategory arguments "%s"!')) % (i, ))
190 +
191 + if i.lower() == "all":
192 + category.append("all")
193 + continue
194 + else:
195 + category_check = request.cfg.cache.page_category_regex.search(i)
196 + if category_check and category_check.group("all") == i:
197 + category.append(i)
198 + continue
199 + try:
200 + i = int(i)
201 + column = i
202 + except:
203 + return (_sysmsg % ('error', _('Invalid ShowCategory arguments "%s"!')) % (i, ))
204 +
205 + if not sort:
206 + sort = ["title"]
207 +
208 + if not category:
209 + category.append(macro.formatter.page.page_name)
210 +
211 + return show_category(request, category, column=column, show_cat=show_cat, show_page=show_page,
212 + show_summary=show_summary, show_capital=show_capital, sort=sort,
213 + reverse=reverse, recursive=recursive, items=items, skipitems=skipitems,
214 + exclude_template=exclude_template)
215 +
216 +# vim:ts=4:sw=4:et
217 diff -r 0bfe351fb737 MoinMoin/wikiutil2.py
218 --- a/MoinMoin/wikiutil2.py Tue Nov 25 09:40:25 2008 +0800
219 +++ b/MoinMoin/wikiutil2.py Tue Nov 25 10:17:40 2008 +0800
220 @@ -8,6 +8,9 @@
221
222 import re
223 from MoinMoin import wikiutil
224 +from MoinMoin import caching
225 +from MoinMoin.Page import Page
226 +from MoinMoin.logfile import editlog
227
228 from MoinMoin import log
229 logging = log.getLogger(__name__)
230 @@ -66,3 +69,292 @@
231
232 return raw_body
233
234 +class CategoryCache(object):
235 + cache_date, pagedict, catcatdict, catpagedict = 0, {}, {}, {}
236 +
237 + def __init__(self, request):
238 + self.request = request
239 + self._update_category_cache_data()
240 +
241 + def _update_category_cache_data(self):
242 + cache = caching.CacheEntry(self.request, 'category', 'dict', scope='wiki', use_pickle=True)
243 + if cache.exists():
244 + try:
245 + self.cache_date, self.pagedict, self.catcatdict, self.catpagedict = cache.content()
246 + except:
247 + cache.remove() # cache gone bad
248 +
249 + log = editlog.EditLog(self.request)
250 + try:
251 + new_date = log.date()
252 + except logfile.LogMissing:
253 + new_date = self.cache_date
254 +
255 + if not (self.pagedict or self.catcatdict or self.catpagedict):
256 + pagelist = self.request.rootpage.getPageList(user="",return_objects=True)
257 + else:
258 + pagenamelist = set()
259 + pagelist = []
260 +
261 + if new_date > self.cache_date:
262 + for event in log.reverse():
263 + if event.ed_time_usecs <= self.cache_date:
264 + break
265 + # action: [ATTDEL, ATTNEW, SAVE, SAVENEW, SAVE/RENAME, SAVE/REVERT]
266 + if event.action not in ['ATTNEW', 'ATTDEL']:
267 + pagenamelist.add(event.pagename)
268 + if event.action == 'SAVE/RENAME':
269 + pagenamelist.add(event.extra)
270 +
271 + for pagename in pagenamelist:
272 + page = Page(self.request, pagename)
273 + pagelist.append(page)
274 + self.cache_date = new_date
275 +
276 + # cache outof date since there are changed pages.
277 + if pagelist:
278 + pattern = re.compile(r'(?:^-----*\s*\r?\n)(?:^##.*\r?\n)*^(?!##)(?P<category>.+?)$', re.M)
279 + for page in pagelist:
280 + body = page.get_raw_body()
281 + pagename = page.page_name
282 +
283 + name_checked = self.request.cfg.cache.page_category_regexact.search(pagename)
284 + if name_checked and name_checked.group("all") == pagename:
285 + page_is_category = True
286 + else:
287 + page_is_category = False
288 + # reset self.pagedict, catdict for pagename
289 + if self.pagedict.has_key(pagename):
290 + for catname in self.pagedict[pagename]:
291 + if page_is_category:
292 + catdict = self.catcatdict
293 + else:
294 + catdict = self.catpagedict
295 + if catdict.has_key(catname):
296 + if pagename in catdict[catname]:
297 + catdict[catname].remove(pagename)
298 + if not catdict[catname]:
299 + del catdict[catname]
300 + del self.pagedict[pagename]
301 +
302 + # if deleted page is a category, delete category if blank.
303 + if not body and page_is_category:
304 + if self.catcatdict.has_key(pagename):
305 + if not self.catcatdict[pagename]:
306 + del self.catcatdict[pagename]
307 + if self.catpagedict.has_key(pagename):
308 + if not self.catpagedict[pagename]:
309 + del self.catpagedict[pagename]
310 +
311 + for match in pattern.finditer(body):
312 + for item in match.group('category').split():
313 +
314 + if item.startswith("[["):
315 + item = item[2:-2]
316 + name_checked = self.request.cfg.cache.page_category_regexact.search(item)
317 + if name_checked and name_checked.group("all") == item:
318 + self.pagedict[pagename] = self.pagedict.get(pagename, set())
319 + self.pagedict[pagename].add(item)
320 + if page_is_category:
321 + self.catcatdict[item] = self.catcatdict.get(item, set())
322 + self.catcatdict[item].add(pagename)
323 + else:
324 + self.catpagedict[item] = self.catpagedict.get(item, set())
325 + self.catpagedict[item].add(pagename)
326 +
327 + # Update cache
328 + cache.update((new_date, self.pagedict, self.catcatdict, self.catpagedict))
329 +
330 + def _get_all_categories(self):
331 + result = set(self.catcatdict.keys())
332 + for value in self.catcatdict.values():
333 + result |= value
334 + result |= set(self.catpagedict.keys())
335 + return result
336 +
337 + def get_sub_categories(self, category, recursive=False, exclude_template=True):
338 + """ Return all sub categories of this category.
339 +
340 + @param category: category name, or list of names of categories
341 + @param recursive: if True, scan sub categories recursively.
342 + Default is False.
343 + @param exclude_template: if True, exclude template page.
344 + Default is True.
345 + @rtype: set()
346 + @return: set of sub categories
347 + """
348 + result = self._get_sub_categories(category, recursive, set())
349 +
350 + # exclude template from result
351 + if exclude_template:
352 + templates = []
353 + for item in result:
354 + if self.request.cfg.cache.page_template_regexact.search(item) is not None:
355 + templates.append(item)
356 + for item in templates:
357 + result.remove(item)
358 +
359 + return result
360 +
361 + def _get_sub_categories(self, category, recursive=False, scanned=set()):
362 + """ Return all subcategories of the category.
363 +
364 + scanned store already scanned categories, to prevent deadly loop.
365 + """
366 + result = set()
367 + scanned.add(category)
368 + if isinstance(category, (list,dict,set)):
369 + if "all" in category:
370 + result = self._get_all_categories()
371 + else:
372 + for c in category:
373 + result = result | self._get_sub_categories(c, recursive, set())
374 +
375 + elif "all" == category:
376 + result = self._get_all_categories()
377 +
378 + else:
379 + children = self.catcatdict.get(category)
380 + if children:
381 + for child in children:
382 + result.add(child)
383 + if child not in scanned and recursive:
384 + result = result | self._get_sub_categories(child, recursive, scanned)
385 +
386 + return result
387 +
388 + def get_sub_pages(self, category, recursive=False, exclude_template=True):
389 + result = set()
390 +
391 + if isinstance(category, (list,dict,set)):
392 + for c in category:
393 + result = result | get_sub_pages(c, recursive=recursive)
394 + return result
395 +
396 + if isinstance(category, (list,dict,set)):
397 + category_list = set(category)
398 + else:
399 + category_list = set([category])
400 +
401 + if recursive:
402 + category_list = category_list | self.get_sub_categories(category, recursive=recursive)
403 +
404 + for c in category_list:
405 + result = result | self.catpagedict.get(c, set())
406 +
407 + # exclude template from result
408 + if exclude_template:
409 + templates = []
410 + for item in result:
411 + if self.request.cfg.cache.page_template_regex.search(item) is not None:
412 + templates.append(item)
413 + for item in templates:
414 + result.remove(item)
415 +
416 + return result
417 +
418 + def get_templates(self, category, recursive=False):
419 + result = set()
420 + subpages = self.get_sub_pages(category, recursive, exclude_template=False)
421 + for item in subpages:
422 + if self.request.cfg.cache.page_template_regexact.search(item) is not None:
423 + result.add(item)
424 + return result
425 +
426 + def get_page_category_trail(self, pagename, level=9):
427 + result = []
428 + self._get_page_category_trail( pagename, level, result)
429 + return result
430 +
431 + def _get_page_category_trail(self, pagename, level, result, scanned=[]):
432 + categories = self.pagedict.get(pagename, set())
433 + if categories:
434 + level -= 1
435 + for category in categories:
436 + if category in scanned:
437 + continue
438 + branch = scanned[:]
439 + branch.append(category)
440 + if not self.pagedict.get(category) or level == 0:
441 + result.append(branch)
442 + else:
443 + self._get_page_category_trail(category, level, result, branch)
444 +
445 +
446 +def formatList(request, itemlist, column=3, show_capital=True):
447 + """
448 + These functions: formatList, shorList, columnList is from MediaWiki.
449 + Modified by Jiang Xin <worldhello.net@gmail.com>
450 + """
451 + cutoff = max(column*2, column+1)
452 + if len(itemlist) > cutoff and column>1:
453 + return columnList(request, itemlist, column=column, show_capital=show_capital)
454 + elif len(itemlist) > 0:
455 + return shortList(request, itemlist, show_capital=show_capital)
456 + else:
457 + return [""]
458 +
459 +def shortList(request, itemlist, show_capital=True):
460 + _ = request.getText
461 + startchar = itemlist[0][0][0]
462 + result = []
463 +
464 + if show_capital:
465 + result += ["<h3>" + startchar + "</h3>"]
466 +
467 + result += ["<ul><li>" + itemlist[0][1] + "</li>"]
468 +
469 + for i in range(1,len(itemlist)):
470 + if show_capital and startchar != itemlist[i][0][0]:
471 + startchar = itemlist[i][0][0]
472 + result += ["</ul><h3>" + startchar + "</h3>\n<ul>"]
473 + result += ["<li>"+itemlist[i][1]+"</li>"]
474 + result += ["</ul>"]
475 + return result
476 +
477 +def columnList(request, itemlist, column=3, show_capital=True):
478 + _ = request.getText
479 + if not column:
480 + column = 3
481 + chunk = len(itemlist) // column
482 + result = ['<table class="borderless" width="100%"><tr valign="top">']
483 +
484 + startChunk = 0
485 + endChunk = chunk
486 + startchar = 'none'
487 +
488 + for chunkIndex in range(0, column):
489 + result.append("<td>")
490 + atColumnTop = True
491 +
492 + for i in range(startChunk, endChunk):
493 + if i >= len(itemlist):
494 + break
495 +
496 + if startchar != itemlist[i][0][0] or atColumnTop:
497 + if show_capital and not atColumnTop:
498 + result.append("</ul>")
499 + if not show_capital:
500 + if atColumnTop:
501 + result.append("<ul>")
502 + else:
503 + if itemlist[i][0][0] == startchar:
504 + result.append("<h3>" + startchar + " " + _("cont.") + "</h3>\n<ul>")
505 + else:
506 + startchar = itemlist[i][0][0]
507 + result.append("<h3>" + startchar + "</h3>\n<ul>")
508 + atColumnTop = False
509 +
510 + result.append("<li>" + itemlist[i][1] + "</li>")
511 +
512 + startChunk = endChunk
513 + endChunk += chunk + 1
514 + if not atColumnTop:
515 + result.append("</ul>")
516 +
517 + result.append("</td>")
518 +
519 + result.append("</tr></table>")
520 +
521 + return result
522 +
523
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.