Attachment 'classic.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
3 MoinMoin classic theme
4
5 This class can also be used as base class for other themes -
6 if you make an empty child class, you will get classic behaviour.
7
8 If you want modified behaviour, just override the stuff you
9 want to change in the child class.
10
11 @copyright: 2003 by ThomasWaldmann (LinuxWiki:ThomasWaldmann)
12 @license: GNU GPL, see COPYING for details.
13 """
14
15 import urllib
16 from MoinMoin import i18n, wikiutil, version
17 from MoinMoin.Page import Page
18
19 class Theme:
20 """ here are the functions generating the html responsible for
21 the look and feel of your wiki site
22 """
23
24 name = "classic"
25
26 _=lambda x: x # dummy function for i18n
27 icons = {
28 # key alt icon filename w h
29 # ------------------------------------------------------------------
30 # navibar
31 'help': ( "%(page_help_contents)s", "moin-help.png", 12, 11),
32 'find': ( "%(page_find_page)s", "moin-search.png", 12, 12),
33 'diff': (_("Diffs"), "moin-diff.png", 15, 11),
34 'info': (_("Info"), "moin-info.png", 12, 11),
35 'edit': (_("Edit"), "moin-edit.png", 12, 12),
36 'unsubscribe':(_("Unsubscribe"), "moin-unsubscribe.png", 14, 10),
37 'subscribe': (_("Subscribe"), "moin-subscribe.png",14, 10),
38 'raw': (_("Raw"), "moin-raw.png", 12, 13),
39 'xml': (_("XML"), "moin-xml.png", 20, 13),
40 'print': (_("Print"), "moin-print.png", 16, 14),
41 'view': (_("View"), "moin-show.png", 12, 13),
42 'home': (_("Home"), "moin-home.png", 13, 12),
43 'up': (_("Up"), "moin-parent.png", 15, 13),
44 # FileAttach (is this used?)
45 'attach': ( "%(attach_count)s", "moin-attach.png", 7, 15),
46 # RecentChanges
47 'rss': (_("[RSS]"), "moin-rss.png", 36, 14),
48 'deleted': (_("[DELETED]"), "moin-deleted.png",60, 12),
49 'updated': (_("[UPDATED]"), "moin-updated.png",60, 12),
50 'new': (_("[NEW]"), "moin-new.png", 31, 12),
51 'diffrc': (_("[DIFF]"), "moin-diff.png", 15, 11),
52 # General
53 'bottom': (_("[BOTTOM]"), "moin-bottom.png", 14, 10),
54 'top': (_("[TOP]"), "moin-top.png", 14, 10),
55 'www': (_("[WWW]"), "moin-www.png", 11, 11),
56 'mailto': (_("[MAILTO]"), "moin-email.png", 14, 10),
57 'news': (_("[NEWS]"), "moin-news.png", 10, 11),
58 'telnet': (_("[TELNET]"), "moin-telnet.png", 10, 11),
59 'ftp': (_("[FTP]"), "moin-ftp.png", 11, 11),
60 'file': (_("[FILE]"), "moin-ftp.png", 11, 11),
61 # search forms
62 'searchbutton': (_("[?]"), "moin-search.png", 12, 12),
63 'interwiki': ( "[%(wikitag)s]", "moin-inter.png", 16, 16),
64 }
65
66 # ??? Why do we need stylesheet_print? the standard stylesheets
67 # provide a print version?
68
69 stylesheets_print = (
70 # theme charset media basename
71 (name, 'iso-8859-1', 'all', 'common'),
72 (name, 'iso-8859-1', 'all', 'print'),
73 )
74
75 stylesheets = (
76 # theme charset media basename
77 (name, 'iso-8859-1', 'all', 'common'),
78 (name, 'iso-8859-1', 'screen', 'screen'),
79 (name, 'iso-8859-1', 'print', 'print'),
80 )
81
82 def __init__(self, request):
83 """
84 Initialize the theme object.
85
86 @param request: the request object
87 """
88 self.request = request
89 self.cfg = request.cfg
90
91 def img_url(self, img):
92 """
93 generate an img url
94
95 @param img: the image filename
96 @rtype: string
97 @return: the image url
98 """
99 return "%s/%s/img/%s" % (self.cfg.url_prefix, self.name, img)
100
101 def css_url(self, basename, theme = None):
102 """
103 generate the css url
104
105 @param basename: the css media type (base filename w/o .css)
106 @param theme: theme name
107 @rtype: string
108 @return: the css url
109 """
110 if not theme:
111 theme = self.name
112 return "%s/%s/css/%s.css" % (self.cfg.url_prefix, theme, basename)
113
114 def emit_custom_html(self, html):
115 """
116 generate custom HTML code in `html`
117
118 @param html: a string or a callable object, in which case
119 it is called and its return value is used
120 @rtype: string
121 @return: string with html
122 """
123 if html:
124 if callable(html): html = html(self.request)
125 return html
126
127 # Header stuff #######################################################
128
129 def logo(self, d):
130 """
131 Assemble the logo
132
133 @param d: parameter dictionary
134 @rtype: string
135 @return: logo html
136 """
137 if d['logo_string']:
138 html = '<div id="logo">%s</div>' % wikiutil.link_tag(
139 self.request, wikiutil.quoteWikinameURL(d['page_front_page']), d['logo_string'])
140 else:
141 html = ''
142 return html
143
144 def title(self, d):
145 """
146 Assemble the title
147
148 @param d: parameter dictionary
149 @rtype: string
150 @return: title html
151 """
152 _ = self.request.getText
153 html = ['<div id="title">']
154 if d['title_link']:
155 html.append('<h1><a title="%s" href="%s">%s</a></h1>' % (
156 _('Click here to do a full-text search for this title'),
157 d['title_link'],
158 wikiutil.escape(d['title_text'])))
159 else:
160 html.append('<h1>%s</h1>' % wikiutil.escape(d['title_text']))
161 html.append('</div>')
162 return ''.join(html)
163
164 def username(self, d):
165 """
166 Assemble the username / userprefs link
167
168 @param d: parameter dictionary
169 @rtype: string
170 @return: username html
171 """
172 html = '<div id="username">%s</div>' % wikiutil.link_tag(
173 self.request, wikiutil.quoteWikinameURL(d['page_user_prefs']),
174 wikiutil.escape(d['user_prefs']))
175 return html
176
177 def navibar(self, d):
178 """
179 Assemble the navibar
180
181 @param d: parameter dictionary
182 @rtype: string
183 @return: navibar html
184 """
185 html = []
186 html.append('<ul id="navibar">\n')
187 if d['navibar']:
188 # Print site name in first field of navibar
189 # html.append(('<li>%(site_name)s</li>\n') % d)
190 for (link, navi_link) in d['navibar']:
191 html.append((
192 '<li><a href="%(link)s">%(navi_link)s</a></li>\n') % {
193 'link': link,
194 'navi_link': navi_link,
195 })
196 html.append('</ul>')
197 return ''.join(html)
198
199 def get_icon(self, icon):
200 try:
201 ret = self.icons[icon]
202 except KeyError: # if called from [[Icon(file)]] we have a filename, not a key
203 # using filenames is deprecated, but for now, we simulate old behaviour!
204 # please use only the icon *key* in future, not the filename any more.
205 icon = icon.replace('.gif','.png') # no gifs any more!
206 for i in self.icons.keys():
207 ret = self.icons[i]
208 if ret[1] == icon: # found the file name?
209 break
210 else:
211 ret = ("", icon, "", "")
212 return (ret[0], self.img_url(ret[1])) + ret[2:]
213
214 def make_icon(self, icon, vars=None):
215 """
216 This is the central routine for making <img> tags for icons!
217 All icons stuff except the top left logo, smileys and search
218 field icons are handled here.
219
220 @param icon: icon id (dict key)
221 @param vars: ...
222 @rtype: string
223 @return: icon html (img tag)
224 """
225 if vars is None:
226 vars = {}
227 alt, img, w, h = self.get_icon(icon)
228 try:
229 alt = alt % vars
230 except KeyError, err:
231 alt = 'KeyError: %s' % str(err)
232 if self.request:
233 alt = self.request.getText(alt, formatted=False)
234 try:
235 tag = self.request.formatter.image(src=img, alt=alt, width=w, height=h)
236 except AttributeError: # XXX FIXME if we have no formatter or no request
237 tag = '<img src="%s" alt="%s" width="%s" height="%s">' % (
238 img, alt, w, h)
239 import warnings
240 warnings.warn("calling themes without correct request", DeprecationWarning)
241 return tag
242
243 def make_iconlink(self, which, d):
244 """
245 Make a link with an icon
246
247 @param which: icon id (dictionary key)
248 @param d: parameter dictionary
249 @rtype: string
250 @return: html link tag
251 """
252 page_params, title, icon = self.cfg.page_icons_table[which]
253 d['title'] = title % d
254 d['i18ntitle'] = self.request.getText(d['title'], formatted=False)
255 img_src = self.make_icon(icon, d)
256 return wikiutil.link_tag(self.request, page_params % d, img_src, attrs='title="%(i18ntitle)s"' % d)
257
258 def iconbar(self, d):
259 """
260 Assemble the iconbar
261
262 @param d: parameter dictionary
263 @rtype: string
264 @return: iconbar html
265 """
266 iconbar = []
267 if self.cfg.page_iconbar and self.request.user.show_toolbar and d['page_name']:
268 iconbar.append('<ul id="iconbar">\n')
269 icons = self.cfg.page_iconbar[:]
270 for icon in icons:
271 if icon == "up":
272 if d['page_parent_page']:
273 iconbar.append('<li>%s</li>\n' % self.make_iconlink(icon, d))
274 elif icon == "subscribe":
275 iconbar.append('<li>%s</li>\n' % self.make_iconlink(
276 ["subscribe", "unsubscribe"][self.request.user.isSubscribedTo([d['page_name']])], d))
277 elif icon == "home":
278 if d['page_home_page']:
279 iconbar.append('<li>%s</li>\n' % self.make_iconlink(icon, d))
280 else:
281 iconbar.append('<li>%s</li>\n' % self.make_iconlink(icon, d))
282 iconbar.append('</ul>\n')
283 return ''.join(iconbar)
284
285 def msg(self, d):
286 """
287 Assemble the msg display
288
289 @param d: parameter dictionary
290 @rtype: string
291 @return: msg display html
292 """
293 html = ''
294 if d['msg']:
295 _ = self.request.getText
296 d.update({'link_text': _('Clear message'),})
297 clear_msg_link = u'<a href="%(script_name)s/%(q_page_name)s?action=show">%(link_text)s</a>' % d
298 d.update({'clear_msg_link': clear_msg_link,})
299 html = u'\n<div id="message">\n<p>%(msg)s</p><p>%(clear_msg_link)s</p></div>' % d
300 return html
301
302 def trail(self, d):
303 """
304 Assemble page trail
305
306 @param d: parameter dictionary
307 @rtype: string
308 @return: trail html
309 """
310 html = []
311 if d['trail']:
312 pagetrail = d['trail']
313 html.append('<ul id="pagetrail">\n')
314 for p in pagetrail[:-1]:
315 html.append('<li><span>%s</span></li>\n' % (Page(self.request, p).link_to(self.request),))
316 html.append('<li><span>%s</span></li>\n' % wikiutil.escape(pagetrail[-1]))
317 html.append('</ul>\n')
318 else:
319 html.append('<hr id="pagetrail">\n')
320 return ''.join(html)
321
322 def html_stylesheet_link(self, charset, media, href):
323 return ('<link rel="stylesheet" type="text/css" charset="%s" '
324 'media="%s" href="%s">\n') % (charset, media, href)
325
326 def html_stylesheets(self, d):
327 """
328 Assemble stylesheet links
329
330 @param d: parameter dictionary
331 @rtype: string
332 @return: links
333 """
334 html = []
335 if d.get('print_mode', False):
336 stylesheets = self.stylesheets_print
337 else:
338 stylesheets = self.stylesheets
339 user_css_url = self.request.user.valid and self.request.user.css_url
340
341 # Create stylesheets links
342 for theme, charset, media, name in stylesheets:
343 href = self.css_url(name, theme)
344 html.append(self.html_stylesheet_link(charset, media, href))
345
346 # workaround for old user settings
347 # Dont add user css url if it matches one of ours
348 if user_css_url and user_css_url == href:
349 user_css_url = None
350
351 # Add user css url (assuming that user css uses iso-8859-1)
352 # ??? Maybe move to utf-8?
353 # TODO: Document this in the Help system
354 if user_css_url and user_css_url.lower() != "none":
355 html.append(
356 self.html_stylesheet_link('iso-8859-1', 'all', user_css_url))
357
358 return ''.join(html)
359
360 def html_head(self, d):
361 """
362 Assemble html head
363
364 @param d: parameter dictionary
365 @rtype: string
366 @return: html head
367 """
368 dict = {
369 'stylesheets_html': self.html_stylesheets(d),
370 }
371 dict.update(d)
372
373 html = """
374 <title>%(title)s - %(sitename)s</title>
375 %(stylesheets_html)s
376 """ % dict
377
378 return html
379
380 def header(self, d):
381 """
382 Assemble page header
383
384 @param d: parameter dictionary
385 @rtype: string
386 @return: page header html
387 """
388 dict = {
389 'config_header1_html': self.emit_custom_html(self.cfg.page_header1),
390 'config_header2_html': self.emit_custom_html(self.cfg.page_header2),
391 'logo_html': self.logo(d),
392 'title_html': self.title(d),
393 'username_html': self.username(d),
394 'navibar_html': self.navibar(d),
395 'iconbar_html': self.iconbar(d),
396 'msg_html': self.msg(d),
397 'trail_html': self.trail(d),
398 }
399 dict.update(d)
400
401 html = """
402 %(config_header1_html)s
403 %(logo_html)s
404 %(username_html)s
405 %(title_html)s
406 %(iconbar_html)s
407 %(navibar_html)s
408 %(trail_html)s
409 %(config_header2_html)s
410 %(msg_html)s
411 """ % dict
412
413 # Next parts will use config.default_lang direction, as set in the <body>
414 return html
415
416 # Footer stuff #######################################################
417
418 def showtext_link(self, d, **keywords):
419 """
420 Assemble ShowText link (on action pages)
421
422 @param d: parameter dictionary
423 @rtype: string
424 @return: edittext link html
425 """
426 _ = self.request.getText
427 html = ''
428 if keywords.get('showpage', 0):
429 html = "<p>%s %s</p>\n" % (
430 wikiutil.link_tag(self.request, d['q_page_name'], _("ShowText", formatted=False)),
431 _('of this page'),
432 )
433 return html
434
435 def edittext_link(self, d, **keywords):
436 """
437 Assemble EditText link (or indication that page cannot be edited)
438
439 @param d: parameter dictionary
440 @rtype: string
441 @return: edittext link html
442 """
443 _ = self.request.getText
444 html = []
445 html.append('<p>')
446 if keywords.get('editable', 1):
447 editable = self.request.user.may.write(d['page_name']) and d['page'].isWritable()
448 if editable:
449 html.append("%s %s" % (
450 wikiutil.link_tag(self.request, d['q_page_name']+'?action=edit', _('EditText', formatted=False)),
451 _('of this page'),
452 ))
453 else:
454 html.append("%s" % _('Immutable page'))
455 html.append(' %(last_edit_info)s' % d)
456 html.append('</p>')
457 return ''.join(html)
458
459 def footer_fragments(self, d, **keywords):
460 """
461 assemble HTML code fragments added by the page formatters
462
463 @param d: parameter dictionary
464 @rtype: string
465 @return: footer fragments html
466 """
467 html = ''
468 if d['footer_fragments']:
469 html = ''.join(d['footer_fragments'].values())
470 return html
471
472 def searchform(self, d):
473 """
474 assemble HTML code for the search forms
475
476 @param d: parameter dictionary
477 @rtype: string
478 @return: search form html
479 """
480 _ = self.request.getText
481 sitenav_pagename = wikiutil.getSysPage(self.request, 'SiteNavigation').page_name
482 dict = {
483 'find_page_html': wikiutil.link_tag(self.request, d['page_find_page']+'?value='+d['q_page_name'], _('FindPage', formatted=False)),
484 'navi_page_html': wikiutil.link_tag(self.request, sitenav_pagename, sitenav_pagename),
485 'search_html': _("or search titles %(titlesearch)s, full text %(textsearch)s or") % d,
486 }
487 dict.update(d)
488
489 html = """
490 <form method="POST" action="%(script_name)s/%(q_page_name)s">
491 <p>
492 <input type="hidden" name="action" value="inlinesearch">
493 <input type="hidden" name="context" value="40">
494 %(find_page_html)s %(search_html)s %(navi_page_html)s
495 </p>
496 </form>
497 """ % dict
498
499 return html
500
501 def availableactions(self, d):
502 """
503 assemble HTML code for the available actions
504
505 @param d: parameter dictionary
506 @rtype: string
507 @return: available actions html
508 """
509 _ = self.request.getText
510 html = []
511 html.append('<p>')
512 first = 1
513 for action in d['available_actions']:
514 html.append("%s %s" % (
515 (',', _('Or try one of these actions:'))[first],
516 wikiutil.link_tag(self.request, '%s?action=%s' % (d['q_page_name'], action), _(action, formatted=False)),
517 ))
518 first = 0
519 html.append('</p>')
520 return ''.join(html)
521
522 def showversion(self, d, **keywords):
523 """
524 assemble HTML code for copyright and version display
525
526 @param d: parameter dictionary
527 @rtype: string
528 @return: copyright and version display html
529 """
530 html = ''
531 if self.cfg.show_version and not keywords.get('print_mode', 0):
532 html = ('<p>'
533 'MoinMoin %s, Copyright \xa9 2000-2004 by Jürgen Hermann'
534 '</p>' % (version.revision,))
535 return html
536
537 def footer(self, d, **keywords):
538 """
539 Assemble page footer
540
541 @param d: parameter dictionary
542 @keyword ...:...
543 @rtype: string
544 @return: page footer html
545 """
546 dict = {
547 'config_page_footer1_html': self.emit_custom_html(self.cfg.page_footer1),
548 'config_page_footer2_html': self.emit_custom_html(self.cfg.page_footer2),
549 'showtext_html': self.showtext_link(d, **keywords),
550 'edittext_html': self.edittext_link(d, **keywords),
551 'search_form_html': self.searchform(d),
552 'available_actions_html': self.availableactions(d),
553 'credits_html': self.emit_custom_html(self.cfg.page_credits),
554 'version_html': self.showversion(d, **keywords),
555 'footer_fragments_html': self.footer_fragments(d, **keywords),
556 }
557 dict.update(d)
558
559 html = """
560 <div id="footer">
561 <div id="credits">
562 %(credits_html)s
563 </div>
564 %(config_page_footer1_html)s
565 %(showtext_html)s
566 %(footer_fragments_html)s
567 %(edittext_html)s
568 %(search_form_html)s
569 %(available_actions_html)s
570 %(config_page_footer2_html)s
571 </div>
572 %(version_html)s
573 """ % dict
574
575 return html
576
577 # RecentChanges ######################################################
578
579 def recentchanges_entry(self, d):
580 """
581 Assemble a single recentchanges entry (table row)
582
583 @param d: parameter dictionary
584 @rtype: string
585 @return: recentchanges entry html
586 """
587 _ = self.request.getText
588 html = []
589 html.append('<tr>\n')
590
591 html.append('<td class="rcicon1">%(icon_html)s</td>\n' % d)
592
593 html.append('<td class="rcpagelink">%(pagelink_html)s</td>\n' % d)
594
595 html.append('<td class="rctime">')
596 if d['time_html']:
597 html.append("%(time_html)s" % d)
598 html.append('</td>\n')
599
600 html.append('<td class="rcicon2">%(info_html)s</td>\n' % d)
601
602 html.append('<td class="rceditor">')
603 if d['editors']:
604 html.append('<br>'.join(d['editors']))
605 html.append('</td>\n')
606
607 html.append('<td class="rccomment">')
608 if d['comments']:
609 if d['changecount'] > 1:
610 notfirst = 0
611 for comment in d['comments']:
612 html.append('%s<tt>#%02d</tt> %s' % (
613 notfirst and '<br>' or '' , comment[0], comment[1]))
614 notfirst = 1
615 else:
616 comment = d['comments'][0]
617 html.append('%s' % comment[1])
618 html.append('</td>\n')
619
620 html.append('</tr>\n')
621
622 return ''.join(html)
623
624 def recentchanges_daybreak(self, d):
625 """
626 Assemble a rc daybreak indication (table row)
627
628 @param d: parameter dictionary
629 @rtype: string
630 @return: recentchanges daybreak html
631 """
632 if d['bookmark_link_html']:
633 set_bm = ' [%(bookmark_link_html)s]' % d
634 else:
635 set_bm = ''
636 return ('<tr class="rcdaybreak"><td colspan="%d">'
637 '<strong>%s</strong>'
638 '%s'
639 '</td></tr>\n') % (6, d['date'], set_bm)
640
641 def recentchanges_header(self, d):
642 """
643 Assemble the recentchanges header (intro + open table)
644
645 @param d: parameter dictionary
646 @rtype: string
647 @return: recentchanges header html
648 """
649 _ = self.request.getText
650 html = '<div class="recentchanges" %s>\n' % self.ui_lang_attr()
651 html += '<div>\n'
652 if d['rc_rss_link']:
653 html += '<p class="rcrss">%s</p>\n' % d['rc_rss_link']
654 html += '<p>'
655 if d['rc_update_bookmark']:
656 html += "%(rc_update_bookmark)s %(rc_curr_bookmark)s<br>" % d
657 if d['rc_days']:
658 days = []
659 for day in d['rc_days']:
660 if day == d['rc_max_days']:
661 days.append('<strong>%d</strong>' % day)
662 else:
663 days.append(
664 wikiutil.link_tag(self.request,
665 '%s?max_days=%d' % (d['q_page_name'], day),
666 str(day)))
667 days = ' | '.join(days)
668 html += (_("Show all changes in the last %s days.") % (days,))
669 html += '</p>\n</div>\n'
670
671 html += '<table>\n'
672 return html
673
674 def recentchanges_footer(self, d):
675 """
676 Assemble the recentchanges footer (close table)
677
678 @param d: parameter dictionary
679 @rtype: string
680 @return: recentchanges footer html
681 """
682 _ = self.request.getText
683 html = ''
684 html += '</table>\n'
685 if d['rc_msg']:
686 html += "<br>%(rc_msg)s\n" % d
687 html += '</div>\n'
688 return html
689
690 #
691 # Language stuff
692 #
693
694 def ui_lang_attr(self):
695 """Generate language attributes for user interface elements
696
697 User interface elements use the user language (if any), kept in
698 request.lang.
699
700 @rtype: string
701 @return: lang and dir html attributes
702 """
703 lang = self.request.lang
704 dir = i18n.getDirection(lang)
705 return 'lang="%(lang)s" dir="%(dir)s"' % locals()
706
707 def content_lang_attr(self):
708 """Generate language attributes for wiki page content
709
710 Page content uses the wiki default language
711
712 @rtype: string
713 @return: lang and dir html attributes
714 """
715 lang = self.cfg.default_lang
716 dir = i18n.getDirection(lang)
717 return 'lang="%(lang)s" dir="%(dir)s"' % locals()
718
719
720 def execute(request):
721 """
722 Generate and return a theme object
723
724 @param request: the request object
725 @rtype: MoinTheme
726 @return: Theme object
727 """
728 return Theme(request)
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.