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
This commit is contained in:
helmutm 2009-01-14 20:06:11 +00:00
parent 73ab6556d1
commit aa43e289f8
9 changed files with 244 additions and 48 deletions

View file

@ -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. default plugins used, so we can already render the page as HTML.
>>> print startPage.render() >>> print startPage.render()
visiting document
visiting paragraph
visiting strong
visiting #text
<p><strong>Welcome to the Demo Wiki</strong></p> <p><strong>Welcome to the Demo Wiki</strong></p>
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 <start_page>`_
... '''
>>> print aboutPage.render()
processing reference:
<reference name="Back to the Start Page"
refuri="start_page">Back to the Start Page</reference>
<p><strong>Information about the Demo Wiki</strong></p>
<p>This is the cybertools demo wiki.</p>
<p><a class="reference" href="start_page">Back to the Start Page</a></p>

View file

@ -38,11 +38,16 @@ class BaseConfiguration(object):
writer = parser = None writer = parser = None
def getConfig(self, functionality): def getConfig(self, functionality):
c = getattr(self, functionality, None) c = self.get(functionality)
if c is None: if c is None:
return self.getParent().getConfig(functionality) parent = self.getParent()
if parent is not None:
return parent.getConfig(functionality)
return c return c
def get(self, key, default=None):
return getattr(self, key, None)
def getParent(self): def getParent(self):
return self.parent return self.parent
@ -54,3 +59,6 @@ class WikiConfiguration(BaseConfiguration):
writer = 'docutils.html' writer = 'docutils.html'
parser = 'docutils.rstx' parser = 'docutils.rstx'
processor = 'standard' processor = 'standard'
linkManager = 'basic'
nodeProcessors = dict(reference=['default'])

View file

@ -35,18 +35,18 @@ class LinkManager(object):
def __init__(self): def __init__(self):
self.links = {} self.links = {}
self.linksBySource = {}
def registerLink(self, link): def createLink(self, name, source, target, **kw):
if link.identifier is None: link = Link(name, source, target, **kw)
self.generateLinkIdentifier(link) link.manager = self
if link.identifier not in self.links: id = self.generateLinkIdentifier(link)
self.links[link.identifier] = link self.linksBySource[source] = self.links[id] = link
link.manager = self
def unregisterLink(self, link): def removeLink(self, link):
if link.identifier in self.links: if link.identifier in self.links:
del self.links[link.identifier]
link.manager = None link.manager = None
del self.links[link.identifier]
def generateLinkIdentifier(self, link): def generateLinkIdentifier(self, link):
identifier = 'l%07i' % (max(self.links.keys() or [0]) + 1) identifier = 'l%07i' % (max(self.links.keys() or [0]) + 1)
@ -60,9 +60,21 @@ class Link(object):
identifier = None identifier = None
manager = None manager = None
formatName = None
def __init__(self, source, target): def __init__(self, name, source, target, **kw):
self.name = name
self.source = source self.source = source
self.target = target 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)

View file

@ -24,8 +24,10 @@ $Id$
from zope.interface import implements from zope.interface import implements
from zope.component import adapts 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): class TreeProcessor(object):
@ -37,18 +39,35 @@ class TreeProcessor(object):
adapts(IWikiPage) adapts(IWikiPage)
tree = None tree = None
visitor = None
def __init__(self, context): def __init__(self, context):
self.context = context self.context = context
def process(self): def process(self):
self.tree.walk(Visitor(self.tree)) self.visitor = visitor = Visitor(self)
self.tree.walk(visitor)
class Visitor(object): class Visitor(object):
def __init__(self, document): def __init__(self, context):
self.document = document 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): 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()

View file

@ -46,6 +46,17 @@ class WikiManager(BaseConfiguration):
wiki.manager = self wiki.manager = self
return wiki 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 # configuration
def getParent(self): def getParent(self):
@ -63,6 +74,9 @@ class Wiki(BaseConfiguration):
self.title = title or name self.title = title or name
self.pages = {} self.pages = {}
def getManager(self):
return self.manager
def createPage(self, name, title=None): def createPage(self, name, title=None):
if name in self.pages: if name in self.pages:
raise ValueError("Name '%s' already present." % name) raise ValueError("Name '%s' already present." % name)
@ -70,10 +84,20 @@ class Wiki(BaseConfiguration):
page.wiki = self page.wiki = self
return page 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 # configuration
def getParent(self): def getParent(self):
return self.manager return self.getManager()
class WikiPage(BaseConfiguration): class WikiPage(BaseConfiguration):
@ -87,6 +111,9 @@ class WikiPage(BaseConfiguration):
self.name = name self.name = name
self.title = title or name self.title = title or name
def getWiki(self):
return self.wiki
def render(self): def render(self):
source = self.preprocess(self.text) source = self.preprocess(self.text)
tree = self.parse(source) tree = self.parse(source)
@ -119,5 +146,5 @@ class WikiPage(BaseConfiguration):
# configuration # configuration
def getParent(self): def getParent(self):
return self.wiki return self.getWiki()

51
wiki/dcu/process.py Normal file
View file

@ -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)

View file

@ -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$ $Id$
""" """

View file

@ -35,23 +35,21 @@ class IWikiConfiguration(Interface):
parser = Attribute('Plug-in component converting from text input ' parser = Attribute('Plug-in component converting from text input '
'format to internal tree format.') 'format to internal tree format.')
def getConfig(functionality):
""" Return the name of the plugin that should used for the
functionality given.
"""
def getParent(): def getParent():
""" Return the parent object in case this configuration does not """ Return the parent object in case this configuration does not
provide configuration information for a certain functionality. 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): class IWikiManager(Interface):
""" Manages wikis and wiki-related objects, like plugins. """ Manages wikis and wiki-related objects, like plugins.
""" """
wikis = Attribute('A collection of wikis managed by this object.')
def addWiki(wiki): def addWiki(wiki):
""" Register the wiki given. """ Register the wiki given.
""" """
@ -60,15 +58,26 @@ class IWikiManager(Interface):
""" Remove the wiki given from the list of wikis. """ 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): class IWiki(Interface):
""" A collection of wiki pages, or - more generally - wiki components. """ 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 ' name = Attribute('The name or address of the wiki unique within the '
'scope of the wiki manager.') '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): def createPage(name, title=None):
""" Create a new wiki page identified by the name (address - """ Create a new wiki page identified by the name (address -
@ -80,17 +89,28 @@ class IWiki(Interface):
all information related to the page. 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): class IWikiPage(Interface):
""" An object representing a page of a wiki. """ 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.') name = Attribute('A page name or address unique within the wiki.')
title = Attribute('A short string describing the wiki page the may be ' title = Attribute('A short string describing the wiki page the may be '
'use as a page title.') 'use as a page title.')
text = Attribute('The page content in input text format.') text = Attribute('The page content in input text format.')
def getWiki():
""" The wiki this page belongs to.'
"""
def render(): def render():
""" Convert the source text of the page to presentation format. """ 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 # wiki elements
class ILinkManager(Interface): 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): def createLink(name, source, target, **kw):
"""Register (store) a link. """ Create and register a link record.
Optional attributes are given as keyword arguments.
""" """
def unregisterLink(link): def removeLink(link):
"""Remove a 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): 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.') identifier = Attribute('An internal identifier of the link record, '
manager = Attribute('The manager that this link is managed by.') 'unique within the link manager.')
formatName = Attribute('Name of the link format that identified the link.') name = Attribute('The external identifier for the link, i.e. the '
'string used in the source text to address the link.')
source = Attribute('Link source.') title = Attribute('A short text, may be used as the default text for '
target = Attribute('Link target.') '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.
"""

View file

@ -12,8 +12,10 @@ from zope import component
from cybertools.wiki.base.config import WikiConfiguration from cybertools.wiki.base.config import WikiConfiguration
from cybertools.wiki.base.process import TreeProcessor 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.html import Writer as DocutilsHTMLWriter
from cybertools.wiki.dcu.rstx import Parser as DocutilsRstxParser from cybertools.wiki.dcu.rstx import Parser as DocutilsRstxParser
from cybertools.wiki.dcu import process
class Test(unittest.TestCase): class Test(unittest.TestCase):
@ -28,6 +30,8 @@ def setUp(testCase):
component.provideUtility(DocutilsHTMLWriter(), name='docutils.html') component.provideUtility(DocutilsHTMLWriter(), name='docutils.html')
component.provideUtility(DocutilsRstxParser(), name='docutils.rstx') component.provideUtility(DocutilsRstxParser(), name='docutils.rstx')
component.provideAdapter(TreeProcessor, name='standard') component.provideAdapter(TreeProcessor, name='standard')
component.provideAdapter(process.Reference, name='default')
component.provideUtility(LinkManager(), name='basic')
def test_suite(): def test_suite():