Back to index

moin  1.9.0~rc2
Classes | Public Member Functions | Public Attributes | Properties | Private Member Functions | Private Attributes
MoinMoin.PageEditor.PageEditor Class Reference

PageEditor - Edit pages. More...

Inheritance diagram for MoinMoin.PageEditor.PageEditor:
Inheritance graph
[legend]
Collaboration diagram for MoinMoin.PageEditor.PageEditor:
Collaboration graph
[legend]

List of all members.

Classes

class  AccessDenied
class  CouldNotLock
class  EditConflict
class  EmptyPage
class  Immutable
class  NoAdmin
class  RevertError
class  SaveError
class  Unchanged

Public Member Functions

def __init__
def mergeEditConflict
def sendconfirmleaving
def sendEditor
def sendCancel
def copyPage
def renamePage
def revertPage
def deletePage
def normalizeText
def copy_underlay_page
def saveText
def reset
def get_body
def set_body
def get_meta
def get_data
def get_pi
def getlines
def get_raw_body
def get_raw_body_str
def set_raw_body
def get_current_from_pagedir
def get_rev_dir
def get_rev
def current_rev
def get_real_rev
def getPageBasePath
def getPageStatus
def getPagePath
def editlog_entry
def edit_info
def last_edit
def lastEditInfo
def isWritable
def isUnderlayPage
def isStandardPage
def exists
def size
def mtime_usecs
def mtime_printable
def split_title
def url
def link_to_raw
def link_to
def getSubscribers
def parse_processing_instructions
def send_raw
def send_page
def getFormatterName
def canUseCache
def send_page_content
def format
def execute
def loadCache
def makeCache
def getRevList
def olderrevision
def getPageText
def getPageHeader
def getPageLinks
def parsePageLinks
def getCategories
def getParentPage
def getACL
def parseACL
def encodeTextMimeType
def decodeTextMimeType
def isConflict
def setConflict

Public Attributes

 do_revision_backup
 do_editor_backup
 uid_override
 lock
 error
 request
 cfg
 page_name
 rev
 include_self
 formatter
 output_mimetype
 default_formatter
 output_charset
 hilite_re
 page_name_fs

Properties

 body = property(fget=get_body, fset=set_body)
 meta = property(fget=get_meta)
 data = property(fget=get_data)
 pi = property(fget=get_pi)

Private Member Functions

def _get_local_timestamp
def _expand_variables
def _save_draft
def _load_draft
def _write_file

Private Attributes

 _

Detailed Description

PageEditor - Edit pages.

Editor for a wiki page. 

Definition at line 60 of file PageEditor.py.


Constructor & Destructor Documentation

def MoinMoin.PageEditor.PageEditor.__init__ (   self,
  request,
  page_name,
  keywords 
)
Create page editor object.

@param page_name: name of the page
@param request: the request object
@keyword do_revision_backup: if 0, suppress making a page backup per revision
@keyword do_editor_backup: if 0, suppress saving of draft copies
@keyword uid_override: override user id and name (default None)

Reimplemented from MoinMoin.Page.Page.

Definition at line 83 of file PageEditor.py.

00083 
00084     def __init__(self, request, page_name, **keywords):
00085         """ Create page editor object.
00086 
00087         @param page_name: name of the page
00088         @param request: the request object
00089         @keyword do_revision_backup: if 0, suppress making a page backup per revision
00090         @keyword do_editor_backup: if 0, suppress saving of draft copies
00091         @keyword uid_override: override user id and name (default None)
00092         """
00093         Page.__init__(self, request, page_name, **keywords)
00094         self._ = request.getText
00095 
00096         self.do_revision_backup = keywords.get('do_revision_backup', 1)
00097         self.do_editor_backup = keywords.get('do_editor_backup', 1)
00098         self.uid_override = keywords.get('uid_override', None)
00099 
00100         self.lock = PageLock(self)


Member Function Documentation

def MoinMoin.PageEditor.PageEditor._expand_variables (   self,
  text 
) [private]
Expand @VARIABLE@ in `text`and return the expanded text.

@param text: current text of wikipage
@rtype: string
@return: new text of wikipage, variables replaced

Definition at line 755 of file PageEditor.py.

00755 
00756     def _expand_variables(self, text):
00757         """ Expand @VARIABLE@ in `text`and return the expanded text.
00758 
00759         @param text: current text of wikipage
00760         @rtype: string
00761         @return: new text of wikipage, variables replaced
00762         """
00763         # TODO: Allow addition of variables via wikiconfig or a global wiki dict.
00764         request = self.request
00765         now = self._get_local_timestamp()
00766         u = request.user
00767         obfuscated_email_address = encodeSpamSafeEmail(u.email)
00768         signature = u.signature()
00769         variables = {
00770             'PAGE': self.page_name,
00771             'TIME': "<<DateTime(%s)>>" % now,
00772             'DATE': "<<Date(%s)>>" % now,
00773             'ME': u.name,
00774             'USERNAME': signature,
00775             'USER': "-- %s" % signature,
00776             'SIG': "-- %s <<DateTime(%s)>>" % (signature, now),
00777             'EMAIL': "<<MailTo(%s)>>" % (obfuscated_email_address)
00778         }
00779 
00780         if u.valid and u.name:
00781             if u.email:
00782                 variables['MAILTO'] = "<<MailTo(%s)>>" % u.email
00783             # Users can define their own variables via
00784             # UserHomepage/MyDict, which override the default variables.
00785             userDictPage = u.name + "/MyDict"
00786             if userDictPage in request.dicts:
00787                 variables.update(request.dicts[userDictPage])
00788 
00789         for name in variables:
00790             text = text.replace('@%s@' % name, variables[name])
00791         return text

Here is the call graph for this function:

Here is the caller graph for this function:

Returns the string that can be used by the TIME substitution.

@return: str with a timestamp in it

Definition at line 728 of file PageEditor.py.

00728 
00729     def _get_local_timestamp(self):
00730         """ Returns the string that can be used by the TIME substitution.
00731 
00732         @return: str with a timestamp in it
00733         """
00734 
00735         now = time.time()
00736         # default: UTC
00737         zone = "Z"
00738         u = self.request.user
00739 
00740         # setup the timezone
00741         if u.valid and u.tz_offset:
00742             tz = u.tz_offset
00743             # round to minutes
00744             tz -= tz % 60
00745             minutes = tz / 60
00746             hours = minutes / 60
00747             minutes -= hours * 60
00748 
00749             # construct the offset
00750             zone = "%+0.2d%02d" % (hours, minutes)
00751             # correct the time by the offset we've found
00752             now += tz
00753 
00754         return time.strftime("%Y-%m-%dT%H:%M:%S", timefuncs.tmtuple(now)) + zone

Here is the caller graph for this function:

Get a draft from the drafts cache arena.

@rtype: unicode
@return: draft text or None

Definition at line 852 of file PageEditor.py.

00852 
00853     def _load_draft(self):
00854         """ Get a draft from the drafts cache arena.
00855 
00856         @rtype: unicode
00857         @return: draft text or None
00858         """
00859         request = self.request
00860         if not request.user.valid:
00861             return None
00862 
00863         arena = 'drafts'
00864         key = request.user.id
00865         cache = caching.CacheEntry(request, arena, key, scope='wiki', use_pickle=True)
00866         pagename = self.page_name
00867         try:
00868             cache_data = cache.content()
00869             return cache_data.get(pagename)
00870         except caching.CacheError:
00871             return None

Here is the caller graph for this function:

def MoinMoin.PageEditor.PageEditor._save_draft (   self,
  text,
  rev,
  kw 
) [private]
Save an editor backup to the drafts cache arena.

@param text: draft text of the page
     (if None, the draft gets removed from the cache)
@param rev: the revision of the page this draft is based on
@param kw: no keyword args used currently

Definition at line 822 of file PageEditor.py.

00822 
00823     def _save_draft(self, text, rev, **kw):
00824         """ Save an editor backup to the drafts cache arena.
00825 
00826         @param text: draft text of the page
00827                      (if None, the draft gets removed from the cache)
00828         @param rev: the revision of the page this draft is based on
00829         @param kw: no keyword args used currently
00830         """
00831         request = self.request
00832         if not request.user.valid or not self.do_editor_backup:
00833             return None
00834 
00835         arena = 'drafts'
00836         key = request.user.id
00837         cache = caching.CacheEntry(request, arena, key, scope='wiki', use_pickle=True)
00838         if cache.exists():
00839             cache_data = cache.content()
00840         else:
00841             cache_data = {}
00842         pagename = self.page_name
00843         if text is None:
00844             try:
00845                 del cache_data[pagename]
00846             except:
00847                 pass
00848         else:
00849             timestamp = int(time.time())
00850             cache_data[pagename] = (timestamp, rev, text)
00851         cache.update(cache_data)

Here is the caller graph for this function:

def MoinMoin.PageEditor.PageEditor._write_file (   self,
  text,
  action = 'SAVE',
  comment = u'',
  extra = u'',
  deleted = False 
) [private]
Write the text to the page file (and make a backup of old page).

@param text: text to save for this page
@param deleted: if True, then don't write page content (used by deletePage)
@rtype: int
@return: mtime_usec of new page

Definition at line 889 of file PageEditor.py.

00889 
00890     def _write_file(self, text, action='SAVE', comment=u'', extra=u'', deleted=False):
00891         """ Write the text to the page file (and make a backup of old page).
00892 
00893         @param text: text to save for this page
00894         @param deleted: if True, then don't write page content (used by deletePage)
00895         @rtype: int
00896         @return: mtime_usec of new page
00897         """
00898         request = self.request
00899         _ = self._
00900         was_deprecated = self.pi.get('deprecated', False)
00901 
00902         self.copy_underlay_page()
00903 
00904         # remember conflict state
00905         self.setConflict(wikiutil.containsConflictMarker(text))
00906 
00907         # Write always on the standard directory, never change the
00908         # underlay directory copy!
00909         pagedir = self.getPagePath(use_underlay=0, check_create=0)
00910 
00911         revdir = os.path.join(pagedir, 'revisions')
00912         cfn = os.path.join(pagedir, 'current')
00913         clfn = os.path.join(pagedir, 'current-locked')
00914         cltfn = os.path.join(pagedir, 'current-locked.tmp')
00915 
00916         # !!! these log objects MUST be created outside the locked area !!!
00917 
00918         # The local log should be the standard edit log, not the
00919         # underlay copy log!
00920         pagelog = self.getPagePath('edit-log', use_underlay=0, isfile=1)
00921         llog = editlog.EditLog(request, filename=pagelog,
00922                                uid_override=self.uid_override)
00923         # Open the global log
00924         glog = editlog.EditLog(request, uid_override=self.uid_override)
00925 
00926         if not os.path.exists(pagedir): # new page, create and init pagedir
00927             os.mkdir(pagedir)
00928         if not os.path.exists(revdir):
00929             os.mkdir(revdir)
00930             f = file(cfn, 'w')
00931             f.write('%08d\n' % 0)
00932             f.close()
00933 
00934         got_lock = False
00935         retry = 0
00936 
00937         try:
00938             while not got_lock and retry < 100:
00939                 retry += 1
00940                 try:
00941                     filesys.rename(cfn, clfn)
00942                     got_lock = True
00943                 except OSError, err:
00944                     got_lock = False
00945                     if err.errno == 2: # there was no 'current' file
00946                         time.sleep(0.1)
00947                     else:
00948                         raise self.CouldNotLock, _("Page could not get locked. Unexpected error (errno=%d).") % err.errno
00949 
00950             if not got_lock:
00951                 raise self.CouldNotLock, _("Page could not get locked. Missing 'current' file?")
00952 
00953             # increment rev number of current(-locked) page
00954             f = file(clfn)
00955             revstr = f.read()
00956             f.close()
00957             try:
00958                 rev = int(revstr)
00959             except ValueError, err:
00960                 raise self.SaveError, _("Unable to determine current page revision from the 'current' file. The page %s is damaged and cannot be edited right now.") % self.page_name
00961 
00962             if not was_deprecated:
00963                 if self.do_revision_backup or rev == 0:
00964                     rev += 1
00965             revstr = '%08d' % rev
00966             # write the current page rev to a temporary file
00967             try:
00968                 f = file(cltfn, 'w')
00969                 f.write(revstr+'\n')
00970                 f.close()
00971             except IOError, err:
00972                 try:
00973                     os.remove(cltfn)
00974                 except:
00975                     pass # we don't care for errors in the os.remove
00976                 # throw a nicer exception
00977                 if err.errno == errno.ENOSPC:
00978                     raise self.SaveError, _("Cannot save page %s, no storage space left.") % self.page_name
00979                 else:
00980                     raise self.SaveError, _("An I/O error occurred while saving page %s (errno=%d)") % (self.page_name, err.errno)
00981             # atomically put it in place (except on windows)
00982             else:
00983                 filesys.rename(cltfn, clfn)
00984 
00985             if not deleted:
00986                 # save to page file
00987                 pagefile = os.path.join(revdir, revstr)
00988                 f = codecs.open(pagefile, 'wb', config.charset)
00989                 # Write the file using text/* mime type
00990                 f.write(self.encodeTextMimeType(text))
00991                 f.close()
00992                 mtime_usecs = wikiutil.timestamp2version(os.path.getmtime(pagefile))
00993                 # set in-memory content
00994                 self.set_raw_body(text)
00995             else:
00996                 mtime_usecs = wikiutil.timestamp2version(time.time())
00997                 # set in-memory content
00998                 self.set_raw_body(None)
00999 
01000             # reset page object
01001             self.reset()
01002 
01003             # write the editlog entry
01004             # for now simply make 2 logs, better would be some multilog stuff maybe
01005             if self.do_revision_backup:
01006                 # do not globally log edits with no revision backup
01007                 # if somebody edits a deprecated page, log it in global log, but not local log
01008                 glog.add(request, mtime_usecs, rev, action, self.page_name, None, extra, comment)
01009             if not was_deprecated and self.do_revision_backup:
01010                 # if we did not create a new revision number, do not locally log it
01011                 llog.add(request, mtime_usecs, rev, action, self.page_name, None, extra, comment)
01012         finally:
01013             if got_lock:
01014                 filesys.rename(clfn, cfn)
01015 
01016         # add event log entry
01017         elog = eventlog.EventLog(request)
01018         elog.add(request, 'SAVEPAGE', {'pagename': self.page_name}, 1, mtime_usecs)
01019 
01020         return mtime_usecs, rev

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.canUseCache (   self,
  parser = None 
) [inherited]
Is caching available for this request?

This make sure we can try to use the caching system for this
request, but it does not make sure that this will
succeed. Themes can use this to decide if a Refresh action
should be displayed.

@param parser: the parser used to render the page
@rtype: bool
@return: if this page can use caching

Definition at line 1244 of file Page.py.

