def renderContentsLink(page): """Render a link to page, suitable for contents or context.""" # quicker than renderLinkToPage, since we know it exists: wikiurl = self.wiki_url() def quicklink(page): id = self.canonicalIdFrom(page) return '<a href="%s/%s" name="%s">%s</a>' \ % (wikiurl,id,id,self.formatWikiname(page)) if here and page == here: if enlarge_current: # just assume we are in the page header, and link to # backlinks as well as enlarging #return '<big><big><big><big><strong>%s</strong></big></big></big></big>' % \ return '<h1 style="display:inline;">%s</h1>' % \ ('<a href="%s/%s/backlinks" title="%s" name="%s">%s</a>' % \ (wikiurl, self.canonicalIdFrom(page), _("which pages link to this one ?"), page, self.formatWikiname(page))) else: # just highlight "here" return '%s <span id="youarehere"><-- %s.</span>' % \ ((suppress_hyperlink and page) # another special case or quicklink(page), _("You are here")) else: #return self.renderLinkToPage(page,name=page) return quicklink(page)
def davLockDialog(self): """ web page displayed in webDAV lock conflict situations. """ titlestr=_('Page is locked') return MessageDialog( title=titlestr, message=""" <b>%s</b> <p> %s <p> %s """ % ( titlestr, _(""" This page has a webDAV lock. Someone is probably editing it with an external editor. You'll need to wait until they've finished and then try again. If you've just made some changes, you may want to back up and copy your version of the text for reference. """), _("To discard your changes and try again, click OK."), ), action=self.pageUrl()+'/editform')
def dummyOutlineConstructor(self): """Stub so we can get PersistentOutline registered with ZMI. The real constructor is a zwikipage method and is called automatically. """ return MessageDialog( title=_("No need to add a Zwiki Outline Cache"), message=_("""Zwiki Outline Cache appears in the ZMI Add menu for implementation reasons, but should not be added directly. Zwiki will create it for you as needed. """))
def showAccessKeys(self): """ Show the access keys supported by the built-in skins. """ return _(""" Access keys can be accessed in mozilla-based browsers by pressing alt-<key> IE users: must also press enter Mac users: command-<key> Opera users: shift-escape-<key> These won't work here, back up to the previous page to try them out. 0 show these access key assignments wiki functions: f show front page c show wiki contents r show wiki recent changes show discussion page t show issue tracker i show wiki index o show wiki options (preferences) h show help page s go to search field page functions: + (in a plone/cmf site with skin switching set up) use zwiki's plone/cmf skin - (in a plone/cmf site with skin switching set up) use zwiki's standard skin v view page m mail subscription b show backlinks (links to this page) d show diffs (page edit history) y show full history (in ZMI) e edit this page x edit with an external editor print this page (and subtopics) q view page source (quick-view) wipe and regenerate this page's render cache go to subtopics go to comments (messages) go to page author's home page, if possible n next page p previous page u up to parent page in edit form: s save changes p preview when viewing diffs: n next edit p previous edit """)
def reparent(self, parents=[], REQUEST=None, pagename=None): """ Move this page under the named parent pages in the wiki outline. parent page names may be passed in several ways: - in the parents argument (a list or string of page names) - in the pagename argument (a single name) (this last is to support the page management form). Page names may be ids, or fuzzy, even partial. Any which do not resolve to an existing page or are duplicates will be ignored. """ if not self.checkSufficientId(REQUEST): return self.denied( _("Sorry, this wiki doesn't allow anonymous reparenting. Please configure a username in options first.")) oldparents = self.getParents() # clean the arguments carefully to avoid parenting anomalies # page mgmt form must use pagename field: if pagename: parents = [pagename] # or parents might be a string elif type(parents) != ListType: parents = [parents] # empty strings are common, remove before calling pageWithFuzzyName parents = filter(lambda x:x, parents) # look up the page (brain) corresponding to each (fuzzy) parent name parents = map(lambda x:self.pageWithFuzzyName(x,allow_partial=1), parents) # strip out Nones (pages not found) parents = filter(lambda x:x, parents) # convert back to proper page names parents = map(lambda x:absattr(x.Title), parents) # remove any duplicates uniqueparents = [] for p in parents: if not p in uniqueparents: uniqueparents.append(p) # finally - update our parents property, the outline cache, and catalog self.setParents(uniqueparents) self.wikiOutline().reparent(self.pageName(),uniqueparents) self.index_object() # send mail if appropriate self.sendMailToEditSubscribers( '%s was reparented from %s to %s.' % ( self.pageName(), oldparents, uniqueparents), REQUEST=REQUEST, subject='(reparented)') if REQUEST is not None: REQUEST.RESPONSE.redirect(REQUEST['URL1'])
def editConflictDialog(self): """ web page displayed in edit conflict situations. """ #XXX form = self.getMessageDialog('editconflict') #XXX form = self.getSkinTemplate('editconflict') titlestr=_('Edit conflict') return MessageDialog( title=titlestr, message=""" <b>%s</b> <p> %s. %s: <ol> <li>%s <li>%s <li>%s <li>%s <li>%s. </ol> %s, <p> %s. """ % ( titlestr, _("Someone else has saved this page while you were editing"), _("To resolve the conflict, do this"), _("Click your browser's back button"), _("Copy your recent edits to the clipboard"), _("Click your browser's refresh button"), _("Paste in your edits again, being mindful of the latest changes"), _("Click the Change button again"), _("or"), _("To discard your changes and start again, click OK"), ), action=self.pageUrl()+'/editform')
def fromHeader(self,REQUEST=None): """ Give the appropriate From: header for mail-outs from this page. Tries to give the best attribution based on configuration and available information. XXX todo: use an authenticated CMF member's email property """ address = (self.fromProperty() or #self.usersEmailAddress() or self.replyToProperty()) # splitlines to fend off header injection attacks from spammers lines = self.usernameFrom(REQUEST,ip_address=0).splitlines() realname = lines and lines[0] or _('anonymous') return '%s (%s)' % (address, realname)
def createform(self, REQUEST=None, page=None, text=None, pagename=None): """ Render the create form (template-customizable). This usually just calls editform; it is protected by a different permission and also allows an alternate pagename argument to support the page management form (XXX temporary). It may also be customized by a createform skin template, in which case page creation and page editing forms are different. """ if not self.checkSufficientId(REQUEST): return self.denied( _("Sorry, this wiki doesn't allow anonymous edits. Please configure a username in options first.")) if self.hasSkinTemplate('createform'): return self.getSkinTemplate('createform')( REQUEST, page or pagename, text) else: return self.editform( REQUEST, page or pagename, text, action='Create')
def editform(self, REQUEST=None, page=None, text=None, action='Change'): """ Render the edit form (template-customizable). This is usually called by createform also, and can handle both editing and creating. The form's textarea contents may be specified. """ if not self.checkSufficientId(REQUEST): return self.denied( _("Sorry, this wiki doesn't allow anonymous edits. Please configure a username in options first.")) if ((not page or page == self.pageName()) and hasattr(self,'wl_isLocked') and self.wl_isLocked()): return self.davLockDialog() # what are we going to do ? set up page, text & action accordingly if page is None: # no page specified - editing the current page page = self.pageName() text = self.read() elif self.pageWithName(page): # editing a different page text = self.pageWithName(page).read() else: # editing a brand-new page action = 'Create' text = text or '' # display the edit form - a dtml method or the builtin default # NB we redefine id as a convenience, so that one header can work # for pages and editforms # XXX can we simplify this/make dtml more version independent ? # NB 'id' and 'oldid' are no longer used, but provide them for # backwards compatibility with old templates return self.getSkinTemplate('editform')(self,REQUEST, page=page, text=text, action=action, id=page, oldid=self.id())
def manage_addWiki(self, new_id, new_title='', wiki_type='zwikidotorg', REQUEST=None, enter=0): """ Create a new zwiki web of the specified type """ if not REQUEST: REQUEST = self.REQUEST # check for a configuration wizard if hasattr(self,wiki_type+'_config'): REQUEST.RESPONSE.redirect('%s/%s_config?new_id=%s&new_title=%s' % \ (REQUEST['URL1'], wiki_type, urllib.quote(new_id), urllib.quote(new_title))) else: if wiki_type in self.listFsWikis(): self.addWikiFromFs(new_id,new_title,wiki_type,REQUEST) elif wiki_type in self.listZodbWikis(): self.addWikiFromZodb(new_id,new_title,wiki_type,REQUEST) else: return MessageDialog( title=_('Unknown wiki type'), message='', action='' ) if REQUEST is not None: folder = getattr(self, new_id) if hasattr(folder, 'setup_%s'%(wiki_type)): REQUEST.RESPONSE.redirect(REQUEST['URL3']+'/'+new_id+'/setup_%s'%(wiki_type)) elif enter: # can't see why this doesn't work after addWikiFromFs #REQUEST.RESPONSE.redirect(getattr(self,new_id).absolute_url()) REQUEST.RESPONSE.redirect(REQUEST['URL3']+'/'+new_id+'/') else: try: u=self.DestinationURL() except: u=REQUEST['URL1'] REQUEST.RESPONSE.redirect(u+'/manage_main?update_menu=1')
def upgradeAll(self,render=1,batch=0,REQUEST=None): """ Update, upgrade, pre-render and re-index all pages and data structures. Normally individual pages are upgraded and pre-rendered on demand, when viewed. An administrator may want to do this for all pages ahead of time, particularly after a zwiki upgrade, to ensure all pages have the latest properties and have been rendered by the latest code, minimizing delay and possible problems later on. Also installs a wiki catalog if not present, re-indexes each page, validates page parents, and rebuilds the wiki outline cache. Also installs the index_html and standard_error_message dtml methods. XXX split ? You can set render=0 to skip the page pre-rendering part, completing much faster on large wikis. The optional batch argument forces a commit every N pages. This may be useful to get a complete run in large/busy wikis, which can be difficult due to conflict errors or memory usage. Requires 'Manage properties' permission on the folder. """ if not self.checkPermission(Permissions.manage_properties, self.folder()): raise 'Unauthorized', ( _('You are not authorized to upgrade all pages.') + \ _('(folder -> Manage properties)')) batch = int(batch) if render: BLATHER('upgrading/reindexing/pre-rendering all pages:') else: BLATHER('upgrading/reindexing all pages:') starttime = clock() n, total = 0, self.pageCount() self.setupCatalog(reindex=0) for p in self.pageObjects(): # poor caching (not a problem here) n += 1 try: p.upgrade(REQUEST) p.upgradeId(REQUEST) p.fixPageEncoding() if render: p.preRender(clear_cache=1) msg = 'upgraded and pre-rendered page' else: msg = 'upgraded page' # make sure every page is cataloged - slow but thorough p.index_object(log=0) BLATHER('%s %d/%d %s'%(msg,n,total,p.id())) except: BLATHER('failed to upgrade page %d/%d %s: %s' \ % (n,total,p.id(),formattedTraceback())) if batch and n % batch == 0: BLATHER('committing') get_transaction().commit() self.updateWikiOutline() self.setupDtmlMethods() endtime = clock() BLATHER('upgrade complete, %d pages processed in %fs, %f pages/s' \ %(n, endtime-starttime, n/(endtime-starttime)))
def asAgeString(self,time): """ return a string describing the approximate elapsed period since time time may be a DateTime or suitable string. Returns a blank string if there was a problem. Based on the dtml version in ZwikiTracker. """ if not time: return 'some time' if type(time) is StringType: time = DateTime(time) # didn't work on a page in CMF, perhaps due to skin acquisition magic #elapsed = self.ZopeTime() - time elapsed = self.getPhysicalRoot().ZopeTime() - time hourfactor=0.041666666666666664 minutefactor=0.00069444444444444447 secondsfactor=1.1574074074074073e-05 days=int(math.floor(elapsed)) weeks=days/7 months=days/30 years=days/365 hours=int(math.floor((elapsed-days)/hourfactor)) minutes=int(math.floor((elapsed-days-hourfactor*hours)/minutefactor)) seconds=int(round(( elapsed-days-hourfactor*hours-minutefactor*minutes)/secondsfactor)) datepattern = ("%(nb)d %(period)s") if years: s = datepattern % {"nb": years, "period": years > 1 and _('years') or _('year')} elif months: s = datepattern % {"nb":months, "period":months > 1 and _('months') or _('month')} elif weeks: s = datepattern % {"nb":weeks, "period":weeks > 1 and _('weeks') or _('week')} elif days: s = datepattern % {"nb":days, "period": days > 1 and _('days') or _('day') } elif hours: s = datepattern % {"nb":hours, "period":hours > 1 and _('hours') or _('hour')} elif minutes: s = datepattern % {"nb":minutes, "period":minutes > 1 and _('minutes') or _('minute')} else: s = datepattern % {"nb":seconds, "period":seconds > 1 and _('seconds') or _('second')} return s