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.
  • [get | view] (2008-11-26 08:19:48, 20.2 KB) [[attachment:60010_macro_showcategory.patch]]
  • [get | view] (2008-11-26 08:20:52, 8.8 KB) [[attachment:60015_newpage_dialog_in_showcategory_page.patch]]
  • [get | view] (2008-11-24 01:47:46, 37.5 KB) [[attachment:category_news.png]]
  • [get | view] (2008-11-24 01:41:53, 16.8 KB) [[attachment:new_subcategory.png]]
  • [get | view] (2008-11-24 01:42:13, 18.5 KB) [[attachment:new_subpage.png]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.