01244 
01245     def canUseCache(self, parser=None):
01246         """ Is caching available for this request?
01247 
01248         This make sure we can try to use the caching system for this
01249         request, but it does not make sure that this will
01250         succeed. Themes can use this to decide if a Refresh action
01251         should be displayed.
01252 
01253         @param parser: the parser used to render the page
01254         @rtype: bool
01255         @return: if this page can use caching
01256         """
01257         if (not self.rev and
01258             not self.hilite_re and
01259             not self.__body_modified and
01260             self.getFormatterName() in self.cfg.caching_formats):
01261             # Everything is fine, now check the parser:
01262             if parser is None:
01263                 parser = wikiutil.searchAndImportPlugin(self.request.cfg, "parser", self.pi['format'])
01264             return getattr(parser, 'caching', False)
01265         return False

Here is the call graph for this function:

Here is the caller graph for this function:

Copy a page from underlay directory to page directory 

Definition at line 872 of file PageEditor.py.

00872 
00873     def copy_underlay_page(self):
00874         # renamed from copypage to avoid conflicts with copyPage
00875         """ Copy a page from underlay directory to page directory """
00876         src = self.getPagePath(use_underlay=1, check_create=0)
00877         dst = self.getPagePath(use_underlay=0, check_create=0)
00878         if src and dst and src != dst and os.path.exists(src):
00879             try:
00880                 os.rmdir(dst) # simply remove empty dst dirs
00881                 # XXX in fact, we should better remove anything we regard as an
00882                 # empty page, maybe also if there is also an edit-lock or
00883                 # empty cache. revisions subdir...
00884             except:
00885                 pass
00886             if not os.path.exists(dst):
00887                 filesys.copytree(src, dst)
00888                 self.reset() # reinit stuff

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.PageEditor.PageEditor.copyPage (   self,
  newpagename,
  comment = u'' 
)
Copy the current version of the page (keeping the backups, logs and attachments).

@param comment: Comment given by user
@rtype: unicode
@return: success flag, error message

Definition at line 523 of file PageEditor.py.

00523 
00524     def copyPage(self, newpagename, comment=u''):
00525         """ Copy the current version of the page (keeping the backups, logs and attachments).
00526 
00527         @param comment: Comment given by user
00528         @rtype: unicode
00529         @return: success flag, error message
00530         """
00531         request = self.request
00532         _ = self._
00533 
00534         if not newpagename:
00535             return False, _("You can't copy to an empty pagename.")
00536 
00537         if not self.request.user.may.write(newpagename):
00538             return False, _('You are not allowed to copy this page!')
00539 
00540         newpage = PageEditor(request, newpagename)
00541 
00542         pageexists_error = _("""'''A page with the name {{{'%s'}}} already exists.'''
00543 
00544 Try a different name.""", wiki=True) % (wikiutil.escape(newpagename), )
00545 
00546         # Check whether a page with the new name already exists
00547         if newpage.exists(includeDeleted=1):
00548             return False, pageexists_error
00549 
00550         # Get old page text
00551         savetext = self.get_raw_body()
00552 
00553         oldpath = self.getPagePath(check_create=0)
00554         newpath = newpage.getPagePath(check_create=0)
00555 
00556         # Copy page
00557         # NOTE: might fail if another process created newpagename just
00558         try:
00559             filesys.copytree(oldpath, newpath)
00560             self.error = None
00561             savetext = u"## page was copied from %s\n%s" % (self.page_name, savetext)
00562             Page.__init__(self, request, newpagename)
00563             self._write_file(savetext, "SAVENEW", comment)
00564 
00565             event = PageCopiedEvent(request, newpage, self, comment)
00566             send_event(event)
00567 
00568             return True, None
00569         except OSError, err:
00570             # Try to understand what happened. Maybe its better to check
00571             # the error code, but I just reused the available code above...
00572             if newpage.exists(includeDeleted=1):
00573                 return False, pageexists_error
00574             else:
00575                 return False, _('Could not copy page because of file system error: %s.') % unicode(err)

Here is the call graph for this function:

def MoinMoin.Page.Page.current_rev (   self) [inherited]
Return number of current revision.

This is the same as get_rev()[1].

@return: int revision

Definition at line 411 of file Page.py.

