Anhang: inyoka-wiki-macros.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | """ inyoka.wiki.macros ~~~~~~~~~~~~~~~~~~ Macros for the wiki. :copyright: (c) 2012-2024 by the Inyoka Team, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ import itertools import operator from django.conf import settings from django.utils.translation import gettext as _ from inyoka.markup import macros, nodes from inyoka.markup.parsertools import MultiMap, flatten_iterator from inyoka.markup.templates import expand_page_template from inyoka.markup.utils import simple_filter from inyoka.utils.imaging import parse_dimensions from inyoka.utils.text import get_pagetitle, join_pagename, normalize_pagename from inyoka.utils.urls import href, is_safe_domain, urlencode from inyoka.wiki.models import MetaData, Page, is_privileged_wiki_page from inyoka.wiki.signals import build_picture_node from inyoka.wiki.views import fetch_real_target class PageCount(macros.Macro): """ Return the number of existing pages. """ names = ('PageCount', 'Seitenzahl') allowed_context = ['wiki'] def build_node(self, context, format): return nodes.Text(str(Page.objects.get_page_count())) class PageList(macros.Macro): """ Return a list of pages. """ names = ('PageList', 'Seitenliste') is_block_tag = True arguments = ( ('pattern', str, ''), ('case_sensitive', bool, True), ('shorten_title', bool, False) ) allowed_context = ['wiki'] def __init__(self, pattern, case_sensitive, shorten_title): self.pattern = normalize_pagename(pattern) self.case_sensitive = case_sensitive self.shorten_title = shorten_title def build_node(self, context, format): result = nodes.List('unordered') pagelist = Page.objects.get_page_list(exclude_privileged=True) if self.pattern: pagelist = simple_filter(self.pattern, pagelist, self.case_sensitive) for page in pagelist: title = [nodes.Text(get_pagetitle(page, not self.shorten_title))] link = nodes.InternalLink(page, title, force_existing=True) result.children.append(nodes.ListItem([link])) result.children.append(nodes.Text('\n')) return result class AttachmentList(macros.Macro): """ Return a list of attachments or attachments below a given page. """ names = ('AttachmentList', 'Anhänge') is_block_tag = True arguments = ( ('page', str, ''), ('shorten_title', bool, False) ) allowed_context = ['wiki'] def __init__(self, page, shorten_title): self.page = normalize_pagename(page) self.shorten_title = shorten_title def build_node(self, context, format): result = nodes.List('unordered') pagelist = Page.objects.get_attachment_list(self.page or None) for page in pagelist: title = [nodes.Text(get_pagetitle(page, not self.shorten_title))] link = nodes.InternalLink(page, title, force_existing=True) result.children.append(nodes.ListItem([link])) return result class OrphanedPages(macros.Macro): """ Return a list of orphaned pages. """ names = ('OrphanedPages', 'VerwaisteSeiten') is_block_tag = True allowed_context = ['wiki'] def build_node(self, context, format): result = nodes.List('unordered') for page in Page.objects.get_orphans(): title = [nodes.Text(get_pagetitle(page, True))] link = nodes.InternalLink(page, title, force_existing=True) result.children.append(nodes.ListItem([link])) return result class RedirectPages(macros.Macro): """ Return a list of pages that redirect to somewhere. """ names = ('RedirectPages', 'Weiterleitungen') is_block_tag = True allowed_context = ['wiki'] def build_node(self, context, format): result = nodes.List('unordered') # TODO i18n: bloody hell, this is crazy... requires some more thinking # and a migration as well as coordination with the wiki team... for page in Page.objects.find_by_metadata('weiterleitung'): target = page.metadata.get('weiterleitung') link = nodes.InternalLink(page.name, [nodes.Text(page.title)], force_existing=True) title = [nodes.Text(get_pagetitle(target, True))] target = nodes.InternalLink(target, title) result.children.append(nodes.ListItem([link, nodes.Text(' \u2794 '), target])) return result class SimilarPages(macros.Macro): """ Show a list of pages similar to the page name given or the page from the render context. """ names = ('SimilarPages', 'ÄhnlicheSeiten') is_block_tag = True arguments = ( ('page', str, ''), ) allowed_context = ['wiki'] def __init__(self, page_name): self.page_name = page_name def build_node(self, context, format): wiki_page = context.kwargs.get('wiki_page', None) if wiki_page: name = wiki_page.name ignore = name else: name = self.page_name ignore = None if not name: msg = _('You must apply a page name because the macro is being ' 'called outside the wiki context.') return nodes.error_box(_('Invalid arguments'), msg) result = nodes.List('unordered') for page in Page.objects.get_similar(name): if page == ignore: continue title = [nodes.Text(get_pagetitle(page, True))] link = nodes.InternalLink(page, title, force_existing=True) result.children.append(nodes.ListItem([link])) return result class TagList(macros.Macro): """ Show a taglist. """ names = ('TagList', 'TagListe') is_block_tag = True arguments = ( ('tag', str, ''), ) allowed_context = ['wiki'] def __init__(self, active_tag): self.active_tag = active_tag def build_node(self, context, format): active_tag = self.active_tag if not active_tag and context.request: active_tag = context.request.GET.get('tag') result = nodes.List('unordered', class_='taglist') if active_tag: pages = Page.objects.find_by_tag(active_tag) for page in sorted(pages, key=str.lower): item = nodes.ListItem([nodes.InternalLink(page)]) result.children.append(item) else: for tag in Page.objects.get_taglist(100): link = nodes.Link('?' + urlencode({ 'tag': tag['name'] }), [nodes.Text(tag['name'])], style='font-size: %s%%' % tag['size'] ) result.children.append(nodes.ListItem([link])) head = nodes.Headline(2, children=[ nodes.Text(_('Pages with tag “%(name)s”') % { 'name': self.active_tag }) ], class_='head') container = nodes.Layer(children=[head, result]) return container class FilterByMetaData(macros.Macro): """ Filter pages by their metadata """ names = ('FilterByMetaData', 'MetaFilter') is_block_tag = True arguments = ( ('filters', str, ''), ) allowed_context = ['wiki'] def __init__(self, filters): self.filters = [x.strip() for x in filters.split(';')] def build_node(self, context, format): mapping = [] for part in self.filters: # TODO: Can we do something else instead of skipping? if ':' not in part: continue key = part.split(':')[0].strip() values = [x.strip() for x in part.split(':')[1].split(',')] mapping.extend([(key, x) for x in values]) mapping = MultiMap(mapping) pages = set() for key in list(mapping.keys()): values = list(flatten_iterator(mapping[key])) includes = [x for x in values if not x.startswith('NOT ')] kwargs = {'key': key, 'value__in': includes} q = MetaData.objects.select_related('page').filter(**kwargs) res = { x.page for x in q if not is_privileged_wiki_page(x.page.name) } pages = pages.union(res) # filter the pages with `AND` res = set() for key in list(mapping.keys()): for page in pages: e = [x[4:] for x in mapping[key] if x.startswith('NOT ')] i = [x for x in mapping[key] if not x.startswith('NOT ')] exclude = False for val in set(page.metadata[key]): if val in e: exclude = True if not exclude and set(page.metadata[key]) == set(i): res.add(page) names = [p.name for p in res] names = sorted(names, key=lambda s: s.lower()) if not names: return nodes.error_box(_('No result'), _('The metadata filter has found no results. Query: %(query)s') % { 'query': '; '.join(self.filters)}) # build the node result = nodes.List('unordered') for page in names: title = [nodes.Text(get_pagetitle(page))] link = nodes.InternalLink(page, title, force_existing=True) result.children.append(nodes.ListItem([link])) return result class PageName(macros.Macro): """ Return the name of the current page if the render context knows about that. This is only useful when rendered from a wiki page. """ names = ('PageName', 'Seitenname') allowed_context = ['wiki'] def build_node(self, context, format): wiki_page = context.kwargs.get('wiki_page', None) if wiki_page: return nodes.Text(wiki_page.title) return nodes.Text(_('Unknown page')) class Template(macros.Macro): """ Include a page as template and expand it. """ names = ('Template', 'Vorlage') has_argument_parser = True is_static = True allowed_context = ['forum', 'ikhaya', 'wiki'] def __init__(self, args, kwargs): if not args: self.template = None self.context = [] return items = list(kwargs.items()) for idx, arg in enumerate(args[1:]): items.append(('arguments.%d' % idx, arg)) # TODO: kill WIKI_ prefix here self.template = join_pagename(settings.WIKI_TEMPLATE_BASE, normalize_pagename(args[0], False)) if is_privileged_wiki_page(self.template): self.template = None self.context = items def build_node(self): return expand_page_template(self.template, self.context, True) class Attachment(macros.Macro): """ This macro displays a download link for an attachment. """ names = ('Attachment', 'Anhang') arguments = ( ('attachment', str, ''), ('text', str, ''), ) allowed_context = ['wiki'] def __init__(self, target, text): self.target = target self.text = text self.is_external = not is_safe_domain(target) if not self.is_external: self.metadata = [nodes.MetaData('X-Attach', [target])] target = normalize_pagename(target, True) self.children = [nodes.Text(self.text or self.target)] def build_node(self, context, format): target = self.target if self.is_external: return nodes.Link(target, self.children) else: wiki_page = context.kwargs.get('wiki_page', None) if wiki_page: target = join_pagename(wiki_page.name, self.target) source = href('wiki', '_attachment', target=target) return nodes.Link(source, self.children) class Picture(macros.Macro): """ This macro can display external images and attachments as images. It also takes care about thumbnail generation. For any internal (attachment) image included that way an ``X-Attach`` metadata is emitted. Like for any link only absolute targets are allowed. This might be surprising behavior if you're used to the MoinMoin syntax but caused by the fact that the parser does not know at parse time on which page it is operating. """ names = ('Picture', 'Bild') arguments = ( ('picture', str, ''), ('size', str, ''), ('align', str, ''), ('alt', str, None), ('title', str, None) ) allowed_context = ['ikhaya', 'wiki'] def __init__(self, target, dimensions, alignment, alt, title): self.metadata = [nodes.MetaData('X-Attach', [target])] self.width, self.height = parse_dimensions(dimensions) self.target = target self.alt = alt or target self.title = title self.align = alignment if self.align not in ('left', 'right', 'center'): self.align = None def build_node(self, context, format): ret_ = build_picture_node.send(sender=self, context=context, format=format) ret = [_f for _f in itertools.chain( list(map(operator.itemgetter(1), ret_)) ) if _f] if ret: assert len(ret) == 1, "There must not be more than one node tree per context" return ret[0] # TODO: refactor using signals on rendering # to get proper application independence if context.application == 'wiki': target = normalize_pagename(self.target, True) else: target = self.target wiki_page = context.kwargs.get('wiki_page', None) if wiki_page: target = join_pagename(wiki_page.name, target) source = fetch_real_target(target, width=self.width, height=self.height) img = nodes.Image(source, self.alt, class_='image-' + (self.align or 'default'), title=self.title) if (self.width or self.height) and wiki_page is not None: return nodes.Link(fetch_real_target(target), [img]) return img macros.register(PageCount) macros.register(PageList) macros.register(AttachmentList) macros.register(OrphanedPages) macros.register(RedirectPages) macros.register(SimilarPages) macros.register(TagList) macros.register(FilterByMetaData) macros.register(PageName) macros.register(Template) macros.register(Attachment) macros.register(Picture) |
Teil von Inyoka: Quelltext der in diesem Artikel beschriebenen Makros
Übertragen von Github am 20.11.2024, hier abgelegt als Referenz zum hiesigen Artikel.