From aa43e289f80fe9ede9233c97de1b0bc5214d5716 Mon Sep 17 00:00:00 2001 From: helmutm Date: Wed, 14 Jan 2009 20:06:11 +0000 Subject: [PATCH] set up node processing; provide link manager, more details for link interface git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@3150 fd906abe-77d9-0310-91a1-e0d9ade77398 --- wiki/README.txt | 22 +++++++-- wiki/base/config.py | 12 ++++- wiki/base/link.py | 32 +++++++++---- wiki/base/process.py | 29 ++++++++++-- wiki/base/wiki.py | 31 +++++++++++- wiki/dcu/process.py | 51 ++++++++++++++++++++ wiki/dcu/rstx.py | 2 +- wiki/interfaces.py | 109 +++++++++++++++++++++++++++++++++---------- wiki/tests.py | 4 ++ 9 files changed, 244 insertions(+), 48 deletions(-) create mode 100644 wiki/dcu/process.py diff --git a/wiki/README.txt b/wiki/README.txt index 4313bc8..ac382d6 100644 --- a/wiki/README.txt +++ b/wiki/README.txt @@ -27,10 +27,24 @@ The parser for restructured text and a corresponding HTML writer are the default plugins used, so we can already render the page as HTML. >>> print startPage.render() - visiting document - visiting paragraph - visiting strong - visiting #text

Welcome to the Demo Wiki

+We now create another page that contains a link to the start page. + + >>> aboutPage = wiki.createPage('about') + >>> aboutPage.text = ''' + ... **Information about the Demo Wiki** + ... + ... This is the cybertools demo wiki. + ... + ... `Back to the Start Page `_ + ... ''' + + >>> print aboutPage.render() + processing reference: + Back to the Start Page +

Information about the Demo Wiki

+

This is the cybertools demo wiki.

+

Back to the Start Page

