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.
>>> print startPage.render()
visiting document
visiting paragraph
visiting strong
visiting #text
<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
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'])

View file

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

View file

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

View file

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

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

View file

@ -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.
"""

View file

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