00411 
00412     def current_rev(self):
00413         """ Return number of current revision.
00414 
00415         This is the same as get_rev()[1].
00416 
00417         @return: int revision
00418         """
00419         pagefile, rev, exists = self.get_rev()
00420         return rev

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.decodeTextMimeType (   self,
  text 
) [inherited]
Decode text from text/* mime type to moin internal representation

@param text: text to decode (unicode). Text must use CRLF!
@rtype: unicode
@return: text using internal representation

Definition at line 1622 of file Page.py.

01622 
01623     def decodeTextMimeType(self, text):
01624         """ Decode text from text/* mime type to moin internal representation
01625 
01626         @param text: text to decode (unicode). Text must use CRLF!
01627         @rtype: unicode
01628         @return: text using internal representation
01629         """
01630         text = text.replace(u'\r', u'')
01631         return text

Here is the caller graph for this function:

def MoinMoin.PageEditor.PageEditor.deletePage (   self,
  comment = None 
)
Delete the current version of the page (making a backup before deletion
    and keeping the backups, logs and attachments).

@param comment: Comment given by user
@rtype: unicode
@return: success flag, error message

Definition at line 683 of file PageEditor.py.

00683 
00684     def deletePage(self, comment=None):
00685         """ Delete the current version of the page (making a backup before deletion
00686             and keeping the backups, logs and attachments).
00687 
00688         @param comment: Comment given by user
00689         @rtype: unicode
00690         @return: success flag, error message
00691         """
00692         request = self.request
00693         _ = self._
00694         success = True
00695         if not (request.user.may.write(self.page_name)
00696                 and request.user.may.delete(self.page_name)):
00697             msg = _('You are not allowed to delete this page!')
00698             raise self.AccessDenied, msg
00699 
00700         try:
00701             msg = self.saveText(u"deleted\n", 0, comment=comment or u'', deleted=True, notify=False)
00702             msg = msg.replace(
00703                 _("Thank you for your changes. Your attention to detail is appreciated."),
00704                 _('Page "%s" was successfully deleted!') % (wikiutil.escape(self.page_name), ))
00705 
00706             event = PageDeletedEvent(request, self, comment)
00707             send_event(event)
00708 
00709         except self.SaveError, message:
00710             # XXX do not only catch base class SaveError here, but
00711             # also the derived classes, so we can give better err msgs
00712             success = False
00713             msg = "SaveError has occured in PageEditor.deletePage. We need locking there."
00714 
00715         # delete pagelinks
00716         arena = self
00717         key = 'pagelinks'
00718         cache = caching.CacheEntry(request, arena, key, scope='item')
00719         cache.remove()
00720 
00721         # clean the cache
00722         for formatter_name in self.cfg.caching_formats:
00723             arena = self
00724             key = formatter_name
00725             cache = caching.CacheEntry(request, arena, key, scope='item')
00726             cache.remove()
00727         return success, msg

Here is the call graph for this function:

def MoinMoin.Page.Page.edit_info (   self) [inherited]
Return timestamp/editor info for this Page object (can be an old revision).

    Note: if you ask about a deleted revision, it will report timestamp and editor
  for the delete action (in the edit-log, this is just a SAVE).

This is used by MoinMoin/xmlrpc/__init__.py.

@rtype: dict
@return: timestamp and editor information

Definition at line 541 of file Page.py.

00541 
00542     def edit_info(self):
00543         """ Return timestamp/editor info for this Page object (can be an old revision).
00544 
00545             Note: if you ask about a deleted revision, it will report timestamp and editor
00546                   for the delete action (in the edit-log, this is just a SAVE).
00547 
00548         This is used by MoinMoin/xmlrpc/__init__.py.
00549 
00550         @rtype: dict
00551         @return: timestamp and editor information
00552         """
00553         line = self.editlog_entry()
00554         if line:
00555             editordata = line.getInterwikiEditorData(self.request)
00556             if editordata[0] == 'interwiki':
00557                 editor = "%s:%s" % editordata[1]
00558             else:
00559                 editor = editordata[1] # ip or email or anon
00560             result = {
00561                 'timestamp': line.ed_time_usecs,
00562                 'editor': editor,
00563             }
00564             del line
00565         else:
00566             result = {}
00567         return result

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.editlog_entry (   self) [inherited]
Return the edit-log entry for this Page object (can be an old revision).

Definition at line 518 of file Page.py.

00518 
00519     def editlog_entry(self):
00520         """ Return the edit-log entry for this Page object (can be an old revision).
00521         """
00522         request = self.request
00523         use_cache = self.rev == 0 # use the cache for current rev
00524         if use_cache:
00525             cache_name, cache_key = self.page_name, 'lastlog'
00526             entry = request.cfg.cache.meta.getItem(request, cache_name, cache_key)
00527         else:
00528             entry = None
00529         if entry is None:
00530             from MoinMoin.logfile import editlog
00531             wanted_rev = "%08d" % self.get_real_rev()
00532             edit_log = editlog.EditLog(request, rootpagename=self.page_name)
00533             for entry in edit_log.reverse():
00534                 if entry.rev == wanted_rev:
00535                     break
00536             else:
00537                 entry = () # don't use None
00538             if use_cache:
00539                 request.cfg.cache.meta.putItem(request, cache_name, cache_key, entry)
00540         return entry

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.encodeTextMimeType (   self,
  text 
) [inherited]
Encode text from moin internal representation to text/* mime type

Make sure text uses CRLF line ends, keep trailing newline.

@param text: text to encode (unicode)
@rtype: unicode
@return: encoded text

Definition at line 1605 of file Page.py.

01605 
01606     def encodeTextMimeType(self, text):
01607         """ Encode text from moin internal representation to text/* mime type
01608 
01609         Make sure text uses CRLF line ends, keep trailing newline.
01610 
01611         @param text: text to encode (unicode)
01612         @rtype: unicode
01613         @return: encoded text
01614         """
01615         if text:
01616             lines = text.splitlines()
01617             # Keep trailing newline
01618             if text.endswith(u'\n') and not lines[-1] == u'':
01619                 lines.append(u'')
01620             text = u'\r\n'.join(lines)
01621         return text

Here is the caller graph for this function:

def MoinMoin.Page.Page.execute (   self,
  request,
  parser,
  code 
) [inherited]
Write page content by executing cache code 

Definition at line 1304 of file Page.py.

01304 
01305     def execute(self, request, parser, code):
01306         """ Write page content by executing cache code """
01307         formatter = self.formatter
01308         request.clock.start("Page.execute")
01309         try:
01310             from MoinMoin.macro import Macro
01311             macro_obj = Macro(parser)
01312             # Fix __file__ when running from a zip package
01313             import MoinMoin
01314             if hasattr(MoinMoin, '__loader__'):
01315                 __file__ = os.path.join(MoinMoin.__loader__.archive, 'dummy')
01316             try:
01317                 exec code
01318             except "CacheNeedsUpdate": # convert the exception
01319                 raise Exception("CacheNeedsUpdate")
01320         finally:
01321             request.clock.stop("Page.execute")

Here is the caller graph for this function:

def MoinMoin.Page.Page.exists (   self,
  rev = 0,
  domain = None,
  includeDeleted = False 
) [inherited]
Does this page exist?

This is the lower level method for checking page existence. Use
the higher level methods isUnderlayPage and isStandardPage for
cleaner code.

@param rev: revision to look for. Default: check current
@param domain: where to look for the page. Default: look in all,
       available values: 'underlay', 'standard'
@param includeDeleted: ignore page state, just check its pagedir
@rtype: bool
@return: true, if page exists

Definition at line 629 of file Page.py.

00629 
00630     def exists(self, rev=0, domain=None, includeDeleted=False):
00631         """ Does this page exist?
00632 
00633         This is the lower level method for checking page existence. Use
00634         the higher level methods isUnderlayPage and isStandardPage for
00635         cleaner code.
00636 
00637         @param rev: revision to look for. Default: check current
00638         @param domain: where to look for the page. Default: look in all,
00639                        available values: 'underlay', 'standard'
00640         @param includeDeleted: ignore page state, just check its pagedir
00641         @rtype: bool
00642         @return: true, if page exists
00643         """
00644         # Edge cases
00645         if domain == 'underlay' and not self.request.cfg.data_underlay_dir:
00646             return False
00647 
00648         if includeDeleted:
00649             # Look for page directory, ignore page state
00650             if domain is None:
00651                 checklist = [0, 1]
00652             else:
00653                 checklist = [domain == 'underlay']
00654             for use_underlay in checklist:
00655                 pagedir = self.getPagePath(use_underlay=use_underlay, check_create=0)
00656                 if os.path.exists(pagedir):
00657                     return True
00658             return False
00659         else:
00660             # Look for non-deleted pages only, using get_rev
00661             if not rev and self.rev:
00662                 rev = self.rev
00663 
00664             if domain is None:
00665                 use_underlay = -1
00666             else:
00667                 use_underlay = domain == 'underlay'
00668             d, d, exists = self.get_rev(use_underlay, rev)
00669             return exists

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.format (   self,
  parser 
) [inherited]
Format and write page content without caching 

Definition at line 1300 of file Page.py.

01300 
01301     def format(self, parser):
01302         """ Format and write page content without caching """
01303         parser.format(self.formatter)

Here is the caller graph for this function:

def MoinMoin.Page.Page.get_body (   self) [inherited]

Definition at line 209 of file Page.py.

00209 
00210     def get_body(self):
00211         if self.__body is None:
00212             # try to open file
00213             try:
00214                 f = codecs.open(self._text_filename(), 'rb', config.charset)
00215             except IOError, er:
00216                 import errno
00217                 if er.errno == errno.ENOENT:
00218                     # just doesn't exist, return empty text (note that we
00219                     # never store empty pages, so this is detectable and also
00220                     # safe when passed to a function expecting a string)
00221                     return ""
00222                 else:
00223                     raise
00224 
00225             # read file content and make sure it is closed properly
00226             try:
00227                 text = f.read()
00228                 text = self.decodeTextMimeType(text)
00229                 self.__body = text
00230             finally:
00231                 f.close()
00232         return self.__body

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.get_current_from_pagedir (   self,
  pagedir 
) [inherited]
Get the current revision number from an arbitrary pagedir.
    Does not modify page object's state, uncached, direct disk access.
    @param pagedir: the pagedir with the 'current' file to read
    @return: int currentrev

Definition at line 294 of file Page.py.

00294 
00295     def get_current_from_pagedir(self, pagedir):
00296         """ Get the current revision number from an arbitrary pagedir.
00297             Does not modify page object's state, uncached, direct disk access.
00298             @param pagedir: the pagedir with the 'current' file to read
00299             @return: int currentrev
00300         """
00301         revfilename = os.path.join(pagedir, 'current')
00302         try:
00303             revfile = file(revfilename)
00304             revstr = revfile.read().strip()
00305             revfile.close()
00306             rev = int(revstr)
00307         except:
00308             rev = 99999999 # XXX do some better error handling
00309         return rev

Here is the caller graph for this function:

def MoinMoin.Page.Page.get_data (   self) [inherited]

Definition at line 245 of file Page.py.

00245 
00246     def get_data(self):
00247         if self.__data is None:
00248             self.__meta, self.__data = wikiutil.get_processing_instructions(self.body)
        return self.__data
def MoinMoin.Page.Page.get_meta (   self) [inherited]

Definition at line 239 of file Page.py.

00239 
00240     def get_meta(self):
00241         if self.__meta is None:
00242             self.__meta, self.__data = wikiutil.get_processing_instructions(self.body)
        return self.__meta
def MoinMoin.Page.Page.get_pi (   self) [inherited]

Definition at line 251 of file Page.py.

00251 
00252     def get_pi(self):
00253         if self.__pi is None:
00254             self.__pi = self.parse_processing_instructions()
        return self.__pi

Here is the call graph for this function:

def MoinMoin.Page.Page.get_raw_body (   self) [inherited]
Load the raw markup from the page file.

@rtype: unicode
@return: raw page contents of this page, unicode

Definition at line 265 of file Page.py.

00265 
00266     def get_raw_body(self):
00267         """ Load the raw markup from the page file.
00268 
00269         @rtype: unicode
00270         @return: raw page contents of this page, unicode
00271         """
00272         return self.body

Here is the caller graph for this function:

def MoinMoin.Page.Page.get_raw_body_str (   self) [inherited]
Returns the raw markup from the page file, as a string.

@rtype: str
@return: raw page contents of this page, utf-8-encoded

Definition at line 273 of file Page.py.

00273 
00274     def get_raw_body_str(self):
00275         """ Returns the raw markup from the page file, as a string.
00276 
00277         @rtype: str
00278         @return: raw page contents of this page, utf-8-encoded
00279         """
00280         return self.body.encode("utf-8")

def MoinMoin.Page.Page.get_real_rev (   self) [inherited]
Returns the real revision number of this page.
    A rev==0 is translated to the current revision.

@returns: revision number > 0
@rtype: int

Definition at line 421 of file Page.py.

00421 
00422     def get_real_rev(self):
00423         """ Returns the real revision number of this page.
00424             A rev==0 is translated to the current revision.
00425 
00426         @returns: revision number > 0
00427         @rtype: int
00428         """
00429         if self.rev == 0:
00430             return self.current_rev()
00431         return self.rev

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.get_rev (   self,
  use_underlay = -1,
  rev = 0 
) [inherited]
Get information about a revision.

filename, number, and (existance test) of this page and revision.

@param use_underlay: -1 == auto, 0 == normal, 1 == underlay
@param rev: int revision to get (default is 0 and means the current
    revision (in this case, the real revint is returned)
@return: (str path to current revision of page,
  int realrevint,
  bool exists)

Definition at line 362 of file Page.py.

00362 
00363     def get_rev(self, use_underlay=-1, rev=0):
00364         """ Get information about a revision.
00365 
00366         filename, number, and (existance test) of this page and revision.
00367 
00368         @param use_underlay: -1 == auto, 0 == normal, 1 == underlay
00369         @param rev: int revision to get (default is 0 and means the current
00370                     revision (in this case, the real revint is returned)
00371         @return: (str path to current revision of page,
00372                   int realrevint,
00373                   bool exists)
00374         """
00375         def layername(underlay):
00376             if underlay == -1:
00377                 return 'layer_auto'
00378             elif underlay == 0:
00379                 return 'layer_normal'
00380             else: # 1
00381                 return 'layer_underlay'
00382 
00383         request = self.request
00384         cache_name = self.page_name
00385         cache_key = layername(use_underlay)
00386         if self._text_filename_force is None:
00387             cache_data = request.cfg.cache.meta.getItem(request, cache_name, cache_key)
00388             if cache_data and (rev == 0 or rev == cache_data[1]):
00389                 # we got the correct rev data from the cache
00390                 #logging.debug("got data from cache: %r %r %r" % cache_data)
00391                 return cache_data
00392 
00393         # Figure out if we should use underlay or not, if needed.
00394         if use_underlay == -1:
00395             underlay, pagedir = self.getPageStatus(check_create=0)
00396         else:
00397             underlay, pagedir = use_underlay, self._pagepath[use_underlay]
00398 
00399         # Find current revision, if automatic selection is requested.
00400         if rev == 0:
00401             realrev = self.get_current_from_pagedir(pagedir)
00402         else:
00403             realrev = rev
00404 
00405         data = self.get_rev_dir(pagedir, realrev)
00406         if rev == 0 and self._text_filename_force is None:
00407             # we only save the current rev to the cache
00408             request.cfg.cache.meta.putItem(request, cache_name, cache_key, data)
00409 
00410         return data

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.get_rev_dir (   self,
  pagedir,
  rev = 0 
) [inherited]
Get a revision of a page from an arbitrary pagedir.

Does not modify page object's state, uncached, direct disk access.

@param pagedir: the path to the page storage area
@param rev: int revision to get (default is 0 and means the current
    revision (in this case, the real revint is returned)
@return: (str path to file of the revision,
  int realrevint,
  bool exists)

Definition at line 310 of file Page.py.

00310 
00311     def get_rev_dir(self, pagedir, rev=0):
00312         """ Get a revision of a page from an arbitrary pagedir.
00313 
00314         Does not modify page object's state, uncached, direct disk access.
00315 
00316         @param pagedir: the path to the page storage area
00317         @param rev: int revision to get (default is 0 and means the current
00318                     revision (in this case, the real revint is returned)
00319         @return: (str path to file of the revision,
00320                   int realrevint,
00321                   bool exists)
00322         """
00323         if rev == 0:
00324             rev = self.get_current_from_pagedir(pagedir)
00325 
00326         revstr = '%08d' % rev
00327         pagefile = os.path.join(pagedir, 'revisions', revstr)
00328         if rev != 99999999:
00329             exists = os.path.exists(pagefile)
00330             if exists:
00331                 self._setRealPageName(pagedir)
00332         else:
00333             exists = False
00334         return pagefile, rev, exists

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.getACL (   self,
  request 
) [inherited]
Get cached ACLs of this page.

Return cached ACL or invoke parseACL and update the cache.

@param request: the request object
@rtype: MoinMoin.security.AccessControlList
@return: ACL of this page

Definition at line 1546 of file Page.py.

01546 
01547     def getACL(self, request):
01548         """ Get cached ACLs of this page.
01549 
01550         Return cached ACL or invoke parseACL and update the cache.
01551 
01552         @param request: the request object
01553         @rtype: MoinMoin.security.AccessControlList
01554         @return: ACL of this page
01555         """
01556         try:
01557             return self.__acl # for request.page, this is n-1 times used
01558         except AttributeError:
01559             # the caching here is still useful for pages != request.page,
01560             # when we have multiple page objects for the same page name.
01561             request.clock.start('getACL')
01562             # Try the cache or parse acl and update the cache
01563             currentRevision = self.current_rev()
01564             cache_name = self.page_name
01565             cache_key = 'acl'
01566             cache_data = request.cfg.cache.meta.getItem(request, cache_name, cache_key)
01567             if cache_data is None:
01568                 aclRevision, acl = None, None
01569             else:
01570                 aclRevision, acl = cache_data
01571             #logging.debug("currrev: %r, cachedaclrev: %r" % (currentRevision, aclRevision))
01572             if aclRevision != currentRevision:
01573                 acl = self.parseACL()
01574                 if currentRevision != 99999999:
01575                     # don't use cache for non existing pages
01576                     # otherwise in the process of creating copies by filesys.copytree (PageEditor.copyPage)
01577                     # the first may test will create a cache entry with the default_acls for a non existing page
01578                     # At the time the page is created acls on that page would be ignored until the process
01579                     # is completed by adding a log entry into edit-log
01580                     cache_data = (currentRevision, acl)
01581                     request.cfg.cache.meta.putItem(request, cache_name, cache_key, cache_data)
01582             self.__acl = acl
01583             request.clock.stop('getACL')
01584             return acl

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.getCategories (   self,
  request 
) [inherited]
Get categories this page belongs to.

@param request: the request object
@rtype: list
@return: categories this page belongs to

Definition at line 1523 of file Page.py.

01523 
01524     def getCategories(self, request):
01525         """ Get categories this page belongs to.
01526 
01527         @param request: the request object
01528         @rtype: list
01529         @return: categories this page belongs to
01530         """
01531         return wikiutil.filterCategoryPages(request, self.getPageLinks(request))

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.getFormatterName (   self) [inherited]
Return a formatter name as used in the caching system

@rtype: string
@return: formatter name as used in caching

Definition at line 1233 of file Page.py.

01233 
01234     def getFormatterName(self):
01235         """ Return a formatter name as used in the caching system
01236 
01237         @rtype: string
01238         @return: formatter name as used in caching
01239         """
01240         if not hasattr(self, 'formatter') or self.formatter is None:
01241             return ''
01242         module = self.formatter.__module__
01243         return module[module.rfind('.') + 1:]

Here is the caller graph for this function:

def MoinMoin.Page.Page.getlines (   self) [inherited]
Return a list of all lines in body.

@rtype: list
@return: list of strs body_lines

Definition at line 257 of file Page.py.

00257 
00258     def getlines(self):
00259         """ Return a list of all lines in body.
00260 
00261         @rtype: list
00262         @return: list of strs body_lines
00263         """
00264         return self.body.split('\n')

def MoinMoin.Page.Page.getPageBasePath (   self,
  use_underlay = -1 
) [inherited]
Get full path to a page-specific storage area. `args` can
    contain additional path components that are added to the base path.

@param use_underlay: force using a specific pagedir, default -1
                -1 = automatically choose page dir
                1 = use underlay page dir
                0 = use standard page dir
@rtype: string
@return: int underlay,
 str the full path to the storage area

Reimplemented in MoinMoin.Page.RootPage.

Definition at line 432 of file Page.py.

00432 
00433     def getPageBasePath(self, use_underlay=-1):
00434         """ Get full path to a page-specific storage area. `args` can
00435             contain additional path components that are added to the base path.
00436 
00437         @param use_underlay: force using a specific pagedir, default -1
00438                                 -1 = automatically choose page dir
00439                                 1 = use underlay page dir
00440                                 0 = use standard page dir
00441         @rtype: string
00442         @return: int underlay,
00443                  str the full path to the storage area
00444         """
00445         standardpath, underlaypath = self._pagepath
00446         if underlaypath is None:
00447             use_underlay = 0
00448 
00449         if use_underlay == -1: # automatic
00450             if self._underlay is None:
00451                 underlay, path = 0, standardpath
00452                 pagefile, rev, exists = self.get_rev(use_underlay=0)
00453                 if not exists:
00454                     pagefile, rev, exists = self.get_rev(use_underlay=1)
00455                     if exists:
00456                         underlay, path = 1, underlaypath
00457                 self._underlay = underlay
00458             else:
00459                 underlay = self._underlay
00460                 path = self._pagepath[underlay]
00461         else: # normal or underlay
00462             underlay, path = use_underlay, self._pagepath[use_underlay]
00463 
00464         return underlay, path

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.getPageHeader (   self,
  start = 0,
  length = None 
) [inherited]
Convenience function to get the page header

@rtype: unicode
@return: page header

Definition at line 1441 of file Page.py.

01441 
01442     def getPageHeader(self, start=0, length=None):
01443         """ Convenience function to get the page header
01444 
01445         @rtype: unicode
01446         @return: page header
01447         """
01448         header = ['#%s %s' % t for t in self.meta]
01449         header = '\n'.join(header)
01450         if header:
01451             if length is None:
01452                 return header[start:]
01453             else:
01454                 return header[start:start+length]
01455         return ''

def MoinMoin.Page.Page.getPageLinks (   self,
  request 
) [inherited]
Get a list of the links on this page.

@param request: the request object
@rtype: list
@return: page names this page links to

Definition at line 1456 of file Page.py.

01456 
01457     def getPageLinks(self, request):
01458         """ Get a list of the links on this page.
01459 
01460         @param request: the request object
01461         @rtype: list
01462         @return: page names this page links to
01463         """
01464         if self.exists():
01465             cache = caching.CacheEntry(request, self, 'pagelinks', scope='item', do_locking=False, use_pickle=True)
01466             if cache.needsUpdate(self._text_filename()):
01467                 links = self.parsePageLinks(request)
01468                 cache.update(links)
01469             else:
01470                 try:
01471                     links = cache.content()
01472                 except caching.CacheError:
01473                     links = self.parsePageLinks(request)
01474                     cache.update(links)
01475         else:
01476             links = []
01477         return links

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.getPagePath (   self,
  args,
  kw 
) [inherited]
Return path to the page storage area. 

Definition at line 499 of file Page.py.

00499 
00500     def getPagePath(self, *args, **kw):
00501         """ Return path to the page storage area. """
00502         return self.getPageStatus(*args, **kw)[1]

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.getPageStatus (   self,
  args,
  kw 
) [inherited]
Get full path to a page-specific storage area. `args` can
    contain additional path components that are added to the base path.

@param args: additional path components
@keyword use_underlay: force using a specific pagedir, default '-1'
                -1 = automatically choose page dir
                1 = use underlay page dir
                0 = use standard page dir
@keyword check_create: if true, ensures that the path requested really exists
               (if it doesn't, create all directories automatically).
               (default true)
@keyword isfile: is the last component in args a filename? (default is false)
@rtype: string
@return: (int underlay (1 if using underlay, 0 otherwise),
  str the full path to the storage area )

Definition at line 465 of file Page.py.

00465 
00466     def getPageStatus(self, *args, **kw):
00467         """ Get full path to a page-specific storage area. `args` can
00468             contain additional path components that are added to the base path.
00469 
00470         @param args: additional path components
00471         @keyword use_underlay: force using a specific pagedir, default '-1'
00472                                 -1 = automatically choose page dir
00473                                 1 = use underlay page dir
00474                                 0 = use standard page dir
00475         @keyword check_create: if true, ensures that the path requested really exists
00476                                (if it doesn't, create all directories automatically).
00477                                (default true)
00478         @keyword isfile: is the last component in args a filename? (default is false)
00479         @rtype: string
00480         @return: (int underlay (1 if using underlay, 0 otherwise),
00481                   str the full path to the storage area )
00482         """
00483         check_create = kw.get('check_create', 1)
00484         isfile = kw.get('isfile', 0)
00485         use_underlay = kw.get('use_underlay', -1)
00486         underlay, path = self.getPageBasePath(use_underlay)
00487         fullpath = os.path.join(*((path, ) + args))
00488         if check_create:
00489             if isfile:
00490                 dirname, filename = os.path.split(fullpath)
00491             else:
00492                 dirname = fullpath
00493             try:
00494                 os.makedirs(dirname)
00495             except OSError, err:
00496                 if not os.path.exists(dirname):
00497                     raise err
00498         return underlay, fullpath

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.getPageText (   self,
  start = 0,
  length = None 
) [inherited]
Convenience function to get the page text, skipping the header

@rtype: unicode
@return: page text, excluding the header

Definition at line 1430 of file Page.py.

01430 
01431     def getPageText(self, start=0, length=None):
01432         """ Convenience function to get the page text, skipping the header
01433 
01434         @rtype: unicode
01435         @return: page text, excluding the header
01436         """
01437         if length is None:
01438             return self.data[start:]
01439         else:
01440             return self.data[start:start+length]

def MoinMoin.Page.Page.getParentPage (   self) [inherited]
Return parent page or None

@rtype: Page
@return: parent page or None

Definition at line 1532 of file Page.py.

01532 
01533     def getParentPage(self):
01534         """ Return parent page or None
01535 
01536         @rtype: Page
01537         @return: parent page or None
01538         """
01539         if self.page_name:
01540             pos = self.page_name.rfind('/')
01541             if pos > 0:
01542                 parent = Page(self.request, self.page_name[:pos])
01543                 if parent.exists():
01544                     return parent
01545         return None

def MoinMoin.Page.Page.getRevList (   self) [inherited]
Get a page revision list of this page, including the current version,
sorted by revision number in descending order (current page first).

@rtype: list of ints
@return: page revisions

Definition at line 1395 of file Page.py.

01395 
01396     def getRevList(self):
01397         """ Get a page revision list of this page, including the current version,
01398         sorted by revision number in descending order (current page first).
01399 
01400         @rtype: list of ints
01401         @return: page revisions
01402         """
01403         revisions = []
01404         if self.page_name:
01405             rev_dir = self.getPagePath('revisions', check_create=0)
01406             if os.path.isdir(rev_dir):
01407                 for rev in filesys.dclistdir(rev_dir):
01408                     try:
01409                         revint = int(rev)
01410                         revisions.append(revint)
01411                     except ValueError:
01412                         pass
01413                 revisions.sort()
01414                 revisions.reverse()
01415         return revisions

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.getSubscribers (   self,
  request,
  kw 
) [inherited]
Get all subscribers of this page.

@param request: the request object
@keyword include_self: if 1, include current user (default: 0)
@keyword return_users: if 1, return user instances (default: 0)
@rtype: dict
@return: lists of subscribed email addresses in a dict by language key

Definition at line 815 of file Page.py.

00815 
00816     def getSubscribers(self, request, **kw):
00817         """ Get all subscribers of this page.
00818 
00819         @param request: the request object
00820         @keyword include_self: if 1, include current user (default: 0)
00821         @keyword return_users: if 1, return user instances (default: 0)
00822         @rtype: dict
00823         @return: lists of subscribed email addresses in a dict by language key
00824         """
00825         include_self = kw.get('include_self', self.include_self)
00826         return_users = kw.get('return_users', 0)
00827 
00828         # extract categories of this page
00829         pageList = self.getCategories(request)
00830 
00831         # add current page name for list matching
00832         pageList.append(self.page_name)
00833 
00834         if self.cfg.SecurityPolicy:
00835             UserPerms = self.cfg.SecurityPolicy
00836         else:
00837             from MoinMoin.security import Default as UserPerms
00838 
00839         # get email addresses of the all wiki user which have a profile stored;
00840         # add the address only if the user has subscribed to the page and
00841         # the user is not the current editor
00842         userlist = user.getUserList(request)
00843         subscriber_list = {}
00844         for uid in userlist:
00845             if uid == request.user.id and not include_self:
00846                 continue # no self notification
00847             subscriber = user.User(request, uid)
00848 
00849             # The following tests should be ordered in order of
00850             # decreasing computation complexity, in particular
00851             # the permissions check may be expensive; see the bug
00852             # MoinMoinBugs/GetSubscribersPerformanceProblem
00853 
00854             # This is a bit wrong if return_users=1 (which implies that the caller will process
00855             # user attributes and may, for example choose to send an SMS)
00856             # So it _should_ be "not (subscriber.email and return_users)" but that breaks at the moment.
00857             if not subscriber.email:
00858                 continue # skip empty email addresses
00859 
00860             # skip people not subscribed
00861             if not subscriber.isSubscribedTo(pageList):
00862                 continue
00863 
00864             # skip people who can't read the page
00865             if not UserPerms(subscriber).read(self.page_name):
00866                 continue
00867 
00868             # add the user to the list
00869             lang = subscriber.language or request.cfg.language_default
00870             if not lang in subscriber_list:
00871                 subscriber_list[lang] = []
00872             if return_users:
00873                 subscriber_list[lang].append(subscriber)
00874             else:
00875                 subscriber_list[lang].append(subscriber.email)
00876 
00877         return subscriber_list

Here is the call graph for this function:

def MoinMoin.Page.Page.isConflict (   self) [inherited]
Returns true if there is a known editing conflict for that page.

@return: true if there is a known conflict.

Definition at line 1632 of file Page.py.

01632 
01633     def isConflict(self):
01634         """ Returns true if there is a known editing conflict for that page.
01635 
01636         @return: true if there is a known conflict.
01637         """
01638 
01639         cache = caching.CacheEntry(self.request, self, 'conflict', scope='item')
01640         return cache.exists()

def MoinMoin.Page.Page.isStandardPage (   self,
  includeDeleted = True 
) [inherited]
Does this page live in the data dir?

Return true even if this is a copy of an underlay page. To check
for data only page, use isStandardPage() and not isUnderlayPage().

@param includeDeleted: include deleted pages
@rtype: bool
@return: true if page lives in the data dir

Definition at line 617 of file Page.py.

00617 
00618     def isStandardPage(self, includeDeleted=True):
00619         """ Does this page live in the data dir?
00620 
00621         Return true even if this is a copy of an underlay page. To check
00622         for data only page, use isStandardPage() and not isUnderlayPage().
00623 
00624         @param includeDeleted: include deleted pages
00625         @rtype: bool
00626         @return: true if page lives in the data dir
00627         """
00628         return self.exists(domain='standard', includeDeleted=includeDeleted)

Here is the call graph for this function:

def MoinMoin.Page.Page.isUnderlayPage (   self,
  includeDeleted = True 
) [inherited]
Does this page live in the underlay dir?

Return true even if the data dir has a copy of this page. To
check for underlay only page, use ifUnderlayPage() and not
isStandardPage()

@param includeDeleted: include deleted pages
@rtype: bool
@return: true if page lives in the underlay dir

Definition at line 604 of file Page.py.

00604 
00605     def isUnderlayPage(self, includeDeleted=True):
00606         """ Does this page live in the underlay dir?
00607 
00608         Return true even if the data dir has a copy of this page. To
00609         check for underlay only page, use ifUnderlayPage() and not
00610         isStandardPage()
00611 
00612         @param includeDeleted: include deleted pages
00613         @rtype: bool
00614         @return: true if page lives in the underlay dir
00615         """
00616         return self.exists(domain='underlay', includeDeleted=includeDeleted)

Here is the call graph for this function:

def MoinMoin.Page.Page.isWritable (   self) [inherited]
Can this page be changed?

@rtype: bool
@return: true, if this page is writable or does not exist

Definition at line 596 of file Page.py.

00596 
00597     def isWritable(self):
00598         """ Can this page be changed?
00599 
00600         @rtype: bool
00601         @return: true, if this page is writable or does not exist
00602         """
00603         return os.access(self._text_filename(), os.W_OK) or not self.exists()

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.last_edit (   self,
  request 
) [inherited]

Definition at line 568 of file Page.py.

00568 
00569     def last_edit(self, request):
00570         # XXX usage of last_edit is DEPRECATED - use edit_info()
00571         if not self.exists(): # XXX doesn't make much sense, but still kept
00572             return None       # XXX here until we remove last_edit()
00573         return self.edit_info()

Here is the call graph for this function:

def MoinMoin.Page.Page.lastEditInfo (   self,
  request = None 
) [inherited]
Return the last edit info.

    Note: if you ask about a deleted revision, it will report timestamp and editor
  for the delete action (in the edit-log, this is just a SAVE).

@param request: the request object (DEPRECATED, unused)
@rtype: dict
@return: timestamp and editor information

Definition at line 574 of file Page.py.

00574 
00575     def lastEditInfo(self, request=None):
00576         """ Return the last edit info.
00577 
00578             Note: if you ask about a deleted revision, it will report timestamp and editor
00579                   for the delete action (in the edit-log, this is just a SAVE).
00580 
00581         @param request: the request object (DEPRECATED, unused)
00582         @rtype: dict
00583         @return: timestamp and editor information
00584         """
00585         log = self.editlog_entry()
00586         if log:
00587             request = self.request
00588             editor = log.getEditor(request)
00589             time = wikiutil.version2timestamp(log.ed_time_usecs)
00590             time = request.user.getFormattedDateTime(time) # Use user time format
00591             result = {'editor': editor, 'time': time}
00592             del log
00593         else:
00594             result = {}
00595         return result

Here is the call graph for this function:

def MoinMoin.Page.Page.link_to (   self,
  request,
  text = None,
  querystr = None,
  anchor = None,
  kw 
) [inherited]
Return HTML markup that links to this page.

See wikiutil.link_tag() for possible keyword parameters.

@param request: the request object
@param text: inner text of the link - it gets automatically escaped
@param querystr: the query string to add after a "?" after the url
@param anchor: if specified, make a link to this anchor
@keyword on: opening/closing tag only
@keyword attachment_indicator: if 1, add attachment indicator after link tag
@keyword css_class: css class to use
@rtype: string
@return: formatted link

Definition at line 777 of file Page.py.

00777 
00778     def link_to(self, request, text=None, querystr=None, anchor=None, **kw):
00779         """ Return HTML markup that links to this page.
00780 
00781         See wikiutil.link_tag() for possible keyword parameters.
00782 
00783         @param request: the request object
00784         @param text: inner text of the link - it gets automatically escaped
00785         @param querystr: the query string to add after a "?" after the url
00786         @param anchor: if specified, make a link to this anchor
00787         @keyword on: opening/closing tag only
00788         @keyword attachment_indicator: if 1, add attachment indicator after link tag
00789         @keyword css_class: css class to use
00790         @rtype: string
00791         @return: formatted link
00792         """
00793         if not text:
00794             text = self.split_title()
00795         text = wikiutil.escape(text)
00796 
00797         # Add css class for non existing page
00798         if not self.exists():
00799             kw['css_class'] = 'nonexistent'
00800 
00801         attachment_indicator = kw.get('attachment_indicator')
00802         if attachment_indicator is None:
00803             attachment_indicator = 0 # default is off
00804         else:
00805             del kw['attachment_indicator'] # avoid having this as <a> tag attribute
00806 
00807         link = self.link_to_raw(request, text, querystr, anchor, **kw)
00808 
00809         # Create a link to attachments if any exist
00810         if attachment_indicator:
00811             from MoinMoin.action import AttachFile
00812             link += AttachFile.getIndicator(request, self.page_name)
00813 
00814         return link

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.link_to_raw (   self,
  request,
  text,
  querystr = None,
  anchor = None,
  kw 
) [inherited]
core functionality of link_to, without the magic 

Definition at line 769 of file Page.py.

00769 
00770     def link_to_raw(self, request, text, querystr=None, anchor=None, **kw):
00771         """ core functionality of link_to, without the magic """
00772         url = self.url(request, querystr, anchor=anchor, relative=True) # scriptName is added by link_tag
00773         # escaping is done by link_tag -> formatter.url -> ._open()
00774         link = wikiutil.link_tag(request, url, text,
00775                                  formatter=getattr(self, 'formatter', None), **kw)
00776         return link

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.loadCache (   self,
  request 
) [inherited]
Return page content cache or raises 'CacheNeedsUpdate' 

Definition at line 1322 of file Page.py.

01322 
01323     def loadCache(self, request):
01324         """ Return page content cache or raises 'CacheNeedsUpdate' """
01325         cache = caching.CacheEntry(request, self, self.getFormatterName(), scope='item')
01326         attachmentsPath = self.getPagePath('attachments', check_create=0)
01327         if cache.needsUpdate(self._text_filename(), attachmentsPath):
01328             raise Exception('CacheNeedsUpdate')
01329 
01330         import marshal
01331         try:
01332             return marshal.loads(cache.content())
01333         except (EOFError, ValueError, TypeError):
01334             # Bad marshal data, must update the cache.
01335             # See http://docs.python.org/lib/module-marshal.html
01336             raise Exception('CacheNeedsUpdate')
01337         except Exception, err:
01338             logging.info('failed to load "%s" cache: %s' %
01339                         (self.page_name, str(err)))
01340             raise Exception('CacheNeedsUpdate')

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.makeCache (   self,
  request,
  parser 
) [inherited]
Format content into code, update cache and return code 

Definition at line 1341 of file Page.py.

01341 
01342     def makeCache(self, request, parser):
01343         """ Format content into code, update cache and return code """
01344         import marshal
01345         from MoinMoin.formatter.text_python import Formatter
01346         formatter = Formatter(request, ["page"], self.formatter)
01347 
01348         # Save request state while formatting page
01349         saved_current_lang = request.current_lang
01350         try:
01351             text = request.redirectedOutput(parser.format, formatter)
01352         finally:
01353             request.current_lang = saved_current_lang
01354 
01355         src = formatter.assemble_code(text)
01356         code = compile(src.encode(config.charset),
01357                        self.page_name.encode(config.charset), 'exec')
01358         cache = caching.CacheEntry(request, self, self.getFormatterName(), scope='item')
01359         cache.update(marshal.dumps(code))
01360         return code

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.PageEditor.PageEditor.mergeEditConflict (   self,
  origrev 
)
Try to merge current page version with new version the user tried to save

@param origrev: the original revision the user was editing
@rtype: bool
@return: merge success status

Definition at line 101 of file PageEditor.py.

00101 
00102     def mergeEditConflict(self, origrev):
00103         """ Try to merge current page version with new version the user tried to save
00104 
00105         @param origrev: the original revision the user was editing
00106         @rtype: bool
00107         @return: merge success status
00108         """
00109         from MoinMoin.util import diff3
00110         allow_conflicts = 1
00111 
00112         # Get current editor text
00113         savetext = self.get_raw_body()
00114 
00115         # The original text from the revision the user was editing
00116         original_text = Page(self.request, self.page_name, rev=origrev).get_raw_body()
00117 
00118         # The current revision someone else saved
00119         saved_text = Page(self.request, self.page_name).get_raw_body()
00120 
00121         # And try to merge all into one with edit conflict separators
00122         verynewtext = diff3.text_merge(original_text, saved_text, savetext,
00123                                        allow_conflicts, *conflict_markers)
00124         if verynewtext:
00125             self.set_raw_body(verynewtext)
00126             return True
00127 
00128         # this should never happen, except for empty pages
00129         return False

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.mtime_printable (   self,
  request 
) [inherited]
Get printable (as per user's preferences) modification timestamp of this page.

@rtype: string
@return: formatted string with mtime of page

Definition at line 697 of file Page.py.

00697 
00698     def mtime_printable(self, request):
00699         """ Get printable (as per user's preferences) modification timestamp of this page.
00700 
00701         @rtype: string
00702         @return: formatted string with mtime of page
00703         """
00704         t = self.mtime_usecs()
00705         if not t:
00706             result = "0" # TODO: i18n, "Ever", "Beginning of time"...?
00707         else:
00708             result = request.user.getFormattedDateTime(
00709                 wikiutil.version2timestamp(t))
00710         return result

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.mtime_usecs (   self) [inherited]
Get modification timestamp of this page (from edit-log, can be for an old revision).

@rtype: int
@return: mtime of page (or 0 if page / edit-log entry does not exist)

Definition at line 688 of file Page.py.

00688 
00689     def mtime_usecs(self):
00690         """ Get modification timestamp of this page (from edit-log, can be for an old revision).
00691 
00692         @rtype: int
00693         @return: mtime of page (or 0 if page / edit-log entry does not exist)
00694         """
00695         entry = self.editlog_entry()
00696         return entry and entry.ed_time_usecs or 0

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.PageEditor.PageEditor.normalizeText (   self,
  text,
  kw 
)
Normalize text

Make sure text uses '\n' line endings, and has a trailing
newline. Strip whitespace on end of lines if needed.

You should normalize any text you enter into a page, for
example, when getting new text from the editor, or when setting
new text manually.

@param text: text to normalize (unicode)
@keyword stripspaces: if 1, strip spaces from text
@rtype: unicode
@return: normalized text

Definition at line 792 of file PageEditor.py.

00792 
00793     def normalizeText(self, text, **kw):
00794         """ Normalize text
00795 
00796         Make sure text uses '\n' line endings, and has a trailing
00797         newline. Strip whitespace on end of lines if needed.
00798 
00799         You should normalize any text you enter into a page, for
00800         example, when getting new text from the editor, or when setting
00801         new text manually.
00802 
00803         @param text: text to normalize (unicode)
00804         @keyword stripspaces: if 1, strip spaces from text
00805         @rtype: unicode
00806         @return: normalized text
00807         """
00808         if text:
00809             lines = text.splitlines()
00810             # Strip trailing spaces if needed
00811             if kw.get('stripspaces', 0):
00812                 lines = [line.rstrip() for line in lines]
00813             # Add final newline if not present, better for diffs (does
00814             # not include former last line when just adding text to
00815             # bottom; idea by CliffordAdams)
00816             if not lines[-1] == u'':
00817                 # '' will make newline after join
00818                 lines.append(u'')
00819 
00820             text = u'\n'.join(lines)
00821         return text

def MoinMoin.Page.Page.olderrevision (   self,
  rev = 0 
) [inherited]
Get revision of the next older page revision than rev.
rev == 0 means this page objects revision (that may be an old
revision already!)

Definition at line 1416 of file Page.py.

01416 
01417     def olderrevision(self, rev=0):
01418         """ Get revision of the next older page revision than rev.
01419         rev == 0 means this page objects revision (that may be an old
01420         revision already!)
01421         """
01422         if rev == 0:
01423             rev = self.rev
01424         revisions = self.getRevList()
01425         for r in revisions:
01426             if r < rev:
01427                 older = r
01428                 break
01429         return older

Here is the call graph for this function:

Parse page text and extract processing instructions,
    return a dict of PIs and the non-PI rest of the body.

Definition at line 878 of file Page.py.

00878 
00879     def parse_processing_instructions(self):
00880         """ Parse page text and extract processing instructions,
00881             return a dict of PIs and the non-PI rest of the body.
00882         """
00883         from MoinMoin import i18n
00884         from MoinMoin import security
00885         request = self.request
00886         pi = {} # we collect the processing instructions here
00887 
00888         # default language from cfg
00889         pi['language'] = self.cfg.language_default or "en"
00890 
00891         body = self.body
00892         # TODO: remove this hack once we have separate metadata and can use mimetype there
00893         if body.startswith('<?xml'): # check for XML content
00894             pi['lines'] = 0
00895             pi['format'] = "xslt"
00896             pi['formatargs'] = ''
00897             pi['acl'] = security.AccessControlList(request.cfg, []) # avoid KeyError on acl check
00898             return pi
00899 
00900         meta = self.meta
00901 
00902         # default is wiki markup
00903         pi['format'] = self.cfg.default_markup or "wiki"
00904         pi['formatargs'] = ''
00905         pi['lines'] = len(meta)
00906         acl = []
00907 
00908         for verb, args in meta:
00909             if verb == "format": # markup format
00910                 format, formatargs = (args + ' ').split(' ', 1)
00911                 pi['format'] = format.lower()
00912                 pi['formatargs'] = formatargs.strip()
00913 
00914             elif verb == "acl":
00915                 acl.append(args)
00916 
00917             elif verb == "language":
00918                 # Page language. Check if args is a known moin language
00919                 if args in i18n.wikiLanguages():
00920                     pi['language'] = args
00921 
00922             elif verb == "refresh":
00923                 if self.cfg.refresh:
00924                     try:
00925                         mindelay, targetallowed = self.cfg.refresh
00926                         args = args.split()
00927                         if len(args) >= 1:
00928                             delay = max(int(args[0]), mindelay)
00929                         if len(args) >= 2:
00930                             target = args[1]
00931                         else:
00932                             target = self.page_name
00933                         if '://' in target:
00934                             if targetallowed == 'internal':
00935                                 raise ValueError
00936                             elif targetallowed == 'external':
00937                                 url = target
00938                         else:
00939                             url = Page(request, target).url(request)
00940                         pi['refresh'] = (delay, url)
00941                     except (ValueError, ):
00942                         pass
00943 
00944             elif verb == "redirect":
00945                 pi['redirect'] = args
00946 
00947             elif verb == "deprecated":
00948                 pi['deprecated'] = True
00949 
00950             elif verb == "openiduser":
00951                 if request.cfg.openid_server_enable_user:
00952                     pi['openid.user'] = args
00953 
00954             elif verb == "pragma":
00955                 try:
00956                     key, val = args.split(' ', 1)
00957                 except (ValueError, TypeError):
00958                     pass
00959                 else:
00960                     request.setPragma(key, val)
00961 
00962         pi['acl'] = security.AccessControlList(request.cfg, acl)
00963         return pi

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.parseACL (   self) [inherited]
Return ACLs parsed from the last available revision

The effective ACL is always from the last revision, even if
you access an older revision.

Definition at line 1585 of file Page.py.

01585 
01586     def parseACL(self):
01587         """ Return ACLs parsed from the last available revision
01588 
01589         The effective ACL is always from the last revision, even if
01590         you access an older revision.
01591         """
01592         from MoinMoin import security
01593         if self.exists() and self.rev == 0:
01594             return self.pi['acl']
01595         try:
01596             lastRevision = self.getRevList()[0]
01597         except IndexError:
01598             return security.AccessControlList(self.request.cfg)
01599         if self.rev == lastRevision:
01600             return self.pi['acl']
01601 
01602         return Page(self.request, self.page_name, rev=lastRevision).parseACL()

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.parsePageLinks (   self,
  request 
) [inherited]
Parse page links by formatting with a pagelinks formatter

This is a old hack to get the pagelinks by rendering the page
with send_page. We can remove this hack after factoring
send_page and send_page_content into small reuseable methods.

More efficient now by using special pagelinks formatter and
redirecting possible output into null file.

Definition at line 1478 of file Page.py.

01478 
01479     def parsePageLinks(self, request):
01480         """ Parse page links by formatting with a pagelinks formatter
01481 
01482         This is a old hack to get the pagelinks by rendering the page
01483         with send_page. We can remove this hack after factoring
01484         send_page and send_page_content into small reuseable methods.
01485 
01486         More efficient now by using special pagelinks formatter and
01487         redirecting possible output into null file.
01488         """
01489         pagename = self.page_name
01490         if request.parsePageLinks_running.get(pagename, False):
01491             #logging.debug("avoid recursion for page %r" % pagename)
01492             return [] # avoid recursion
01493 
01494         #logging.debug("running parsePageLinks for page %r" % pagename)
01495         # remember we are already running this function for this page:
01496         request.parsePageLinks_running[pagename] = True
01497 
01498         request.clock.start('parsePageLinks')
01499 
01500         class Null:
01501             def write(self, data):
01502                 pass
01503 
01504         request.redirect(Null())
01505         request.mode_getpagelinks += 1
01506         #logging.debug("mode_getpagelinks == %r" % request.mode_getpagelinks)
01507         try:
01508             try:
01509                 from MoinMoin.formatter.pagelinks import Formatter
01510                 formatter = Formatter(request, store_pagelinks=1)
01511                 page = Page(request, pagename, formatter=formatter)
01512                 page.send_page(content_only=1)
01513             except:
01514                 logging.exception("pagelinks formatter failed, traceback follows")
01515         finally:
01516             request.mode_getpagelinks -= 1
01517             #logging.debug("mode_getpagelinks == %r" % request.mode_getpagelinks)
01518             request.redirect()
01519             if hasattr(request, '_fmt_hd_counters'):
01520                 del request._fmt_hd_counters
01521             request.clock.stop('parsePageLinks')
01522         return formatter.pagelinks

Here is the caller graph for this function:

def MoinMoin.PageEditor.PageEditor.renamePage (   self,
  newpagename,
  comment = u'' 
)
Rename the current version of the page (making a backup before deletion
    and keeping the backups, logs and attachments).

@param comment: Comment given by user
@rtype: unicode
@return: success flag, error message

Definition at line 576 of file PageEditor.py.

00576 
00577     def renamePage(self, newpagename, comment=u''):
00578         """ Rename the current version of the page (making a backup before deletion
00579             and keeping the backups, logs and attachments).
00580 
00581         @param comment: Comment given by user
00582         @rtype: unicode
00583         @return: success flag, error message
00584         """
00585         request = self.request
00586         _ = self._
00587 
00588         if not (request.user.may.delete(self.page_name)
00589                 and request.user.may.write(newpagename)):
00590             msg = _('You are not allowed to rename this page!')
00591             raise self.AccessDenied, msg
00592 
00593         if not newpagename:
00594             return False, _("You can't rename to an empty pagename.")
00595 
00596         newpage = PageEditor(request, newpagename)
00597 
00598         pageexists_error = _("""'''A page with the name {{{'%s'}}} already exists.'''
00599 
00600 Try a different name.""", wiki=True) % (wikiutil.escape(newpagename), )
00601 
00602         # Check whether a page with the new name already exists
00603         if newpage.exists(includeDeleted=1):
00604             return False, pageexists_error
00605 
00606         # Get old page text
00607         savetext = self.get_raw_body()
00608 
00609         oldpath = self.getPagePath(check_create=0)
00610         newpath = newpage.getPagePath(check_create=0)
00611 
00612         # Rename page
00613 
00614         # NOTE: might fail if another process created newpagename just
00615         # NOW, while you read this comment. Rename is atomic for files -
00616         # but for directories, rename will fail if the directory
00617         # exists. We should have global edit-lock to avoid this.
00618         # See http://docs.python.org/lib/os-file-dir.html
00619         try:
00620             os.rename(oldpath, newpath)
00621             self.error = None
00622             # Save page text with a comment about the old name
00623             savetext = u"## page was renamed from %s\n%s" % (self.page_name, savetext)
00624             newpage.saveText(savetext, 0, comment=comment, extra=self.page_name, action='SAVE/RENAME', notify=False)
00625             # delete pagelinks
00626             arena = newpage
00627             key = 'pagelinks'
00628             cache = caching.CacheEntry(request, arena, key, scope='item')
00629             cache.remove()
00630 
00631             # clean the cache
00632             for formatter_name in self.cfg.caching_formats:
00633                 arena = newpage
00634                 key = formatter_name
00635                 cache = caching.CacheEntry(request, arena, key, scope='item')
00636                 cache.remove()
00637 
00638             event = PageRenamedEvent(request, newpage, self, comment)
00639             send_event(event)
00640 
00641             return True, None
00642         except OSError, err:
00643             # Try to understand what happened. Maybe its better to check
00644             # the error code, but I just reused the available code above...
00645             if newpage.exists(includeDeleted=1):
00646                 return False, pageexists_error
00647             else:
00648                 return False, _('Could not rename page because of file system error: %s.') % unicode(err)
00649 

Here is the call graph for this function:

def MoinMoin.Page.Page.reset (   self) [inherited]
Reset page state 

Definition at line 182 of file Page.py.

00182 
00183     def reset(self):
00184         """ Reset page state """
00185         page_name = self.page_name
00186         # page_name quoted for file system usage, needs to be reset to
00187         # None when pagename changes
00188 
00189         qpagename = wikiutil.quoteWikinameFS(page_name)
00190         self.page_name_fs = qpagename
00191 
00192         # the normal and the underlay path used for this page
00193         normalpath = os.path.join(self.cfg.data_dir, "pages", qpagename)
00194         if not self.cfg.data_underlay_dir is None:
00195             underlaypath = os.path.join(self.cfg.data_underlay_dir, "pages", qpagename)
00196         else:
00197             underlaypath = None
00198 
00199         # TUNING - remember some essential values
00200 
00201         # does the page come from normal page storage (0) or from
00202         # underlay dir (1) (can be used as index into following list)
00203         self._underlay = None
00204 
00205         # path to normal / underlay page dir
00206         self._pagepath = [normalpath, underlaypath]

Here is the caller graph for this function:

def MoinMoin.PageEditor.PageEditor.revertPage (   self,
  revision,
  comment = u'' 
)
Reverts page to the given revision

@param revision: revision to revert to
@type revision: int

Definition at line 650 of file PageEditor.py.

00650 
00651     def revertPage(self, revision, comment=u''):
00652         """ Reverts page to the given revision
00653 
00654         @param revision: revision to revert to
00655         @type revision: int
00656 
00657         """
00658         _ = self.request.getText
00659 
00660         if not self.request.user.may.revert(self.page_name):
00661             # no real message necessary, cannot happen if
00662             # user doesn't try to exploit us
00663             raise self.RevertError('not allowed')
00664         elif revision is None:
00665             # see above
00666             raise self.RevertError('cannot revert to current rev')
00667         else:
00668             revstr = '%08d' % revision
00669             pg = Page(self.request, self.page_name, rev=revision)
00670             msg = self.saveText(pg.get_raw_body(), 0, extra=revstr, action="SAVE/REVERT", notify=False, comment=comment)
00671 
00672             # Remove cache entry (if exists)
00673             pg = Page(self.request, self.page_name)
00674             key = self.request.form.get('key', 'text_html') # XXX see cleanup code in deletePage
00675             caching.CacheEntry(self.request, pg, key, scope='item').remove()
00676             caching.CacheEntry(self.request, pg, "pagelinks", scope='item').remove()
00677 
00678             # Notify observers
00679             e = PageRevertedEvent(self.request, self.page_name, revision, revstr)
00680             send_event(e)
00681 
00682             return msg

Here is the call graph for this function:

def MoinMoin.PageEditor.PageEditor.saveText (   self,
  newtext,
  rev,
  kw 
)
Save new text for a page.

@param newtext: text to save for this page
@param rev: revision of the page
@keyword trivial: trivial edit (default: 0)
@keyword extra: extra info field (e.g. for SAVE/REVERT with revno)
@keyword comment: comment field (when preview is true)
@keyword action: action for editlog (default: SAVE)
@keyword index: needs indexing, not already handled (default: 1)
@keyword deleted: if True, then don't save page content (used by DeletePage, default: False)
@keyword notify: if False (default: True), don't send a PageChangedEvent
@rtype: unicode
@return: error msg

Definition at line 1021 of file PageEditor.py.

01021 
01022     def saveText(self, newtext, rev, **kw):
01023         """ Save new text for a page.
01024 
01025         @param newtext: text to save for this page
01026         @param rev: revision of the page
01027         @keyword trivial: trivial edit (default: 0)
01028         @keyword extra: extra info field (e.g. for SAVE/REVERT with revno)
01029         @keyword comment: comment field (when preview is true)
01030         @keyword action: action for editlog (default: SAVE)
01031         @keyword index: needs indexing, not already handled (default: 1)
01032         @keyword deleted: if True, then don't save page content (used by DeletePage, default: False)
01033         @keyword notify: if False (default: True), don't send a PageChangedEvent
01034         @rtype: unicode
01035         @return: error msg
01036         """
01037         request = self.request
01038         _ = self._
01039         self._save_draft(newtext, rev, **kw)
01040         action = kw.get('action', 'SAVE')
01041         deleted = kw.get('deleted', False)
01042         notify = kw.get('notify', True)
01043 
01044         #!!! need to check if we still retain the lock here
01045         #!!! rev check is not enough since internal operations use "0"
01046 
01047         # expand variables, unless it's a template or form page
01048         if not wikiutil.isTemplatePage(request, self.page_name):
01049             newtext = self._expand_variables(newtext)
01050 
01051         msg = ""
01052         if not request.user.may.save(self, newtext, rev, **kw):
01053             msg = _('You are not allowed to edit this page!')
01054             raise self.AccessDenied, msg
01055         elif not self.isWritable():
01056             msg = _('Page is immutable!')
01057             raise self.Immutable, msg
01058         elif not newtext:
01059             msg = _('You cannot save empty pages.')
01060             raise self.EmptyPage, msg
01061         elif rev != 0 and rev != self.current_rev():
01062             # check if we already saved that page
01063             other = False
01064             pagelog = self.getPagePath('edit-log', use_underlay=0, isfile=1)
01065             next_line = None
01066             for line in editlog.EditLog(request, pagelog).reverse():
01067                 if int(line.rev) == int(rev):
01068                     break
01069                 if not line.is_from_current_user(request):
01070                     other = True
01071                 next_line = line
01072             if next_line and next_line.is_from_current_user(request):
01073                 saved_page = Page(request, self.page_name, rev=int(next_line.rev))
01074                 if newtext == saved_page.get_raw_body():
01075                     msg = _("You already saved this page!")
01076                     return msg
01077                 else:
01078                     msg = _("You already edited this page! Please do not use the back button.")
01079                     raise self.EditConflict, msg
01080 
01081                 msg = _("""Someone else saved this page while you were editing!
01082 Please review the page and save then. Do not save this page as it is!""")
01083 
01084             raise self.EditConflict, msg
01085         elif newtext == self.get_raw_body():
01086             msg = _('You did not change the page content, not saved!')
01087             self.lock.release()
01088             raise self.Unchanged, msg
01089         else:
01090             from MoinMoin.security import parseACL
01091             # Get current ACL and compare to new ACL from newtext. If
01092             # they are not the sames, the user must have admin
01093             # rights. This is a good place to update acl cache - instead
01094             # of wating for next request.
01095             acl = self.getACL(request)
01096             if (not request.user.may.admin(self.page_name) and
01097                 parseACL(request, newtext).acl != acl.acl and
01098                 action != "SAVE/REVERT"):
01099                 msg = _("You can't change ACLs on this page since you have no admin rights on it!")
01100                 raise self.NoAdmin, msg
01101 
01102         presave = PagePreSaveEvent(request, self, newtext)
01103         results = send_event(presave)
01104 
01105         for result in results:
01106             if isinstance(result, Abort):
01107                 # XXX: this should return a list of messages to the sorrounding context
01108                 # XXX: rather than dumbly concatenate them. Fix in the future.
01109                 msg = msg + result.reason
01110 
01111         # save only if no error occurred (msg is empty) and no abort has been requested
01112         if not msg:
01113             # set success msg
01114             msg = _("Thank you for your changes. Your attention to detail is appreciated.")
01115 
01116             # determine action for edit log
01117             if action == 'SAVE' and not self.exists():
01118                 action = 'SAVENEW'
01119             comment = kw.get('comment', u'')
01120             extra = kw.get('extra', u'')
01121             trivial = kw.get('trivial', 0)
01122             # write the page file
01123             mtime_usecs, rev = self._write_file(newtext, action, comment, extra, deleted=deleted)
01124             self._save_draft(None, None) # everything fine, kill the draft for this page
01125 
01126             if notify:
01127                 # send notifications
01128                 from MoinMoin import events
01129 
01130                 if trivial:
01131                     e = events.TrivialPageChangedEvent(self.request, self, comment)
01132                 else:
01133                     e = events.PageChangedEvent(self.request, self, comment)
01134                 results = events.send_event(e)
01135 
01136                 recipients = set()
01137                 for result in results:
01138                     if isinstance(result, notification.Success):
01139                         recipients.update(result.recipients)
01140 
01141                         if recipients:
01142                             info = _("Notifications sent to:")
01143                             msg = msg + "<p>%s %s</p>" % (info, ", ".join(recipients))
01144 
01145             # Update page trail with the page we just saved.
01146             # This is needed for NewPage macro with backto because it does not
01147             # send the page we just saved.
01148             request.user.addTrail(self)
01149 
01150         # remove lock (forcibly if we were allowed to break it by the UI)
01151         # !!! this is a little fishy, since the lock owner might not notice
01152         # we broke his lock ==> but revision checking during preview will
01153         self.lock.release(force=not msg) # XXX does "not msg" make any sense?
01154 
01155         return msg
01156 

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.send_page (   self,
  keywords 
) [inherited]
Output the formatted page.

TODO: "kill send_page(), quick" (since 2002 :)

@keyword content_only: if 1, omit http headers, page header and footer
@keyword content_id: set the id of the enclosing div
@keyword count_hit: if 1, add an event to the log
@keyword send_special: if True, this is a special page send
@keyword omit_footnotes: if True, do not send footnotes (used by include macro)

Definition at line 993 of file Page.py.

00993 
00994     def send_page(self, **keywords):
00995         """ Output the formatted page.
00996 
00997         TODO: "kill send_page(), quick" (since 2002 :)
00998 
00999         @keyword content_only: if 1, omit http headers, page header and footer
01000         @keyword content_id: set the id of the enclosing div
01001         @keyword count_hit: if 1, add an event to the log
01002         @keyword send_special: if True, this is a special page send
01003         @keyword omit_footnotes: if True, do not send footnotes (used by include macro)
01004         """
01005         request = self.request
01006         _ = request.getText
01007         request.clock.start('send_page')
01008         emit_headers = keywords.get('emit_headers', 1)
01009         content_only = keywords.get('content_only', 0)
01010         omit_footnotes = keywords.get('omit_footnotes', 0)
01011         content_id = keywords.get('content_id', 'content')
01012         do_cache = keywords.get('do_cache', 1)
01013         send_special = keywords.get('send_special', False)
01014         print_mode = keywords.get('print_mode', 0)
01015         if print_mode:
01016             media = request.values.get('media', 'print')
01017         else:
01018             media = 'screen'
01019         self.hilite_re = (keywords.get('hilite_re') or
01020                           request.values.get('highlight'))
01021 
01022         # count hit?
01023         if keywords.get('count_hit', 0):
01024             eventlog.EventLog(request).add(request, 'VIEWPAGE', {'pagename': self.page_name})
01025 
01026         # load the text
01027         body = self.data
01028         pi = self.pi
01029 
01030         if 'redirect' in pi and not (
01031             'action' in request.values or 'redirect' in request.values or content_only):
01032             # redirect to another page
01033             # note that by including "action=show", we prevent endless looping
01034             # (see code in "request") or any cascaded redirection
01035             pagename, anchor = wikiutil.split_anchor(pi['redirect'])
01036             redirect_url = Page(request, pagename).url(request,
01037                                                        querystr={'action': 'show', 'redirect': self.page_name, },
01038                                                        anchor=anchor)
01039             request.http_redirect(redirect_url, code=301)
01040             return
01041 
01042         # if necessary, load the formatter
01043         if self.default_formatter:
01044             from MoinMoin.formatter.text_html import Formatter
01045             self.formatter = Formatter(request, store_pagelinks=1)
01046         elif not self.formatter:
01047             Formatter = wikiutil.searchAndImportPlugin(request.cfg, "formatter", self.output_mimetype)
01048             self.formatter = Formatter(request)
01049 
01050         # save formatter
01051         no_formatter = object()
01052         old_formatter = getattr(request, "formatter", no_formatter)
01053         request.formatter = self.formatter
01054 
01055         self.formatter.setPage(self)
01056         if self.hilite_re:
01057             try:
01058                 self.formatter.set_highlight_re(self.hilite_re)
01059             except re.error, err:
01060                 request.theme.add_msg(_('Invalid highlighting regular expression "%(regex)s": %(error)s') % {
01061                                           'regex': self.hilite_re,
01062                                           'error': str(err),
01063                                       }, "warning")
01064                 self.hilite_re = None
01065 
01066         if 'deprecated' in pi:
01067             # deprecated page, append last backup version to current contents
01068             # (which should be a short reason why the page is deprecated)
01069             request.theme.add_msg(_('The backed up content of this page is deprecated and will rank lower in search results!'), "warning")
01070 
01071             revisions = self.getRevList()
01072             if len(revisions) >= 2: # XXX shouldn't that be ever the case!? Looks like not.
01073                 oldpage = Page(request, self.page_name, rev=revisions[1])
01074                 body += oldpage.get_raw_body()
01075                 del oldpage
01076 
01077         lang = self.pi.get('language', request.cfg.language_default)
01078         request.setContentLanguage(lang)
01079 
01080         # start document output
01081         page_exists = self.exists()
01082         if not content_only:
01083             if emit_headers:
01084                 request.content_type = "%s; charset=%s" % (self.output_mimetype, self.output_charset)
01085                 if page_exists:
01086                     if not request.user.may.read(self.page_name):
01087                         request.status_code = 403
01088                     else:
01089                         request.status_code = 200
01090                     if not request.cacheable:
01091                         # use "nocache" headers if we're using a method that is not simply "display"
01092                         request.disableHttpCaching(level=2)
01093                     elif request.user.valid:
01094                         # use nocache headers if a user is logged in (which triggers personalisation features)
01095                         request.disableHttpCaching(level=1)
01096                     else:
01097                         # TODO: we need to know if a page generates dynamic content -
01098                         # if it does, we must not use the page file mtime as last modified value
01099                         # The following code is commented because it is incorrect for dynamic pages:
01100                         #lastmod = os.path.getmtime(self._text_filename())
01101                         #request.setHttpHeader("Last-Modified: %s" % util.timefuncs.formathttpdate(lastmod))
01102                         pass
01103                 else:
01104                     request.status_code = 404
01105 
01106             if not page_exists and self.request.isSpiderAgent:
01107                 # don't send any 404 content to bots
01108                 return
01109 
01110             request.write(self.formatter.startDocument(self.page_name))
01111 
01112             # send the page header
01113             if self.default_formatter:
01114                 if self.rev:
01115                     request.theme.add_msg("<strong>%s</strong><br>" % (
01116                         _('Revision %(rev)d as of %(date)s') % {
01117                             'rev': self.rev,
01118                             'date': self.mtime_printable(request)
01119                         }), "info")
01120 
01121                 # This redirect message is very annoying.
01122                 # Less annoying now without the warning sign.
01123                 if 'redirect' in request.values:
01124                     redir = request.values['redirect']
01125                     request.theme.add_msg('<strong>%s</strong><br>' % (
01126                         _('Redirected from page "%(page)s"') % {'page':
01127                             wikiutil.link_tag(request, wikiutil.quoteWikinameURL(redir) + "?action=show", self.formatter.text(redir))}), "info")
01128                 if 'redirect' in pi:
01129                     request.theme.add_msg('<strong>%s</strong><br>' % (
01130                         _('This page redirects to page "%(page)s"') % {'page': wikiutil.escape(pi['redirect'])}), "info")
01131 
01132                 # Page trail
01133                 trail = None
01134                 if not print_mode:
01135                     request.user.addTrail(self)
01136                     trail = request.user.getTrail()
01137 
01138                 title = self.split_title()
01139 
01140                 html_head = ''
01141                 if request.cfg.openid_server_enabled:
01142                     openid_username = self.page_name
01143                     userid = user.getUserId(request, openid_username)
01144 
01145                     if userid is None and 'openid.user' in self.pi:
01146                         openid_username = self.pi['openid.user']
01147                         userid = user.getUserId(request, openid_username)
01148 
01149                     openid_group_name = request.cfg.openid_server_restricted_users_group
01150                     if userid is not None and not openid_group_name or \
01151                             (openid_group_name in request.groups and openid_username in request.groups[openid_group_name]):
01152                         html_head = '<link rel="openid2.provider" href="%s">' % \
01153                                         wikiutil.escape(request.getQualifiedURL(self.url(request,
01154                                                                                 querystr={'action': 'serveopenid'})), True)
01155                         html_head += '<link rel="openid.server" href="%s">' % \
01156                                         wikiutil.escape(request.getQualifiedURL(self.url(request,
01157                                                                                 querystr={'action': 'serveopenid'})), True)
01158                         html_head += '<meta http-equiv="x-xrds-location" content="%s">' % \
01159                                         wikiutil.escape(request.getQualifiedURL(self.url(request,
01160                                                                                 querystr={'action': 'serveopenid', 'yadis': 'ep'})), True)
01161                     elif self.page_name == request.cfg.page_front_page:
01162                         html_head = '<meta http-equiv="x-xrds-location" content="%s">' % \
01163                                         wikiutil.escape(request.getQualifiedURL(self.url(request,
01164                                                                                 querystr={'action': 'serveopenid', 'yadis': 'idp'})), True)
01165 
01166                 request.theme.send_title(title, page=self,
01167                                     print_mode=print_mode,
01168                                     media=media, pi_refresh=pi.get('refresh'),
01169                                     allow_doubleclick=1, trail=trail,
01170                                     html_head=html_head,
01171                                     )
01172 
01173         # special pages handling, including denying access
01174         special = None
01175 
01176         if not send_special:
01177             if not page_exists and not body:
01178                 special = 'missing'
01179             elif not request.user.may.read(self.page_name):
01180                 special = 'denied'
01181 
01182             # if we have a special page, output it, unless
01183             #  - we should only output content (this is for say the pagelinks formatter)
01184             #  - we have a non-default formatter
01185             if special and not content_only and self.default_formatter:
01186                 self._specialPageText(request, special) # this recursively calls send_page
01187 
01188         # if we didn't short-cut to a special page, output this page
01189         if not special:
01190             # start wiki content div
01191             request.write(self.formatter.startContent(content_id))
01192 
01193             # parse the text and send the page content
01194             self.send_page_content(request, body,
01195                                    format=pi['format'],
01196                                    format_args=pi['formatargs'],
01197                                    do_cache=do_cache,
01198                                    start_line=pi['lines'])
01199 
01200             # check for pending footnotes
01201             if getattr(request, 'footnotes', None) and not omit_footnotes:
01202                 from MoinMoin.macro.FootNote import emit_footnotes
01203                 request.write(emit_footnotes(request, self.formatter))
01204 
01205             # end wiki content div
01206             request.write(self.formatter.endContent())
01207 
01208         # end document output
01209         if not content_only:
01210             # send the page footer
01211             if self.default_formatter:
01212                 request.theme.send_footer(self.page_name, print_mode=print_mode)
01213 
01214             request.write(self.formatter.endDocument())
01215 
01216         request.clock.stop('send_page')
01217         if not content_only and self.default_formatter:
01218             request.theme.send_closing_html()
01219 
01220         # cache the pagelinks
01221         if do_cache and self.default_formatter and page_exists:
01222             cache = caching.CacheEntry(request, self, 'pagelinks', scope='item', use_pickle=True)
01223             if cache.needsUpdate(self._text_filename()):
01224                 links = self.formatter.pagelinks
01225                 cache.update(links)
01226 
01227         # restore old formatter (hopefully we dont throw any exception that is catched again)
01228         if old_formatter is no_formatter:
01229             del request.formatter
01230         else:
01231             request.formatter = old_formatter
01232 

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.send_page_content (   self,
  request,
  body,
  format = 'wiki',
  format_args = '',
  do_cache = 1,
  kw 
) [inherited]
Output the formatted wiki page, using caching if possible

@param request: the request object
@param body: text of the wiki page
@param format: format of content, default 'wiki'
@param format_args: #format arguments, used by some parsers
@param do_cache: if True, use cached content

Definition at line 1266 of file Page.py.

01266 
01267     def send_page_content(self, request, body, format='wiki', format_args='', do_cache=1, **kw):
01268         """ Output the formatted wiki page, using caching if possible
01269 
01270         @param request: the request object
01271         @param body: text of the wiki page
01272         @param format: format of content, default 'wiki'
01273         @param format_args: #format arguments, used by some parsers
01274         @param do_cache: if True, use cached content
01275         """
01276         request.clock.start('send_page_content')
01277         # Load the parser
01278         Parser = wikiutil.searchAndImportPlugin(request.cfg, "parser", format)
01279         parser = Parser(body, request, format_args=format_args, **kw)
01280 
01281         if not (do_cache and self.canUseCache(Parser)):
01282             self.format(parser)
01283         else:
01284             try:
01285                 code = self.loadCache(request)
01286                 self.execute(request, parser, code)
01287             except Exception, e:
01288                 if not is_cache_exception(e):
01289                     raise
01290                 try:
01291                     code = self.makeCache(request, parser)
01292                     self.execute(request, parser, code)
01293                 except Exception, e:
01294                     if not is_cache_exception(e):
01295                         raise
01296                     logging.error('page cache failed after creation')
01297                     self.format(parser)
01298 
01299         request.clock.stop('send_page_content')

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.Page.Page.send_raw (   self,
  content_disposition = None,
  mimetype = None 
) [inherited]
Output the raw page data (action=raw).
    With no content_disposition, the browser usually just displays the
    data on the screen, with content_disposition='attachment', it will
    offer a dialogue to save it to disk (used by Save action).
    Supplied mimetype overrides default text/plain.

Definition at line 964 of file Page.py.

00964 
00965     def send_raw(self, content_disposition=None, mimetype=None):
00966         """ Output the raw page data (action=raw).
00967             With no content_disposition, the browser usually just displays the
00968             data on the screen, with content_disposition='attachment', it will
00969             offer a dialogue to save it to disk (used by Save action).
00970             Supplied mimetype overrides default text/plain.
00971         """
00972         request = self.request
00973         request.mimetype = mimetype or 'text/plain'
00974         if self.exists():
00975             # use the correct last-modified value from the on-disk file
00976             # to ensure cacheability where supported. Because we are sending
00977             # RAW (file) content, the file mtime is correct as Last-Modified header.
00978             request.status_code = 200
00979             request.last_modified = os.path.getmtime(self._text_filename())
00980             text = self.encodeTextMimeType(self.body)
00981             #request.setHttpHeader("Content-Length: %d" % len(text))  # XXX WRONG! text is unicode obj, but we send utf-8!
00982             if content_disposition:
00983                 # TODO: fix the encoding here, plain 8 bit is not allowed according to the RFCs
00984                 # There is no solution that is compatible to IE except stripping non-ascii chars
00985                 filename_enc = "%s.txt" % self.page_name.encode(config.charset)
00986                 dispo_string = '%s; filename="%s"' % (content_disposition, filename_enc)
00987                 request.headers.add('Content-Disposition', dispo_string)
00988         else:
00989             request.status_code = 404
00990             text = u"Page %s not found." % self.page_name
00991 
00992         request.write(text)

Here is the call graph for this function:

def MoinMoin.PageEditor.PageEditor.sendCancel (   self,
  newtext,
  rev 
)
User clicked on Cancel button.
    If edit locking is active, delete the current lock file.

@param newtext: the edited text (which has been cancelled)
@param rev: not used!?

Definition at line 503 of file PageEditor.py.

00503 
00504     def sendCancel(self, newtext, rev):
00505         """ User clicked on Cancel button.
00506             If edit locking is active, delete the current lock file.
00507 
00508         @param newtext: the edited text (which has been cancelled)
00509         @param rev: not used!?
00510         """
00511         request = self.request
00512         _ = self._
00513         self._save_draft(newtext, rev) # shall we really save a draft on CANCEL?
00514         self.lock.release()
00515 
00516         backto = request.values.get('backto')
00517         if backto:
00518             pg = Page(request, backto)
00519             request.http_redirect(pg.url(request))
00520         else:
00521             request.theme.add_msg(_('Edit was cancelled.'), "error")
00522             self.send_page()

Here is the call graph for this function:

Prevents moving away from the page without saving it. 

Definition at line 130 of file PageEditor.py.

00130 
00131     def sendconfirmleaving(self):
00132         """ Prevents moving away from the page without saving it. """
00133         _ = self._
00134         self.request.write(u'''\
00135 <script type="text/javascript">
00136     var flgChange = false;
00137     function confirmleaving() {
00138         if ( flgChange )
00139             return "%s";
00140     }
00141 </script>
00142 ''' % _("Your changes are not saved!"))

Here is the caller graph for this function:

Send the editor form page.

@keyword preview: if given, show this text in preview mode
@keyword staytop: don't go to #preview
@keyword comment: comment field (when preview is true)

Reimplemented in MoinMoin.PageGraphicalEditor.PageGraphicalEditor.

Definition at line 143 of file PageEditor.py.

00143 
00144     def sendEditor(self, **kw):
00145         """ Send the editor form page.
00146 
00147         @keyword preview: if given, show this text in preview mode
00148         @keyword staytop: don't go to #preview
00149         @keyword comment: comment field (when preview is true)
00150         """
00151         from MoinMoin import i18n
00152         from MoinMoin.action import SpellCheck
00153         request = self.request
00154         form = request.form
00155         _ = self._
00156 
00157         raw_body = ''
00158         msg = None
00159         conflict_msg = None
00160         edit_lock_message = None
00161         preview = kw.get('preview', None)
00162         staytop = kw.get('staytop', 0)
00163 
00164         from MoinMoin.formatter.text_html import Formatter
00165         request.formatter = Formatter(request, store_pagelinks=1)
00166 
00167         # check edit permissions
00168         if not request.user.may.write(self.page_name):
00169             msg = _('You are not allowed to edit this page.')
00170         elif not self.isWritable():
00171             msg = _('Page is immutable!')
00172         elif self.rev:
00173             # Trying to edit an old version, this is not possible via
00174             # the web interface, but catch it just in case...
00175             msg = _('Cannot edit old revisions!')
00176         else:
00177             try:
00178                 # try to acquire edit lock
00179                 ok, edit_lock_message = self.lock.acquire()
00180                 if not ok:
00181                     # failed to get the lock
00182                     if preview is not None:
00183                         edit_lock_message = _('The lock you held timed out. Be prepared for editing conflicts!'
00184                             ) + "<br>" + edit_lock_message
00185                     else:
00186                         msg = edit_lock_message
00187             except OSError, err:
00188                 if err.errno == errno.ENAMETOOLONG:
00189                     msg = _("Page name is too long, try shorter name.")
00190                 else:
00191                     raise
00192 
00193         # Did one of the prechecks fail?
00194         if msg:
00195             request.theme.add_msg(msg, "error")
00196             self.send_page()
00197             return
00198 
00199         # Emit http_headers after checks (send_page)
00200         request.disableHttpCaching(level=2)
00201 
00202         # check if we want to load a draft
00203         use_draft = None
00204         if 'button_load_draft' in form:
00205             wanted_draft_timestamp = int(form.get('draft_ts', '0'))
00206             if wanted_draft_timestamp:
00207                 draft = self._load_draft()
00208                 if draft is not None:
00209                     draft_timestamp, draft_rev, draft_text = draft
00210                     if draft_timestamp == wanted_draft_timestamp:
00211                         use_draft = draft_text
00212 
00213         # Check for draft / normal / preview submit
00214         if use_draft is not None:
00215             title = _('Draft of "%(pagename)s"')
00216             # Propagate original revision
00217             rev = int(form['draft_rev'])
00218             self.set_raw_body(use_draft, modified=1)
00219             preview = use_draft
00220         elif preview is None:
00221             title = _('Edit "%(pagename)s"')
00222         else:
00223             title = _('Preview of "%(pagename)s"')
00224             # Propagate original revision
00225             rev = request.rev
00226             self.set_raw_body(preview, modified=1)
00227 
00228         # send header stuff
00229         lock_timeout = self.lock.timeout / 60
00230         lock_page = wikiutil.escape(self.page_name, quote=1)
00231         lock_expire = _("Your edit lock on %(lock_page)s has expired!") % {'lock_page': lock_page}
00232         lock_mins = _("Your edit lock on %(lock_page)s will expire in # minutes.") % {'lock_page': lock_page}
00233         lock_secs = _("Your edit lock on %(lock_page)s will expire in # seconds.") % {'lock_page': lock_page}
00234 
00235         # get request parameters
00236         try:
00237             text_rows = int(form['rows'])
00238         except StandardError:
00239             text_rows = self.cfg.edit_rows
00240             if request.user.valid:
00241                 text_rows = int(request.user.edit_rows)
00242 
00243         if preview is not None:
00244             # Check for editing conflicts
00245             if not self.exists():
00246                 # page does not exist, are we creating it?
00247                 if rev:
00248                     conflict_msg = _('Someone else deleted this page while you were editing!')
00249             elif rev != self.current_rev():
00250                 conflict_msg = _('Someone else changed this page while you were editing!')
00251                 if self.mergeEditConflict(rev):
00252                     conflict_msg = _("""Someone else saved this page while you were editing!
00253 Please review the page and save then. Do not save this page as it is!""")
00254                     rev = self.current_rev()
00255             if conflict_msg:
00256                 # We don't show preview when in conflict
00257                 preview = None
00258 
00259         elif self.exists():
00260             # revision of existing page
00261             rev = self.current_rev()
00262         else:
00263             # page creation
00264             rev = 0
00265 
00266         # Page editing is done using user language
00267         request.setContentLanguage(request.lang)
00268 
00269         # Get the text body for the editor field.
00270         # TODO: what about deleted pages? show the text of the last revision or use the template?
00271         if preview is not None:
00272             raw_body = self.get_raw_body()
00273             if use_draft:
00274                 request.theme.add_msg(_("[Content loaded from draft]"), 'info')
00275         elif self.exists():
00276             # If the page exists, we get the text from the page.
00277             # TODO: maybe warn if template argument was ignored because the page exists?
00278             raw_body = self.get_raw_body()
00279         elif 'template' in request.values:
00280             # If the page does not exist, we try to get the content from the template parameter.
00281             template_page = wikiutil.unquoteWikiname(request.values['template'])
00282             if request.user.may.read(template_page):
00283                 raw_body = Page(request, template_page).get_raw_body()
00284                 if raw_body:
00285                     request.theme.add_msg(_("[Content of new page loaded from %s]") % (template_page, ), 'info')
00286                 else:
00287                     request.theme.add_msg(_("[Template %s not found]") % (template_page, ), 'warning')
00288             else:
00289                 request.theme.add_msg(_("[You may not read %s]") % (template_page, ), 'error')
00290 
00291         # Make backup on previews - but not for new empty pages
00292         if not use_draft and preview and raw_body:
00293             self._save_draft(raw_body, rev)
00294 
00295         draft_message = None
00296         loadable_draft = False
00297         if preview is None:
00298             draft = self._load_draft()
00299             if draft is not None:
00300                 draft_timestamp, draft_rev, draft_text = draft
00301                 if draft_text != raw_body:
00302                     loadable_draft = True
00303                     page_rev = rev
00304                     draft_timestamp_str = request.user.getFormattedDateTime(draft_timestamp)
00305                     draft_message = _(u"'''<<BR>>Your draft based on revision %(draft_rev)d (saved %(draft_timestamp_str)s) can be loaded instead of the current revision %(page_rev)d by using the load draft button - in case you lost your last edit somehow without saving it.''' A draft gets saved for you when you do a preview, cancel an edit or unsuccessfully save.", wiki=True) % locals()
00306 
00307         # Setup status message
00308         status = [kw.get('msg', ''), conflict_msg, edit_lock_message, draft_message]
00309         status = [msg for msg in status if msg]
00310         status = ' '.join(status)
00311         status = Status(request, content=status)
00312         request.theme.add_msg(status, "dialog")
00313 
00314         request.theme.send_title(
00315             title % {'pagename': self.split_title(), },
00316             page=self,
00317             html_head=self.lock.locktype and (
00318                 _countdown_js % {
00319                      'countdown_script': request.theme.externalScript('countdown'),
00320                      'lock_timeout': lock_timeout,
00321                      'lock_expire': lock_expire,
00322                      'lock_mins': lock_mins,
00323                      'lock_secs': lock_secs,
00324                     }) or '',
00325             editor_mode=1,
00326         )
00327 
00328         request.write(request.formatter.startContent("content"))
00329 
00330         # Generate default content for new pages
00331         if not raw_body:
00332             raw_body = _('Describe %s here.') % (self.page_name, )
00333 
00334         # send form
00335         request.write('<form id="editor" method="post" action="%s#preview" onSubmit="flgChange = false;">' % (
00336                 request.href(self.page_name)
00337         ))
00338 
00339         # yet another weird workaround for broken IE6 (it expands the text
00340         # editor area to the right after you begin to type...). IE sucks...
00341         # http://fplanque.net/2003/Articles/iecsstextarea/
00342         request.write('<fieldset style="border:none;padding:0;">')
00343 
00344         request.write(unicode(html.INPUT(type="hidden", name="action", value="edit")))
00345 
00346         # Send revision of the page our edit is based on
00347         request.write('<input type="hidden" name="rev" value="%d">' % (rev, ))
00348 
00349         # Create and send a ticket, so we can check the POST
00350         request.write('<input type="hidden" name="ticket" value="%s">' % wikiutil.createTicket(request))
00351 
00352         # Save backto in a hidden input
00353         backto = request.values.get('backto')
00354         if backto:
00355             request.write(unicode(html.INPUT(type="hidden", name="backto", value=backto)))
00356 
00357         # button bar
00358         button_spellcheck = '<input class="button" type="submit" name="button_spellcheck" value="%s" onClick="flgChange = false;">' % _('Check Spelling')
00359 
00360         save_button_text = _('Save Changes')
00361         cancel_button_text = _('Cancel')
00362 
00363         if self.cfg.page_license_enabled:
00364             request.write('<p><em>', _(
00365 """By hitting '''%(save_button_text)s''' you put your changes under the %(license_link)s.
00366 If you don't want that, hit '''%(cancel_button_text)s''' to cancel your changes.""", wiki=True) % {
00367                 'save_button_text': save_button_text,
00368                 'cancel_button_text': cancel_button_text,
00369                 'license_link': wikiutil.getLocalizedPage(request, self.cfg.page_license_page).link_to(request),
00370             }, '</em></p>')
00371 
00372 
00373         request.write('''
00374 <input class="button" type="submit" name="button_save" value="%s" onClick="flgChange = false;">
00375 <input class="button" type="submit" name="button_preview" value="%s" onClick="flgChange = false;">
00376 ''' % (save_button_text, _('Preview'), ))
00377 
00378         if not (request.cfg.editor_force and request.cfg.editor_default == 'text'):
00379             request.write('''
00380 <input id="switch2gui" style="display: none;" class="button" type="submit" name="button_switch" value="%s">
00381 ''' % (_('GUI Mode'), ))
00382 
00383         if loadable_draft:
00384             request.write('''
00385 <input class="button" type="submit" name="button_load_draft" value="%s" onClick="flgChange = false;">
00386 <input type="hidden" name="draft_ts" value="%d">
00387 <input type="hidden" name="draft_rev" value="%d">
00388 ''' % (_('Load Draft'), draft_timestamp, draft_rev))
00389 
00390         request.write('''
00391 %s
00392 <input class="button" type="submit" name="button_cancel" value="%s">
00393 <input type="hidden" name="editor" value="text">
00394 ''' % (button_spellcheck, cancel_button_text, ))
00395 
00396         # Trivial Change-checkbox to the top of the page, shows up only if user has JavaScript enabled. It's "linked" with the bottom's box (checking one checks both)
00397         if self.cfg.mail_enabled:
00398             request.write('''
00399 <script type="text/javascript">
00400     <!--
00401     function toggle_trivial(CheckedBox)
00402     {
00403         TrivialBoxes = document.getElementsByName("trivial");
00404         for (var i = 0; i < TrivialBoxes.length; i++)
00405             TrivialBoxes[i].checked = CheckedBox.checked;
00406     }
00407 
00408     document.write('<input type="checkbox" name="trivial" id="chktrivialtop" value="1" %(checked)s onclick="toggle_trivial(this)">');
00409     document.write('<label for="chktrivialtop">%(label)s</label>');
00410     //-->
00411 </script> ''' % {
00412                 'checked': ('', 'checked')[form.get('trivial', '0') == '1'],
00413                 'label': _("Trivial change"),
00414             })
00415 
00416         from MoinMoin.security.textcha import TextCha
00417         request.write(TextCha(request).render())
00418 
00419         # Add textarea with page text
00420         self.sendconfirmleaving()
00421 
00422         lang = self.pi.get('language', request.cfg.language_default)
00423 
00424         request.write(
00425             u'''\
00426 <textarea id="editor-textarea" name="savetext" lang="%(lang)s" dir="%(dir)s" rows="%(rows)d" cols="80"
00427           onChange="flgChange = true;" onKeyPress="flgChange = true;">\
00428 %(text)s\
00429 </textarea>''' % {
00430             'lang': lang,
00431             'dir': i18n.getDirection(lang),
00432             'rows': text_rows,
00433             'text': wikiutil.escape(raw_body)
00434         })
00435 
00436         request.write("<p>")
00437         request.write(_("Comment:"),
00438             ' <input id="editor-comment" type="text" name="comment" value="%s" size="80" maxlength="200"'
00439             ' onChange="flgChange = true;" onKeyPress="flgChange = true;">' % (
00440                 wikiutil.escape(kw.get('comment', ''), 1), ))
00441         request.write("</p>")
00442 
00443         # Category selection
00444         filterfn = self.cfg.cache.page_category_regexact.search
00445         cat_pages = request.rootpage.getPageList(filter=filterfn)
00446         cat_pages.sort()
00447         cat_pages = [wikiutil.pagelinkmarkup(p) for p in cat_pages]
00448         cat_pages.insert(0, ('', _('<No addition>')))
00449         request.write("<p>")
00450         request.write(_('Add to: %(category)s') % {
00451             'category': unicode(web.makeSelection('category', cat_pages)),
00452         })
00453 
00454         if self.cfg.mail_enabled:
00455             request.write('''
00456 &nbsp;
00457 
00458 <input type="checkbox" name="trivial" id="chktrivial" value="1" %(checked)s onclick="toggle_trivial(this)">
00459 <label for="chktrivial">%(label)s</label>
00460 
00461 ''' % {
00462                 'checked': ('', 'checked')[form.get('trivial', '0') == '1'],
00463                 'label': _("Trivial change"),
00464                 })
00465 
00466         request.write('''
00467 &nbsp;
00468 <input type="checkbox" name="rstrip" id="chkrstrip" value="1" %(checked)s>
00469 <label for="chkrstrip">%(label)s</label>
00470 ''' % {
00471             'checked': ('', 'checked')[form.get('rstrip', '0') == '1'],
00472             'label': _('Remove trailing whitespace from each line')
00473             })
00474         request.write("</p>")
00475 
00476         badwords_re = None
00477         if preview is not None:
00478             if 'button_spellcheck' in form or 'button_newwords' in form:
00479                 badwords, badwords_re, msg = SpellCheck.checkSpelling(self, request, own_form=0)
00480                 request.write("<p>%s</p>" % msg)
00481         request.write('</fieldset>')
00482         request.write("</form>")
00483 
00484         # QuickHelp originally by Georg Mischler <schorsch@lightingwiki.com>
00485         markup = self.pi['format'] or request.cfg.default_markup
00486         parser = wikiutil.searchAndImportPlugin(self.request.cfg, "parser", markup)
00487         quickhelp = getattr(parser, 'quickhelp', None)
00488         if quickhelp:
00489             request.write(request.formatter.div(1, id="editor-help"))
00490             request.write(_(quickhelp, wiki=True))
00491             request.write(request.formatter.div(0))
00492 
00493         if preview is not None:
00494             if staytop:
00495                 content_id = 'previewbelow'
00496             else:
00497                 content_id = 'preview'
00498             self.send_page(content_id=content_id, content_only=1, hilite_re=badwords_re)
00499 
00500         request.write(request.formatter.endContent())
00501         request.theme.send_footer(self.page_name)
00502         request.theme.send_closing_html()

Here is the call graph for this function:

def MoinMoin.Page.Page.set_body (   self,
  newbody 
) [inherited]

Definition at line 233 of file Page.py.

00233 
00234     def set_body(self, newbody):
00235         self.__body = newbody
00236         self.__meta = None
        self.__data = None
def MoinMoin.Page.Page.set_raw_body (   self,
  body,
  modified = 0 
) [inherited]
Set the raw body text (prevents loading from disk).

TODO: this should not be a public function, as Page is immutable.

@param body: raw body text
@param modified: 1 means that we internally modified the raw text and
    that it is not in sync with the page file on disk.  This is
    used e.g. by PageEditor when previewing the page.

Definition at line 281 of file Page.py.

00281 
00282     def set_raw_body(self, body, modified=0):
00283         """ Set the raw body text (prevents loading from disk).
00284 
00285         TODO: this should not be a public function, as Page is immutable.
00286 
00287         @param body: raw body text
00288         @param modified: 1 means that we internally modified the raw text and
00289             that it is not in sync with the page file on disk.  This is
00290             used e.g. by PageEditor when previewing the page.
00291         """
00292         self.body = body
00293         self.__body_modified = modified

Here is the caller graph for this function:

def MoinMoin.Page.Page.setConflict (   self,
  state 
) [inherited]
Sets the editing conflict flag.

@param state: bool, true if there is a conflict.

Definition at line 1641 of file Page.py.

01641 
01642     def setConflict(self, state):
01643         """ Sets the editing conflict flag.
01644 
01645         @param state: bool, true if there is a conflict.
01646         """
01647         cache = caching.CacheEntry(self.request, self, 'conflict', scope='item')
01648         if state:
01649             cache.update("") # touch it!
01650         else:
01651             cache.remove()
01652 

Here is the caller graph for this function:

def MoinMoin.Page.Page.size (   self,
  rev = 0 
) [inherited]
Get Page size.

@rtype: int
@return: page size, 0 for non-existent pages.

Definition at line 670 of file Page.py.

00670 
00671     def size(self, rev=0):
00672         """ Get Page size.
00673 
00674         @rtype: int
00675         @return: page size, 0 for non-existent pages.
00676         """
00677         if rev == self.rev: # same revision as self
00678             if self.__body is not None:
00679                 return len(self.__body)
00680 
00681         try:
00682             return os.path.getsize(self._text_filename(rev=rev))
00683         except EnvironmentError, e:
00684             import errno
00685             if e.errno == errno.ENOENT:
00686                 return 0
00687             raise

Here is the call graph for this function:

def MoinMoin.Page.Page.split_title (   self,
  force = 0 
) [inherited]
Return a string with the page name split by spaces, if the user wants that.

@param force: if != 0, then force splitting the page_name
@rtype: unicode
@return: pagename of this page, splitted into space separated words

Definition at line 711 of file Page.py.

00711 
00712     def split_title(self, force=0):
00713         """ Return a string with the page name split by spaces, if the user wants that.
00714 
00715         @param force: if != 0, then force splitting the page_name
00716         @rtype: unicode
00717         @return: pagename of this page, splitted into space separated words
00718         """
00719         request = self.request
00720         if not force and not request.user.wikiname_add_spaces:
00721             return self.page_name
00722 
00723         # look for the end of words and the start of a new word,
00724         # and insert a space there
00725         splitted = config.split_regex.sub(r'\1 \2', self.page_name)
00726         return splitted

Here is the caller graph for this function:

def MoinMoin.Page.Page.url (   self,
  request,
  querystr = None,
  anchor = None,
  relative = False,
  kw 
) [inherited]
Return complete URL for this page, including scriptname.
    The URL is NOT escaped, if you write it to HTML, use wikiutil.escape
    (at least if you have a querystr, to escape the & chars).

@param request: the request object
@param querystr: the query string to add after a "?" after the url
    (str or dict, see wikiutil.makeQueryString)
@param anchor: if specified, make a link to this anchor
@param relative: create a relative link (default: False), note that this
         changed in 1.7, in 1.6, the default was True.
@rtype: str
@return: complete url of this page, including scriptname

Definition at line 727 of file Page.py.

00727 
00728     def url(self, request, querystr=None, anchor=None, relative=False, **kw):
00729         """ Return complete URL for this page, including scriptname.
00730             The URL is NOT escaped, if you write it to HTML, use wikiutil.escape
00731             (at least if you have a querystr, to escape the & chars).
00732 
00733         @param request: the request object
00734         @param querystr: the query string to add after a "?" after the url
00735             (str or dict, see wikiutil.makeQueryString)
00736         @param anchor: if specified, make a link to this anchor
00737         @param relative: create a relative link (default: False), note that this
00738                          changed in 1.7, in 1.6, the default was True.
00739         @rtype: str
00740         @return: complete url of this page, including scriptname
00741         """
00742         assert(isinstance(anchor, (type(None), str, unicode)))
00743         # Create url, excluding scriptname
00744         url = wikiutil.quoteWikinameURL(self.page_name)
00745         if querystr:
00746             if isinstance(querystr, dict):
00747                 action = querystr.get('action', None)
00748             else:
00749                 action = None # we don't support getting the action out of a str
00750 
00751             querystr = wikiutil.makeQueryString(querystr)
00752 
00753             # make action URLs denyable by robots.txt:
00754             if action is not None and request.cfg.url_prefix_action is not None:
00755                 url = "%s/%s/%s" % (request.cfg.url_prefix_action, action, url)
00756             url = '%s?%s' % (url, querystr)
00757 
00758         if not relative:
00759             url = '%s/%s' % (request.script_root, url)
00760 
00761         # Add anchor
00762         if anchor:
00763             fmt = getattr(self, 'formatter', request.html_formatter)
00764             if fmt:
00765                 anchor = fmt.sanitize_to_id(anchor)
00766             url = "%s#%s" % (url, anchor)
00767 
00768         return url

Here is the caller graph for this function:


Member Data Documentation

Definition at line 93 of file PageEditor.py.

Definition at line 149 of file Page.py.

Definition at line 159 of file Page.py.

Definition at line 96 of file PageEditor.py.

Definition at line 95 of file PageEditor.py.

Definition at line 559 of file PageEditor.py.

Reimplemented in MoinMoin.PageGraphicalEditor.PageGraphicalEditor.

Definition at line 157 of file Page.py.

Definition at line 172 of file Page.py.

Definition at line 152 of file Page.py.

Definition at line 99 of file PageEditor.py.

Definition at line 169 of file Page.py.

Definition at line 158 of file Page.py.

Definition at line 150 of file Page.py.

Definition at line 189 of file Page.py.

Definition at line 148 of file Page.py.

Definition at line 151 of file Page.py.

Definition at line 97 of file PageEditor.py.


Property Documentation

MoinMoin.Page.Page.body = property(fget=get_body, fset=set_body) [static, inherited]

Definition at line 237 of file Page.py.

MoinMoin.Page.Page.data = property(fget=get_data) [static, inherited]

Definition at line 249 of file Page.py.

MoinMoin.Page.Page.meta = property(fget=get_meta) [static, inherited]

Definition at line 243 of file Page.py.

MoinMoin.Page.Page.pi = property(fget=get_pi) [static, inherited]

Definition at line 255 of file Page.py.


The documentation for this class was generated from the following file: