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: FabianFranz_2fPage_2epy,v 1.2 2002/06/20 01:43:05 linuxwiki 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 _split_title_py16(self, force=0):
  63         """ Return a string with the page name split by spaces, if
  64             the user wants that.
  65         """
  66         if not force and not user.current.wikiname_add_spaces: return self.page_name
  67     
  68         # Provided by Magnus Lyckå
  69         # Note that "upperletters" HAS to start with "A-Z"
  70         # This is a quickfix, and not worth any more effort
  71         temp = [self.page_name[0]]
  72         ucase = string.uppercase + config.upperletters[3:]
  73         for letter in self.page_name[1:]:
  74             if letter in ucase:
  75                 temp.append(' ')
  76             temp.append(letter)
  77         return string.join(temp,'')
  78 
  79 
  80     def _text_filename(self):
  81         """The name of the page file, possibly of an older page"""
  82         if self.prev_date:
  83             return os.path.join(config.backup_dir, wikiutil.quoteFilename(self.page_name) + "." + self.prev_date)
  84         else:
  85             return os.path.join(config.text_dir, wikiutil.quoteFilename(self.page_name))
  86 
  87 
  88     def _tmp_filename(self):
  89         """The name of the temporary file used while saving"""
  90         return os.path.join(config.text_dir, ('#' + wikiutil.quoteFilename(self.page_name) + '.' + `os.getpid()` + '#'))
  91 
  92 
  93     def exists(self):
  94         """True if the page exists"""
  95         return os.path.exists(self._text_filename())
  96 
  97 
  98     def size(self):
  99         """Return page size"""
 100         if self.raw_body is not None:
 101             return len(self.raw_body)
 102         
 103         return os.path.getsize(self._text_filename())
 104 
 105 
 106     def url(self, querystr=None):
 107         """ Return an URL for this page.
 108         """
 109         url = "%s/%s" % (webapi.getScriptname(), wikiutil.quoteWikiname(self.page_name))
 110         if querystr: url = "%s?%s" % (url, string.replace(querystr, '&', '&amp;'))
 111         return url
 112 
 113 
 114     def link_to(self, text=None, querystr=None, anchor=None):
 115         """Return HTML markup that links to this page"""
 116         text = text or self.split_title()
 117         fmt = getattr(self, 'formatter', None)
 118         url = wikiutil.quoteWikiname(self.page_name)
 119         if querystr: url = "%s?%s" % (url, string.replace(querystr, '&', '&amp;'))
 120         if anchor: url = "%s#%s" % (url, urllib.quote_plus(anchor))
 121         if self.exists():
 122             return wikiutil.link_tag(url, text, formatter=fmt)
 123         elif user.current.show_nonexist_qm:
 124             return wikiutil.link_tag(url,
 125                 '?', 'nonexistent', formatter=fmt) + text
 126         else:
 127             return wikiutil.link_tag(url, text, 'nonexistent', formatter=fmt)
 128 
 129 
 130     def get_raw_body(self):
 131         """Load the raw markup from the page file"""
 132         if self.raw_body is not None:
 133             return self.raw_body
 134 
 135         # try to open file
 136         try:
 137             file = open(self._text_filename(), 'rb')
 138         except IOError, er:
 139             import errno
 140             if er.errno == errno.ENOENT:
 141                 # just doesn't exist, return empty text (note that we
 142                 # never store empty pages, so this is detectable and also
 143                 # safe when passed to a function expecting a string)
 144                 return ""
 145             else:
 146                 raise er
 147 
 148         # read file content and make sure it is closed properly
 149         try:
 150             result = file.read()
 151         finally:
 152             file.close()
 153 
 154         return result
 155 
 156 
 157     def send_page(self, request, msg=None, **keywords):
 158         """ Send the formatted page to stdout.
 159 
 160             **form** -- CGI-Form
 161             **msg** -- if given, display message in header area
 162             **keywords** --
 163                 content_only: 1 to omit page header and footer
 164                 count_hit: add an event to the log
 165         """
 166         request.clock.start('send_page')
 167         import cgi
 168         form = request.form
 169 
 170         # determine modes
 171         print_mode = form.has_key('action') and form['action'].value == 'print'
 172         content_only = keywords.get('content_only', 0)
 173         self.hilite_re = keywords.get('hilite_re', None)
 174         if msg is None: msg = ""
 175 
 176         # count hit?
 177         if keywords.get('count_hit', 0):
 178             request.getEventLogger().add('VIEWPAGE', {'pagename': self.page_name})
 179 
 180         # load the text
 181         body = self.get_raw_body()
 182 
 183         # if necessary, load the default formatter
 184         if self.default_formatter:
 185             from formatter.text_html import Formatter
 186             self.formatter = Formatter(request, store_pagelinks=1)
 187         self.formatter.setPage(self)
 188 
 189         # default is wiki markup
 190         pi_format = config.default_markup or "wiki"
 191         pi_redirect = None
 192         pi_formtext = []
 193         pi_formfields = []
 194         wikiform = None
 195 
 196         # check for XML content
 197         if body and body[:5] == '<?xml':
 198             pi_format = "xslt"
 199 
 200         # check processing instructions
 201         while body and body[0] == '#':
 202             # extract first line
 203             try:
 204                 line, body = string.split(body, '\n', 1)
 205             except ValueError:
 206                 line = body
 207                 body = ''
 208 
 209             # skip comments (lines with two hash marks)
 210             if line[1] == '#': continue
 211 
 212             # parse the PI
 213             verb, args = string.split(line[1:]+' ', ' ', 1)
 214             verb = string.lower(verb)
 215             args = string.strip(args)
 216 
 217             # check the PIs
 218             if verb == "format":
 219                 # markup format
 220                 pi_format = string.lower(args)
 221             elif verb == "redirect":
 222                 # redirect to another page
 223                 # note that by including "action=show", we prevent
 224                 # endless looping (see code in "cgimain") or any
 225                 # cascaded redirection
 226                 pi_redirect = args
 227                 if form.has_key('action') or form.has_key('redirect') or content_only: continue
 228 
 229                 webapi.http_redirect(request, '%s/%s?action=show&redirect=%s' % (
 230                     webapi.getScriptname(),
 231                     wikiutil.quoteWikiname(pi_redirect),
 232                     urllib.quote_plus(self.page_name, ''),))
 233                 return
 234             elif verb == "deprecated":
 235                 # deprecated page, append last backup version to current contents
 236                 # (which should be a short reason why the page is deprecated)
 237                 msg = '%s<b>%s</b><br>%s' % (
 238                     wikiutil.getSmiley('/!\\', self.formatter),
 239                     _('The backupped content of this page is deprecated and will not be included in search results!'),
 240                     msg)
 241 
 242                 oldversions = wikiutil.getBackupList(config.backup_dir, self.page_name)
 243                 if oldversions:
 244                     oldfile = oldversions[0]
 245                     olddate = os.path.basename(oldfile)[len(wikiutil.quoteFilename(self.page_name))+1:]
 246                     oldpage = Page(self.page_name, date=olddate)
 247                     body = body + oldpage.get_raw_body()
 248                     del oldfile
 249                     del olddate
 250                     del oldpage
 251             elif verb == "pragma":
 252                 # store a list of name/value pairs for general use
 253                 try:
 254                     key, val = string.split(args, ' ', 1)
 255                 except (ValueError, TypeError):
 256                     pass
 257                 else:
 258                     request.setPragma(key, val)
 259             elif verb == "form":
 260                 # ignore form PIs on non-form pages
 261                 if not wikiutil.isFormPage(self.page_name):
 262                     continue
 263 
 264                 # collect form definitions
 265                 if not wikiform:
 266                     from MoinMoin import wikiform
 267                     pi_formtext.append('<table border="1" cellspacing="1" cellpadding="3">\n'
 268                         '<form method="POST" action="%s">\n'
 269                         '<input type="hidden" name="action" value="formtest">\n' % self.url())
 270                 pi_formtext.append(wikiform.parseDefinition(args, pi_formfields))
 271             else:
 272                 # unknown PI ==> end PI parsing
 273                 break
 274 
 275         # start document output
 276         doc_leader = self.formatter.startDocument(self.page_name)
 277         if not content_only:
 278             # send the document leader
 279             webapi.http_headers(request)
 280             sys.stdout.write(doc_leader)
 281 
 282             # send the page header
 283             if self.default_formatter:
 284                 page_needle = self.page_name
 285                 if config.allow_subpages and string.count(page_needle, '/'):
 286                     page_needle = '/' + string.split(page_needle, '/')[-1]
 287                 link = '%s/%s?action=fullsearch&value=%s&literal=1&case=1' % (
 288                     webapi.getScriptname(),
 289                     wikiutil.quoteWikiname(self.page_name),
 290                     urllib.quote_plus(page_needle, ''))
 291                 title = self.split_title()
 292                 if self.prev_date:
 293                     msg = "<b>%s</b><br>%s" % (
 294                         _('Version as of %(date)s') % {'date':
 295                             user.current.getFormattedDateTime(os.path.getmtime(self._text_filename()))},
 296                         msg)
 297                 if form.has_key('redirect'):
 298                     redir = form['redirect'].value
 299                     msg = '%s<b>%s</b><br>%s' % (
 300                         wikiutil.getSmiley('/!\\', self.formatter),
 301                         _('Redirected from page "%(page)s"') % {'page':
 302                             wikiutil.link_tag(wikiutil.quoteWikiname(redir) + "?action=show", redir)},
 303                         msg)
 304                 if pi_redirect:
 305                     msg = '%s<b>%s</b><br>%s' % (
 306                         wikiutil.getSmiley('<!>', self.formatter),
 307                         _('This page redirects to page "%(page)s"') % {'page': pi_redirect},
 308                         msg)
 309                 wikiutil.send_title(title, link=link, msg=msg,
 310                     pagename=self.page_name, print_mode=print_mode,
 311                     allow_doubleclick=1)
 312 
 313                 # page trail?
 314                 if not print_mode and user.current.valid:
 315                     user.current.addTrail(self.page_name)
 316                     trail = user.current.getTrail()
 317                     if trail and user.current.show_page_trail:
 318                         delim = '&gt;'
 319                         if string.lower(config.charset) == 'iso-8859-1':
 320                             delim = '»'
 321                         print '<font face="Verdana" size="-1">%s&nbsp;%s %s</font><hr>' % (
 322                             string.join(
 323                                 map(lambda p: Page(p).link_to(), trail[:-1]),
 324                                 "&nbsp;%s " % delim),
 325                             delim, cgi.escape(trail[-1]))
 326 
 327                 # user-defined form preview?
 328                 if pi_formtext:
 329                     pi_formtext.append('<input type="hidden" name="fieldlist" value="%s">\n' %
 330                         string.join(pi_formfields, "|"))
 331                     pi_formtext.append('</form></table>\n')
 332                     pi_formtext.append(_(
 333                         '<p><small>If you submit this form, the submitted values'
 334                         ' will be displayed.\nTo use this form on other pages, insert a\n'
 335                         '<br><br><b><tt>&nbsp;&nbsp;&nbsp;&nbsp;'
 336                         '[[Form("%(pagename)s")]]'
 337                         '</tt></b><br><br>\n'
 338                         'macro call.</b></small></p>\n'
 339                     ) % {'pagename': self.page_name[:-len(config.page_form_ending)]})
 340                     print string.join(pi_formtext, '')
 341 
 342         # try to load the parser
 343         Parser = util.importName("MoinMoin.parser." + pi_format, "Parser")
 344         if Parser is None:
 345             # default to plain text formatter (i.e. show the page source)
 346             del Parser
 347             from parser.plain import Parser
 348 
 349         # new page?
 350         if not self.exists() and self.default_formatter and not content_only:
 351             # generate the default page content for new pages
 352             print wikiutil.link_tag(wikiutil.quoteWikiname(self.page_name)+'?action=edit',
 353                 _("Create this page"))
 354 
 355             # look for template pages
 356             templates = filter(lambda page, u = wikiutil: u.isTemplatePage(page),
 357                 wikiutil.getPageList(config.text_dir))
 358             if templates:
 359                 print self.formatter.paragraph(1)
 360                 print self.formatter.text(_('Alternatively, use one of these templates:'))
 361                 print self.formatter.paragraph(0)
 362 
 363                 # send list of template pages
 364                 print self.formatter.bullet_list(1)
 365                 for page in templates:
 366                     print self.formatter.listitem(1)
 367                     print wikiutil.link_tag("%s?action=edit&template=%s" % (
 368                             wikiutil.quoteWikiname(self.page_name),
 369                             wikiutil.quoteWikiname(page)),
 370                         page)
 371                     print self.formatter.listitem(0)
 372                 print self.formatter.bullet_list(0)
 373 
 374             print self.formatter.paragraph(1)
 375             print self.formatter.text(_('To create you own templates, ' +
 376                 'add a page with a name ending in Template.'))
 377             print self.formatter.paragraph(0)
 378         else:
 379             # parse the text and send the page content
 380             print Parser(body, request).format(self.formatter, form, 0) # Using new parsing syntax, as printing will be set to 0 the whole buffer is returned
 381 
 382             # check for pending footnotes
 383             if getattr(request, 'footnotes', None):
 384                 from MoinMoin.macro.FootNote import emit_footnotes
 385                 print self.formatter.linebreak(0)
 386                 #print emit_footnotes(request, self.formatter)
 387                 print emit_footnotes(request, self.formatter, 1, Parser, form) # Added Variables: hasparser=1, the Parser, and the form, which wil be used for formatting the footnotes
 388 
 389         # end document output
 390         doc_trailer = self.formatter.endDocument()
 391         if not content_only:
 392             # send the page footer
 393             if self.default_formatter and not print_mode:
 394                 wikiutil.send_footer(request, self.page_name, self._last_modified(),
 395                     print_mode=print_mode)
 396 
 397             sys.stdout.write(doc_trailer)
 398 
 399         # cache the pagelinks
 400         if self.default_formatter and self.exists():
 401             arena = "pagelinks"
 402             key   = wikiutil.quoteFilename(self.page_name)
 403             cache = caching.CacheEntry(arena, key)
 404             if cache.needsUpdate(self._text_filename()):
 405                 links = self.formatter.pagelinks
 406                 links.sort()
 407                 cache.update(string.join(links, '\n'))
 408 
 409         request.clock.stop('send_page')
 410 
 411 
 412     def _last_modified(self):
 413         if not self.exists():
 414             return None
 415         return user.current.getFormattedDateTime(os.path.getmtime(self._text_filename()))
 416 
 417 
 418     def getPageLinks(self, request):
 419         """Get a list of the links on this page"""
 420         if not self.exists(): return []
 421 
 422         arena = "pagelinks"
 423         key   = wikiutil.quoteFilename(self.page_name)
 424         cache = caching.CacheEntry(arena, key)
 425         if cache.needsUpdate(self._text_filename()):
 426             # this is normally never called, but is here to fill the cache
 427             # in existing wikis; thus, we do a "null" send_page here, which
 428             # is not efficient, but reduces code duplication
 429             stdout = sys.stdout
 430             sys.stdout = cStringIO.StringIO()
 431             try:
 432                 try:
 433                     Page(self.page_name).send_page(request, content_only=1)
 434                 except:
 435                     cache.update('')
 436             finally:
 437                 sys.stdout = stdout
 438                 if hasattr(request, '_fmt_hd_counters'):
 439                     del request._fmt_hd_counters
 440 
 441         return filter(None, string.split(cache.content(), '\n'))
 442 
 443 
 444 # Python 1.6's "re" is buggy
 445 if sys.version[:3] == "1.6":
 446     Page.split_title = Page._split_title_py16

FabianFranz/Page.py (zuletzt geändert am 2007-12-23 22:45:37 durch localhost)