diff --git a/wiki/base/config.py b/wiki/base/config.py index 4a67026..97ac548 100644 --- a/wiki/base/config.py +++ b/wiki/base/config.py @@ -38,11 +38,16 @@ class BaseConfiguration(object): writer = parser = None def getConfig(self, functionality): - c = getattr(self, functionality, None) + c = self.get(functionality) if c is None: - return self.getParent().getConfig(functionality) + parent = self.getParent() + if parent is not None: + return parent.getConfig(functionality) return c + def get(self, key, default=None): + return getattr(self, key, None) + def getParent(self): return self.parent @@ -54,3 +59,6 @@ class WikiConfiguration(BaseConfiguration): writer = 'docutils.html' parser = 'docutils.rstx' processor = 'standard' + linkManager = 'basic' + + nodeProcessors = dict(reference=['default']) diff --git a/wiki/base/link.py b/wiki/base/link.py index d0c3f1e..e2b3d82 100644 --- a/wiki/base/link.py +++ b/wiki/base/link.py @@ -35,18 +35,18 @@ class LinkManager(object): def __init__(self): self.links = {} + self.linksBySource = {} - def registerLink(self, link): - if link.identifier is None: - self.generateLinkIdentifier(link) - if link.identifier not in self.links: - self.links[link.identifier] = link - link.manager = self + def createLink(self, name, source, target, **kw): + link = Link(name, source, target, **kw) + link.manager = self + id = self.generateLinkIdentifier(link) + self.linksBySource[source] = self.links[id] = link - def unregisterLink(self, link): + def removeLink(self, link): if link.identifier in self.links: - del self.links[link.identifier] link.manager = None + del self.links[link.identifier] def generateLinkIdentifier(self, link): identifier = 'l%07i' % (max(self.links.keys() or [0]) + 1) @@ -60,9 +60,21 @@ class Link(object): identifier = None manager = None - formatName = None - def __init__(self, source, target): + def __init__(self, name, source, target, **kw): + self.name = name self.source = source self.target = target + for k, v in kw.items(): + if k not in ILink: + raise AttributeError(k) + setattr(self, k, v) + + def getManager(self): + return self.manager + + def __getattr__(self, attr): + if attr not in ILink: + raise AttributeError(attr) + return getattr(self, attr, None) diff --git a/wiki/base/process.py b/wiki/base/process.py index dcd2655..0a1438c 100644 --- a/wiki/base/process.py +++ b/wiki/base/process.py @@ -24,8 +24,10 @@ $Id$ from zope.interface import implements from zope.component import adapts +from zope import component -from cybertools.wiki.interfaces import ITreeProcessor, IWikiPage +from cybertools.wiki.interfaces import ITreeProcessor, INodeProcessor +from cybertools.wiki.interfaces import IWikiPage class TreeProcessor(object): @@ -37,18 +39,35 @@ class TreeProcessor(object): adapts(IWikiPage) tree = None + visitor = None def __init__(self, context): self.context = context def process(self): - self.tree.walk(Visitor(self.tree)) + self.visitor = visitor = Visitor(self) + self.tree.walk(visitor) class Visitor(object): - def __init__(self, document): - self.document = document + def __init__(self, context): + self.context = context # the tree processor + self.document = context.tree # needed internally + self.processors = {} # cache + self.processorNames = self.context.context.getConfig('nodeProcessors') def dispatch_visit(self, node): - print 'visiting', node.tagname + #print 'visiting', node.tagname + tag = node.tagname + procs = self.processors.get(tag) + if procs is None: + procs = self.processors[tag] = [] + procNames = self.processorNames.get(tag, []) + for n in procNames: + proc = component.queryAdapter(node, INodeProcessor, name=n) + if proc is not None: + proc.parent = self.context + procs.append(proc) + for p in procs: + p.process() diff --git a/wiki/base/wiki.py b/wiki/base/wiki.py index 79bc4f6..bdf0c36 100644 --- a/wiki/base/wiki.py +++ b/wiki/base/wiki.py @@ -46,6 +46,17 @@ class WikiManager(BaseConfiguration): wiki.manager = self return wiki + def removeWiki(self, wiki): + name = wiki.name + if name in self.wikis: + del self.wikis[name] + + def listWikis(self): + return self.wikis.values() + + def getPlugin(self, type, name): + return component.getUtility(type, name=name) + # configuration def getParent(self): @@ -63,6 +74,9 @@ class Wiki(BaseConfiguration): self.title = title or name self.pages = {} + def getManager(self): + return self.manager + def createPage(self, name, title=None): if name in self.pages: raise ValueError("Name '%s' already present." % name) @@ -70,10 +84,20 @@ class Wiki(BaseConfiguration): page.wiki = self return page + def removePage(self, name): + if name in self.pages: + del self.pages[name] + + def getPage(self, name): + return self.pages.get(name) + + def listPages(self): + return self.pages.values() + # configuration def getParent(self): - return self.manager + return self.getManager() class WikiPage(BaseConfiguration): @@ -87,6 +111,9 @@ class WikiPage(BaseConfiguration): self.name = name self.title = title or name + def getWiki(self): + return self.wiki + def render(self): source = self.preprocess(self.text) tree = self.parse(source) @@ -119,5 +146,5 @@ class WikiPage(BaseConfiguration): # configuration def getParent(self): - return self.wiki + return self.getWiki() diff --git a/wiki/dcu/process.py b/wiki/dcu/process.py new file mode 100644 index 0000000..62ada95 --- /dev/null +++ b/wiki/dcu/process.py @@ -0,0 +1,51 @@ +# +# Copyright (c) 2009 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Node processor implementations for docutils nodes. + +$Id$ +""" + +from docutils.nodes import reference +from zope.interface import implements +from zope.component import adapts + +from cybertools.wiki.interfaces import INodeProcessor, ILinkManager + + +class Reference(object): + + implements(INodeProcessor) + adapts(reference) + + parent = None # parent (tree) processor + + def __init__(self, context): + self.node = self.context = context + + def process(self): + print 'processing reference:', self.node + source = self.parent.context + wiki = source.getWiki() + sourceName = ':'.join((wiki.name, source.name)) + targetName = self.node['refuri'] + lmName = source.getConfig('linkManager') + lm = wiki.getManager().getPlugin(ILinkManager, lmName) + target = wiki.getPage(targetName) + diff --git a/wiki/dcu/rstx.py b/wiki/dcu/rstx.py index eef50d0..a8cf42a 100644 --- a/wiki/dcu/rstx.py +++ b/wiki/dcu/rstx.py @@ -17,7 +17,7 @@ # """ -A Wiki manager managing wikis and wiki-related objects, esp plugins. +A parser implementation based on the docutils restructured text parser. $Id$ """ diff --git a/wiki/interfaces.py b/wiki/interfaces.py index 2506142..3073125 100644 --- a/wiki/interfaces.py +++ b/wiki/interfaces.py @@ -35,23 +35,21 @@ class IWikiConfiguration(Interface): parser = Attribute('Plug-in component converting from text input ' 'format to internal tree format.') - def getConfig(functionality): - """ Return the name of the plugin that should used for the - functionality given. - """ - def getParent(): """ Return the parent object in case this configuration does not provide configuration information for a certain functionality. """ + def getConfig(functionality): + """ Return the name of the plugin that should used for the + functionality given. + """ + class IWikiManager(Interface): """ Manages wikis and wiki-related objects, like plugins. """ - wikis = Attribute('A collection of wikis managed by this object.') - def addWiki(wiki): """ Register the wiki given. """ @@ -60,15 +58,26 @@ class IWikiManager(Interface): """ Remove the wiki given from the list of wikis. """ + def listWikis(): + """ Return a collection of wikis managed by this object. + """ + + def getPlugin(type, name): + """ Return the plugin of the type given with the name given. + """ + class IWiki(Interface): """ A collection of wiki pages, or - more generally - wiki components. """ - manager = Attribute('The wiki manager this wiki is managed by.') name = Attribute('The name or address of the wiki unique within the ' 'scope of the wiki manager.') - pages = Attribute('A collection of the pages belonging to this wiki.') + pages = Attribute('') + + def getManager(): + """ Return the wiki manager this wiki is managed by. + """ def createPage(name, title=None): """ Create a new wiki page identified by the name (address - @@ -80,17 +89,28 @@ class IWiki(Interface): all information related to the page. """ + def getPage(name): + """ Return the page with the name given or None if not present. + """ + + def listPages(): + """ Return a collection of the pages belonging to this wiki. + """ + class IWikiPage(Interface): """ An object representing a page of a wiki. """ - wiki = Attribute('The wiki this page belongs to.') name = Attribute('A page name or address unique within the wiki.') title = Attribute('A short string describing the wiki page the may be ' 'use as a page title.') text = Attribute('The page content in input text format.') + def getWiki(): + """ The wiki this page belongs to.' + """ + def render(): """ Convert the source text of the page to presentation format. """ @@ -148,32 +168,73 @@ class ITreeProcessor(Interface): """ +class INodeProcessor(Interface): + """ Processes a document tree. + """ + + context = Attribute('The node to be processed.') + parent = Attribute('The parent (=tree) processor.') + + def process(): + """ Do what is necessary. + """ + + # wiki elements class ILinkManager(Interface): - """Manages (and possibly contains) all kinds of wiki-related links. + """ Manages (and possibly contains) all kinds of wiki-related links. """ - def registerLink(link): - """Register (store) a link. + def createLink(name, source, target, **kw): + """ Create and register a link record. + + Optional attributes are given as keyword arguments. """ - def unregisterLink(link): - """Remove a link. + def removeLink(link): + """ Remove a link. """ + def getLink(identifier): + """ Return the link record identfied by the identifier given or None if + not present. + """ + + def query(source=None, target=None, name=None, **kw): + """ Search for link records matching the criteria given. One of + source or target must be given, the other one and name are optional. + + Additional (optional) criteria may be supported by the implementation. + """ -# TODO: convert the following stuff so that it fits in the parser/writer -# paradigm. class ILink(Interface): - """A hyperlink between two local or foreign objects. + """ A hyperlink between two local or foreign objects. + + There may be more than one link records with the same name + that represent the real link at different times or under + different conditions. """ - identifier = Attribute('Identifier of the link, unique within its link manager.') - manager = Attribute('The manager that this link is managed by.') - formatName = Attribute('Name of the link format that identified the link.') - - source = Attribute('Link source.') - target = Attribute('Link target.') + identifier = Attribute('An internal identifier of the link record, ' + 'unique within the link manager.') + name = Attribute('The external identifier for the link, i.e. the ' + 'string used in the source text to address the link.') + title = Attribute('A short text, may be used as the default text for ' + 'the link or for the alt tag of an image. Could also serve ' + 'for identifying a new link.') + description = Attribute('Optional: some text, may be used as a title attribute.') + state = Attribute('A short string denoting the state of the link entry.') + source = Attribute('Identifier of the link\'s source object.') + target = Attribute('Identifier of the link\'s target object or - ' + 'for external links - the target URI.') + targetFragment = Attribute('Optional: an address part leading to a ' + 'text anchor or the part of an image.') + user = Attribute('Optional: a string denoting the creator of the record.') + run = Attribute('Optional: May be used to group the links from a certain ' + 'source at different times.') + def getManager(): + """ Return the link manager this link is managed by. + """ diff --git a/wiki/tests.py b/wiki/tests.py index 3702124..2c524a0 100755 --- a/wiki/tests.py +++ b/wiki/tests.py @@ -12,8 +12,10 @@ from zope import component from cybertools.wiki.base.config import WikiConfiguration from cybertools.wiki.base.process import TreeProcessor +from cybertools.wiki.base.link import LinkManager from cybertools.wiki.dcu.html import Writer as DocutilsHTMLWriter from cybertools.wiki.dcu.rstx import Parser as DocutilsRstxParser +from cybertools.wiki.dcu import process class Test(unittest.TestCase): @@ -28,6 +30,8 @@ def setUp(testCase): component.provideUtility(DocutilsHTMLWriter(), name='docutils.html') component.provideUtility(DocutilsRstxParser(), name='docutils.rstx') component.provideAdapter(TreeProcessor, name='standard') + component.provideAdapter(process.Reference, name='default') + component.provideUtility(LinkManager(), name='basic') def test_suite():