Dateianhang 'Page.py'

Herunterladen

   1 """
   2     MoinMoin - Page class
   3 
   4     Copyright (c) 2000, 2001, 2002 by Jürgen Hermann <jh@web.de>
   5     All rights reserved, see COPYING for details.
   6 
   7     $Id: Page.py,v 1.139 2003/01/24 21:31:14 jhermann Exp $
   8 """
   9 
  10 # Imports
  11 import cStringIO, os, re, sys, string, urllib
  12 from MoinMoin import caching, config, user, util, wikiutil, webapi
  13 from MoinMoin.i18n import _
  14 
  15 
  16 #############################################################################
  17 ### Page - Manage a page associated with a WikiName
  18 #############################################################################
  19 class Page:
  20     """ An immutable wiki page.
  21 
  22         To change a page's content, use the PageEditor class.
  23     """
  24 
  25     _SPLIT_RE = re.compile('([%s])([%s])' % (config.lowerletters, config.upperletters))
  26 
  27 
  28     def __init__(self, page_name, **keywords):
  29         """ Create page object.
  30 
  31             Note that this is a 'lean' operation, since the text for the page
  32             is loaded on demand. Thus, things like `Page(name).link_to()` are
  33             efficient.
  34 
  35             **page_name** -- WikiName of the page
  36             **keywords** --
  37                 date: date of older revision
  38                 formatter: formatter instance
  39         """
  40         self.page_name = page_name
  41         self.prev_date = keywords.get('date')
  42         self._raw_body = None
  43 
  44         if keywords.has_key('formatter'):
  45             self.formatter = keywords.get('formatter')
  46             self.default_formatter = 0
  47         else:
  48             self.default_formatter = 1
  49 
  50 
  51     def split_title(self, force=0):
  52         """ Return a string with the page name split by spaces, if
  53             the user wants that.
  54         """
  55         if not force and not user.current.wikiname_add_spaces: return self.page_name
  56     
  57         # look for the end of words and the start of a new word,
  58         # and insert a space there
  59         return self._SPLIT_RE.sub(r'\1 \2', self.page_name)
  60 
  61 
  62     def _text_filename(self):
  63         """The name of the page file, possibly of an older page"""
  64         if self.prev_date:
  65             return os.path.join(config.backup_dir, wikiutil.quoteFilename(self.page_name) + "." + self.prev_date)
  66         else:
  67             return os.path.join(config.text_dir, wikiutil.quoteFilename(self.page_name))
  68 
  69 
  70     def _tmp_filename(self):
  71         """The name of the temporary file used while saving"""
  72         return os.path.join(config.text_dir, ('#' + wikiutil.quoteFilename(self.page_name) + '.' + `os.getpid()` + '#'))
  73 
  74 
  75     def _last_modified(self, request):
  76         if not self.exists():
  77             return None
  78 
  79         result = None
  80 
  81         lastedited = wikiutil.getPagePath(self.page_name, 'last-edited', check_create=0)
  82         if os.path.exists(lastedited):
  83             from MoinMoin import editlog
  84 
  85             log = editlog.EditLog(request, filename=lastedited)
  86             if log.next():
  87                 result = _("(last edited %(time)s by %(editor)s)") % {
  88                     'time': user.current.getFormattedDateTime(log.ed_time),
  89                     'editor': log.getEditor(),
  90                 }
  91             del log
  92 
  93         return result or _("(last modified %s)") % user.current.getFormattedDateTime(
  94                 os.path.getmtime(self._text_filename()))
  95 
  96 
  97     def isWritable(self):
  98         """True if page can be changed"""
  99         return os.access(self._text_filename(), os.W_OK) or not self.exists()
 100 
 101 
 102     def exists(self):
 103         """True if the page exists"""
 104         return os.path.exists(self._text_filename())
 105 
 106 
 107     def size(self):
 108         """Return page size, 0 for non-existent pages"""
 109         if self._raw_body is not None:
 110             return len(self._raw_body)
 111 
 112         try:
 113             return os.path.getsize(self._text_filename())
 114         except EnvironmentError, e:
 115             import errno
 116             if e.errno == errno.ENOENT: return 0
 117             raise
 118 
 119 
 120     def get_raw_body(self):
 121         """Load the raw markup from the page file"""
 122         if self._raw_body is None:
 123             # try to open file
 124             try:
 125                 file = open(self._text_filename(), 'rb')
 126             except IOError, er:
 127                 import errno
 128                 if er.errno == errno.ENOENT:
 129                     # just doesn't exist, return empty text (note that we
 130                     # never store empty pages, so this is detectable and also
 131                     # safe when passed to a function expecting a string)
 132                     return ""
 133                 else:
 134                     raise er
 135 
 136             # read file content and make sure it is closed properly
 137             try:
 138                 self.set_raw_body(file.read())
 139             finally:
 140                 file.close()
 141 
 142         return self._raw_body
 143 
 144 
 145     def set_raw_body(self, body):
 146         """Set the raw body text (prevents loading from disk)"""
 147         self._raw_body = body
 148 
 149 
 150     def url(self, querystr=None):
 151         """ Return an URL for this page.
 152         """
 153         url = "%s/%s" % (webapi.getScriptname(), wikiutil.quoteWikiname(self.page_name))
 154         if querystr: url = "%s?%s" % (url, string.replace(querystr, '&', '&amp;'))
 155         return url
 156 
 157 
 158     def link_to(self, text=None, querystr=None, anchor=None, **kw):
 159         """ Return HTML markup that links to this page.
 160 
 161             See wikiutil.link_tag() for possible keyword parameters.
 162         """
 163         text = text or self.split_title()
 164         fmt = getattr(self, 'formatter', None)
 165         url = wikiutil.quoteWikiname(self.page_name)
 166         if querystr: url = "%s?%s" % (url, string.replace(querystr, '&', '&amp;'))
 167         if anchor: url = "%s#%s" % (url, urllib.quote_plus(anchor))
 168         if self.exists():
 169             return wikiutil.link_tag(url, text, formatter=fmt, **kw)
 170         elif user.current.show_nonexist_qm:
 171             return wikiutil.link_tag(url,
 172                 '?', 'nonexistent', formatter=fmt, **kw) + text
 173         else:
 174             return wikiutil.link_tag(url, text, 'nonexistent', formatter=fmt, **kw)
 175 
 176 
 177     def getSubscribers(self, request, **kw):
 178         """ Get all subscribers of this page.
 179             Return dict with email lists per language.
 180 
 181             include_self == 1: include current user (default: 0)
 182             return_users == 1: return user instances (default: 0)
 183         """
 184         include_self = kw.get('include_self', 0)
 185         return_users = kw.get('return_users', 0)
 186 
 187         # extract categories of this page
 188         pageList = self.getCategories(request)
 189         
 190         # add current page name for list matching
 191         pageList.append(self.page_name)
 192 
 193         # get email addresses of the all wiki user which have a profile stored;
 194         # add the address only if the user has subscribed to the page and
 195         # the user is not the current editor
 196         userlist = user.getUserList()
 197         emails = {}
 198         for uid in userlist:
 199             if uid == request.user.id and not include_self: continue # no self notification
 200             subscriber = user.User(request, uid)
 201             if not subscriber.email: continue # skip empty email address
 202 
 203             if subscriber.isSubscribedTo(pageList):                
 204                 lang = subscriber.language or 'en'
 205                 if not emails.has_key(lang): emails[lang] = []
 206                 if return_users:
 207                     emails[lang].append(subscriber)
 208                 else:
 209                     emails[lang].append(subscriber.email) 
 210 
 211         return emails
 212 
 213 
 214     def send_page(self, request, msg=None, **keywords):
 215         """ Send the formatted page to stdout.
 216 
 217             **form** -- CGI-Form
 218             **msg** -- if given, display message in header area
 219             **keywords** --
 220                 content_only: 1 to omit page header and footer
 221                 count_hit: add an event to the log
 222         """
 223         request.clock.start('send_page')
 224         request.clock.start('send_page_import_log')
 225         import cgi
 226         from MoinMoin.util import pysupport
 227 
 228         # determine modes
 229         print_mode = request.form.has_key('action') and request.form['action'].value == 'print'
 230         content_only = keywords.get('content_only', 0)
 231         self.hilite_re = keywords.get('hilite_re', None)
 232         if msg is None: msg = ""
 233 
 234         # count hit?
 235         if keywords.get('count_hit', 0):
 236             request.getEventLogger().add('VIEWPAGE', {'pagename': self.page_name})
 237 
 238         request.clock.stop('send_page_import_log')
 239 
 240         # load the text
 241         request.clock.start('send_page_get_raw')
 242         body = self.get_raw_body()
 243         request.clock.stop('send_page_get_raw')
 244 
 245         request.clock.start('send_page_formatter_pi')
 246         # if necessary, load the default formatter
 247         if self.default_formatter:
 248             from MoinMoin.formatter.text_html import Formatter
 249             self.formatter = Formatter(request, store_pagelinks=1)
 250         self.formatter.setPage(self)
 251 
 252         # default is wiki markup
 253         pi_format = config.default_markup or "wiki"
 254         pi_redirect = None
 255         pi_formtext = []
 256         pi_formfields = []
 257         wikiform = None
 258 
 259         # check for XML content
 260         if body and body[:5] == '<?xml':
 261             pi_format = "xslt"
 262 
 263         # check processing instructions
 264         while body and body[0] == '#':
 265             # extract first line
 266             try:
 267                 line, body = string.split(body, '\n', 1)
 268             except ValueError:
 269                 line = body
 270                 body = ''
 271 
 272             # skip comments (lines with two hash marks)
 273             if line[1] == '#': continue
 274 
 275             # parse the PI
 276             verb, args = string.split(line[1:]+' ', ' ', 1)
 277             verb = string.lower(verb)
 278             args = string.strip(args)
 279 
 280             # check the PIs
 281             if verb == "format":
 282                 # markup format
 283                 pi_format = string.lower(args)
 284             elif verb == "redirect":
 285                 # redirect to another page
 286                 # note that by including "action=show", we prevent
 287                 # endless looping (see code in "cgimain") or any
 288                 # cascaded redirection
 289                 pi_redirect = args
 290                 if request.form.has_key('action') or request.form.has_key('redirect') or content_only: continue
 291 
 292                 webapi.http_redirect(request, '%s/%s?action=show&redirect=%s' % (
 293                     webapi.getScriptname(),
 294                     wikiutil.quoteWikiname(pi_redirect),
 295                     urllib.quote_plus(self.page_name, ''),))
 296                 return
 297             elif verb == "deprecated":
 298                 # deprecated page, append last backup version to current contents
 299                 # (which should be a short reason why the page is deprecated)
 300                 msg = '%s<b>%s</b><br>%s' % (
 301                     wikiutil.getSmiley('/!\\', self.formatter),
 302                     _('The backupped content of this page is deprecated and will not be included in search results!'),
 303                     msg)
 304 
 305                 oldversions = wikiutil.getBackupList(config.backup_dir, self.page_name)
 306                 if oldversions:
 307                     oldfile = oldversions[0]
 308                     olddate = os.path.basename(oldfile)[len(wikiutil.quoteFilename(self.page_name))+1:]
 309                     oldpage = Page(self.page_name, date=olddate)
 310                     body = body + oldpage.get_raw_body()
 311                     del oldfile
 312                     del olddate
 313                     del oldpage
 314             elif verb == "pragma":
 315                 # store a list of name/value pairs for general use
 316                 try:
 317                     key, val = string.split(args, ' ', 1)
 318                 except (ValueError, TypeError):
 319                     pass
 320                 else:
 321                     request.setPragma(key, val)
 322             elif verb == "form":
 323                 # ignore form PIs on non-form pages
 324                 if not wikiutil.isFormPage(self.page_name):
 325                     continue
 326 
 327                 # collect form definitions
 328                 if not wikiform:
 329                     from MoinMoin import wikiform
 330                     pi_formtext.append('<table border="1" cellspacing="1" cellpadding="3">\n'
 331                         '<form method="POST" action="%s">\n'
 332                         '<input type="hidden" name="action" value="formtest">\n' % self.url())
 333                 pi_formtext.append(wikiform.parseDefinition(request, args, pi_formfields))
 334             else:
 335                 # unknown PI ==> end PI parsing
 336                 break
 337 
 338         request.clock.stop('send_page_formatter_pi')
 339         
 340         request.clock.start('send_page_doc_hdr')
 341         # start document output
 342         doc_leader = self.formatter.startDocument(self.page_name)
 343         if not content_only:
 344             # send the document leader
 345             webapi.http_headers(request)
 346             request.write(doc_leader)
 347 
 348             # send the page header
 349             if self.default_formatter:
 350                 page_needle = self.page_name
 351                 if config.allow_subpages and string.count(page_needle, '/'):
 352                     page_needle = '/' + string.split(page_needle, '/')[-1]
 353                 link = '%s/%s?action=fullsearch&value=%s&literal=1&case=1&context=40' % (
 354                     webapi.getScriptname(),
 355                     wikiutil.quoteWikiname(self.page_name),
 356                     urllib.quote_plus(page_needle, ''))
 357                 title = self.split_title()
 358                 if self.prev_date:
 359                     msg = "<b>%s</b><br>%s" % (
 360                         _('Version as of %(date)s') % {'date':
 361                             user.current.getFormattedDateTime(os.path.getmtime(self._text_filename()))},
 362                         msg)
 363                 if request.form.has_key('redirect'):
 364                     redir = request.form['redirect'].value
 365                     msg = '%s<b>%s</b><br>%s' % (
 366                         wikiutil.getSmiley('/!\\', self.formatter),
 367                         _('Redirected from page "%(page)s"') % {'page':
 368                             wikiutil.link_tag(wikiutil.quoteWikiname(redir) + "?action=show", redir)},
 369                         msg)
 370                 if pi_redirect:
 371                     msg = '%s<b>%s</b><br>%s' % (
 372                         wikiutil.getSmiley('<!>', self.formatter),
 373                         _('This page redirects to page "%(page)s"') % {'page': pi_redirect},
 374                         msg)
 375                 wikiutil.send_title(request, title, link=link, msg=msg,
 376                     pagename=self.page_name, print_mode=print_mode,
 377                     allow_doubleclick=1)
 378 
 379                 # page trail?
 380                 if not print_mode and user.current.valid:
 381                     user.current.addTrail(self.page_name)
 382                     trail = user.current.getTrail()
 383                     if trail and user.current.show_page_trail:
 384                         delim = '&gt;'
 385                         if string.lower(config.charset) == 'iso-8859-1':
 386                             delim = '»'
 387                         print '<font face="Verdana" size="-1">%s&nbsp;%s %s</font><hr>' % (
 388                             string.join(
 389                                 map(lambda p: Page(p).link_to(), trail[:-1]),
 390                                 "&nbsp;%s " % delim),
 391                             delim, cgi.escape(trail[-1]))
 392 
 393                 # user-defined form preview?
 394                 if pi_formtext:
 395                     pi_formtext.append('<input type="hidden" name="fieldlist" value="%s">\n' %
 396                         string.join(pi_formfields, "|"))
 397                     pi_formtext.append('</form></table>\n')
 398                     pi_formtext.append(_(
 399                         '<p><small>If you submit this form, the submitted values'
 400                         ' will be displayed.\nTo use this form on other pages, insert a\n'
 401                         '<br><br><b><tt>&nbsp;&nbsp;&nbsp;&nbsp;'
 402                         '[[Form("%(pagename)s")]]'
 403                         '</tt></b><br><br>\n'
 404                         'macro call.</b></small></p>\n'
 405                     ) % {'pagename': self.page_name[:-len(config.page_form_ending)]})
 406                     print string.join(pi_formtext, '')
 407 
 408         request.clock.stop('send_page_doc_hdr')
 409 
 410         request.clock.start('send_page_ld_parser')
 411         # try to load the parser
 412         Parser = pysupport.importName("MoinMoin.parser." + pi_format, "Parser")
 413         if Parser is None:
 414             # default to plain text formatter (i.e. show the page source)
 415             del Parser
 416             from parser.plain import Parser
 417         request.clock.stop('send_page_ld_parser')
 418 
 419         request.clock.start('send_page_doc_content')
 420         # new page?
 421         if not self.exists() and self.default_formatter and not content_only:
 422             self._emptyPageText(request)
 423         else:
 424             # parse the text and send the page content
 425             #Parser(body, request).format(self.formatter)
 426             self._send_page_content(request, Parser, body)
 427 
 428             # check for pending footnotes
 429             if getattr(request, 'footnotes', None):
 430                 from MoinMoin.macro.FootNote import emit_footnotes
 431                 print self.formatter.linebreak(0)
 432                 print emit_footnotes(request, self.formatter)
 433 
 434         request.clock.stop('send_page_doc_content')
 435 
 436         request.clock.start('send_page_doc_ftr')
 437         # end document output
 438         doc_trailer = self.formatter.endDocument()
 439         if not content_only:
 440             # send the page footer
 441             if self.default_formatter and not print_mode:
 442                 wikiutil.send_footer(request, self.page_name, self._last_modified(request),
 443                     print_mode=print_mode)
 444 
 445             request.write(doc_trailer)
 446 
 447         # cache the pagelinks
 448         if self.default_formatter and self.exists():
 449             arena = "pagelinks"
 450             key   = wikiutil.quoteFilename(self.page_name)
 451             cache = caching.CacheEntry(arena, key)
 452             if cache.needsUpdate(self._text_filename()):
 453                 links = self.formatter.pagelinks
 454                 links.sort()
 455                 cache.update(string.join(links, '\n'))
 456 
 457         request.clock.stop('send_page_doc_ftr')
 458         request.clock.stop('send_page')
 459 
 460 
 461     def _send_page_content(self, request, Parser, body):
 462 
 463         formatter_name = str(self.formatter.__class__).\
 464                          replace('MoinMoin.formatter.', '').\
 465                          replace('.Formatter', '')
 466 
 467         # if no caching
 468         if (self.prev_date or self.hilite_re or
 469             (not getattr(Parser, 'caching', None)) or
 470             (not formatter_name in config.caching_formats)):
 471             # parse the text and send the page content
 472             Parser(body, request).format(self.formatter)
 473             return
 474 
 475         import marshal
 476 
 477         #try cache
 478         from MoinMoin import caching, wikimacro
 479         arena = 'Page.py'
 480         key = wikiutil.quoteFilename(self.page_name) + '.' + formatter_name
 481         cache = caching.CacheEntry(arena, key)
 482         code = None
 483 
 484         # render page
 485         if cache.needsUpdate(self._text_filename()):
 486             from MoinMoin.formatter.text_python import Formatter
 487             formatter = Formatter(request, self.formatter)
 488 
 489             #redirect stdout
 490             import cStringIO, sys
 491             buffer = cStringIO.StringIO()
 492             stdout = sys.stdout
 493             sys.stdout = buffer
 494 
 495             parser = Parser(body, request)
 496             parser.format(formatter)
 497             
 498             # reset stdout
 499             sys.stdout = stdout
 500             text = buffer.getvalue()
 501             buffer.close()
 502 
 503             #print text
 504             
 505             text = text.replace('"', r'\"')
 506             # now we split the text at the code position markers <<<>>>:
 507             text = text.split('<<<>>>', len(formatter.code_fragments))
 508             # we generate python source code by inserting print statements
 509             # and putting the code fragments into place:
 510             source = ['print """', text[0]]
 511             for i in range(0,len(text)-1): 
 512                 source.extend(['"""', formatter.code_fragments[i], 'print """', text[i+1]])
 513             source.extend(['"""', ''])
 514             # putting together the code pieces, inserting newlines:
 515             source = '\n'.join(source)
 516             #print source
 517             code = compile(source, self.page_name, 'exec')
 518             cache.update(marshal.dumps(code))
 519         # offer link to refresh page cache
 520         else:
 521             refresh = wikiutil.link_tag(
 522                 wikiutil.quoteWikiname(self.page_name) +
 523                 "?action=refresh&arena=%s&key=%s" % (arena, key),
 524                 ("RefreshCache")) + (' for this page (cached %(date)s)' % \
 525                 {'date': self.formatter.request.user.getFormattedDateTime
 526                                       (cache.mtime())} + '<br>')
 527             self.formatter.request.add2footer('RefreshCache', refresh)
 528             
 529         # send page
 530         formatter = self.formatter
 531         parser = Parser(body, request)
 532         parser.formatter = formatter
 533         macro_obj = wikimacro.Macro(parser)
 534 
 535         if not code:
 536             code = marshal.loads(cache.content())
 537         
 538         exec code
 539 
 540     def _emptyPageText(self, request):
 541         from MoinMoin.action import LikePages
 542 
 543         # generate the default page content for new pages
 544         print wikiutil.link_tag(wikiutil.quoteWikiname(self.page_name)+'?action=edit',
 545             _("Create this page"))
 546 
 547         # look for template pages
 548         templates = filter(lambda page, u = wikiutil: u.isTemplatePage(page),
 549             wikiutil.getPageList(config.text_dir))
 550         if templates:
 551             print self.formatter.paragraph(1)
 552             print self.formatter.text(_('Alternatively, use one of these templates:'))
 553             print self.formatter.paragraph(0)
 554 
 555             # send list of template pages
 556             print self.formatter.bullet_list(1)
 557             for page in templates:
 558                 print self.formatter.listitem(1)
 559                 print wikiutil.link_tag("%s?action=edit&template=%s" % (
 560                         wikiutil.quoteWikiname(self.page_name),
 561                         wikiutil.quoteWikiname(page)),
 562                     page)
 563                 print self.formatter.listitem(0)
 564             print self.formatter.bullet_list(0)
 565 
 566         print self.formatter.paragraph(1)
 567         print self.formatter.text(_('To create your own templates, ' +
 568             'add a page with a name ending in Template.'))
 569         print self.formatter.paragraph(0)
 570 
 571         # list similar pages that already exist
 572         start, end, matches = LikePages.findMatches(self.page_name, request)
 573         if matches and not isinstance(matches, type('')):
 574             print self.formatter.rule()
 575             print _('<p>The following pages with similar names already exist...</p>')
 576             LikePages.showMatches(self.page_name, request, start, end, matches)
 577 
 578 
 579     def getPageLinks(self, request):
 580         """Get a list of the links on this page"""
 581         if not self.exists(): return []
 582 
 583         arena = "pagelinks"
 584         key   = wikiutil.quoteFilename(self.page_name)
 585         cache = caching.CacheEntry(arena, key)
 586         if cache.needsUpdate(self._text_filename()):
 587             # this is normally never called, but is here to fill the cache
 588             # in existing wikis; thus, we do a "null" send_page here, which
 589             # is not efficient, but reduces code duplication
 590             stdout = sys.stdout
 591             sys.stdout = cStringIO.StringIO()
 592             try:
 593                 try:
 594                     request.mode_getpagelinks = 1
 595                     Page(self.page_name).send_page(request, content_only=1)
 596                 except:
 597                     import traceback
 598                     traceback.print_exc()
 599                     cache.update('')
 600             finally:
 601                 request.mode_getpagelinks = 0
 602                 sys.stdout = stdout
 603                 if hasattr(request, '_fmt_hd_counters'):
 604                     del request._fmt_hd_counters
 605 
 606         return filter(None, string.split(cache.content(), '\n'))
 607 
 608 
 609     def getCategories(self, request):
 610         """ Return a list of categories this page belongs to.
 611         """
 612         return wikiutil.filterCategoryPages(self.getPageLinks(request))

Gespeicherte Dateianhänge

Um Dateianhänge in eine Seite einzufügen sollte unbedingt eine Angabe wie attachment:dateiname benutzt werden, wie sie auch in der folgenden Liste der Dateien erscheint. Es sollte niemals die URL des Verweises ("laden") kopiert werden, da sich diese jederzeit ändern kann und damit der Verweis auf die Datei brechen würde.
  • [laden | anzeigen] (2003-02-06 10:35:07, 23.8 KB) [[attachment:Page.py]]
 Alle Dateien | Ausgewählte Dateien: löschen verschieben auf Seite kopieren auf Seite

Sie dürfen keine Anhänge an diese Seite anhängen!