work in progress: link processing

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@3153 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2009-01-17 16:51:09 +00:00
parent 5c5260c3e3
commit 28c88e7176
8 changed files with 188 additions and 50 deletions

View file

@ -4,6 +4,9 @@ Yet Another WikiWiki Framework
($Id$)
>>> from zope import component
>>> from zope.publisher.browser import TestRequest
An Example for an Elementary Wiki Structure
===========================================
@ -26,9 +29,12 @@ We format the content of the start page using the restructured text format.
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()
>>> print startPage.render(TestRequest())
<p><strong>Welcome to the Demo Wiki</strong></p>
Links to existing pages
-----------------------
We now create another page that contains a link to the start page.
>>> aboutPage = wiki.createPage('about')
@ -40,11 +46,36 @@ We now create another page that contains a link to the start page.
... `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>
>>> print aboutPage.render(TestRequest())
<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>
<p><a class="reference"
href="http://127.0.0.1/demo_wiki/start_page">Back to the Start Page</a></p>
Let's now have a look at the link manager - it should have recorded the link
from the page content.
>>> from cybertools.wiki.interfaces import ILinkManager
>>> linkManager = manager.getPlugin(ILinkManager, 'basic')
>>> links = linkManager.links
>>> len(links)
1
>>> link = links.values()[0]
>>> link.source, link.target, link.name, link.refuri
(0, 1, u'start_page', 'http://127.0.0.1/demo_wiki/start_page')
Links to not yet existing pages
-------------------------------
>>> aboutPage.text += '''
... `More... <more>`_
... '''
>>> print aboutPage.render(TestRequest())
<p><strong>Information about the Demo Wiki</strong></p>
<p>This is the cybertools demo wiki.</p>
<p><a class="reference"
href="http://127.0.0.1/demo_wiki/start_page">Back to the Start Page</a></p>
<p><a class="reference"
href="http://127.0.0.1/demo_wiki/create.html?linkid=0000002">?More...</a></p>

View file

@ -40,7 +40,7 @@ class BaseConfiguration(object):
def getConfig(self, functionality):
c = self.get(functionality)
if c is None:
parent = self.getParent()
parent = self.getConfigParent()
if parent is not None:
return parent.getConfig(functionality)
return c
@ -48,7 +48,7 @@ class BaseConfiguration(object):
def get(self, key, default=None):
return getattr(self, key, None)
def getParent(self):
def getConfigParent(self):
return self.parent

View file

@ -22,9 +22,11 @@ Basic (sample) implementations for links and link management
$Id$
"""
from docutils.nodes import Text
from zope.interface import implements
from zope.traversing.browser import absoluteURL
from cybertools.wiki.interfaces import ILink, ILinkManager
from cybertools.wiki.interfaces import ILink, ILinkManager, ILinkProcessor
class LinkManager(object):
@ -41,15 +43,31 @@ class LinkManager(object):
link = Link(name, source, target, **kw)
link.manager = self
id = self.generateLinkIdentifier(link)
self.linksBySource[source] = self.links[id] = link
self.links[id] = link
self.linksBySource.setdefault(source, []).append(link)
return link
def removeLink(self, link):
if link.identifier in self.links:
link.manager = None
del self.links[link.identifier]
def query(self, source=None, target=None, name=None, **kw):
if source is None:
result = self.links.values()
else:
result = self.linksBySource.get(source, [])
kw.update(dict(target=target, name=name))
for k, v in kw.items():
if v is None:
continue
if not isinstance(v, (list, tuple)):
v = [v]
result = [r for r in result if getattr(r, k) in v]
return result
def generateLinkIdentifier(self, link):
identifier = 'l%07i' % (max(self.links.keys() or [0]) + 1)
identifier = '%07i' % (max([int(k) for k in self.links.keys()] or [0]) + 1)
link.identifier = identifier
return identifier
@ -76,5 +94,51 @@ class Link(object):
def __getattr__(self, attr):
if attr not in ILink:
raise AttributeError(attr)
return getattr(self, attr, None)
return self.__dict__.get(attr)
class LinkProcessor(object):
implements(ILinkProcessor)
parent = None # parent (tree) processor
def __init__(self, context):
self.node = self.context = context
def getProperties(self):
raise ValueError("Method 'getProperties()' must be implemented by subclass.")
def process(self):
#print 'processing reference:', self.node
props = self.getProperties()
source = self.parent.context
wiki = source.getWiki()
manager = wiki.getManager()
sourceUid = manager.getUid(source)
name = props['targetName']
lmName = source.getConfig('linkManager')
lm = wiki.getManager().getPlugin(ILinkManager, lmName)
existing = lm.query(source=sourceUid, name=name)
if existing:
link = existing[0]
target = manager.getObject(link.target)
else:
target = wiki.getPage(name)
targetUid = manager.getUid(target)
link = lm.createLink(name, sourceUid, targetUid)
if link.refuri is None:
request = self.parent.request
if request is not None:
if target is None:
link.refuri = '%s/create.html?linkid=%s' % (
absoluteURL(wiki, request), link.identifier)
else:
link.refuri = absoluteURL(target, request)
self.setProperty('refuri', link.refuri)
if target is None:
# change CSS class, link text
# needs overriding of HTMLTranslator.visit_reference()
self.setProperty('class', 'create') # no direct effect
self.node.insert(0, Text('?'))

View file

@ -38,7 +38,7 @@ class TreeProcessor(object):
implements(ITreeProcessor)
adapts(IWikiPage)
tree = None
tree = request = None
visitor = None
def __init__(self, context):
@ -54,20 +54,17 @@ class Visitor(object):
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
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)
procs = []
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

@ -24,6 +24,7 @@ $Id$
from zope import component
from zope.interface import implements
from zope.app.intid.interfaces import IIntIds
from cybertools.wiki.interfaces import IWikiConfiguration
from cybertools.wiki.interfaces import IWikiManager, IWiki, IWikiPage
@ -57,9 +58,15 @@ class WikiManager(BaseConfiguration):
def getPlugin(self, type, name):
return component.getUtility(type, name=name)
def getUid(self, obj):
return component.getUtility(IIntIds).getId(obj)
def getObject(self, uid):
return component.getUtility(IIntIds).getObject(uid)
# configuration
def getParent(self):
def getConfigParent(self):
return component.getUtility(IWikiConfiguration)
@ -96,7 +103,7 @@ class Wiki(BaseConfiguration):
# configuration
def getParent(self):
def getConfigParent(self):
return self.getManager()
@ -114,10 +121,10 @@ class WikiPage(BaseConfiguration):
def getWiki(self):
return self.wiki
def render(self):
def render(self, request=None):
source = self.preprocess(self.text)
tree = self.parse(source)
self.process(tree)
self.process(tree, request)
result = self.write(tree)
return self.postprocess(result)
@ -134,10 +141,11 @@ class WikiPage(BaseConfiguration):
def preprocess(self, source):
return source
def process(self, tree):
def process(self, tree, request=None):
processor = component.getAdapter(self, ITreeProcessor,
name=self.getConfig('processor'))
processor.tree = tree
processor.request = request
processor.process()
def postprocess(self, result):
@ -145,6 +153,6 @@ class WikiPage(BaseConfiguration):
# configuration
def getParent(self):
def getConfigParent(self):
return self.getWiki()

View file

@ -26,26 +26,15 @@ from docutils.nodes import reference
from zope.interface import implements
from zope.component import adapts
from cybertools.wiki.interfaces import INodeProcessor, ILinkManager
from cybertools.wiki.base.link import LinkProcessor
class Reference(object):
class Reference(LinkProcessor):
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)
def getProperties(self):
return dict(targetName=self.node['refuri'])
def setProperty(self, name, value):
self.node[name] = value

View file

@ -35,7 +35,7 @@ class IWikiConfiguration(Interface):
parser = Attribute('Plug-in component converting from text input '
'format to internal tree format.')
def getParent():
def getConfigParent():
""" Return the parent object in case this configuration does not
provide configuration information for a certain functionality.
"""
@ -66,6 +66,13 @@ class IWikiManager(Interface):
""" Return the plugin of the type given with the name given.
"""
def getUid(obj):
""" Return the unique id of the object given.
"""
def getObject(uid):
""" Return the object referenced by the unique id given.
"""
class IWiki(Interface):
""" A collection of wiki pages, or - more generally - wiki components.
@ -111,7 +118,7 @@ class IWikiPage(Interface):
""" The wiki this page belongs to.'
"""
def render():
def render(request=None):
""" Convert the source text of the page to presentation format.
"""
@ -127,7 +134,7 @@ class IWikiPage(Interface):
""" Modify the source text of the page before parsing it and return it.
"""
def process(tree):
def process(tree, request=None):
""" Scan the tree, changing it if necessary and collecting
interesting information about the nodes, e.g. about links.
"""
@ -162,6 +169,8 @@ class ITreeProcessor(Interface):
context = Attribute('The wiki page from which the tree was generated.')
tree = Attribute('The tree to be processed.')
request = Attribute('An optional request object, needed e.g. for '
'rendering absolute URLs.')
def process():
""" Do what is necessary.
@ -179,6 +188,11 @@ class INodeProcessor(Interface):
""" Do what is necessary.
"""
def getProperties():
""" Return a dictionary of properties provided by the context
node that may be needed for processing.
"""
# wiki elements
@ -187,7 +201,7 @@ class ILinkManager(Interface):
"""
def createLink(name, source, target, **kw):
""" Create and register a link record.
""" Create, register, and return a link record.
Optional attributes are given as keyword arguments.
"""
@ -231,6 +245,7 @@ class ILink(Interface):
'for external links - the target URI.')
targetFragment = Attribute('Optional: an address part leading to a '
'text anchor or the part of an image.')
refuri = Attribute('The URI linking to the target object.')
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.')
@ -238,3 +253,9 @@ class ILink(Interface):
def getManager():
""" Return the link manager this link is managed by.
"""
class ILinkProcessor(INodeProcessor):
""" A node processor specialized on links (references).
"""

View file

@ -9,13 +9,38 @@ $Id$
import unittest, doctest
from zope.testing.doctestunit import DocFileSuite
from zope import component
from zope.interface import implements
from zope.app.intid.interfaces import IIntIds
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.traversing.browser.interfaces import IAbsoluteURL
from cybertools.relation.tests import IntIdsStub
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
from cybertools.wiki.interfaces import IWiki, IWikiPage
class WikiURL(object):
implements(IAbsoluteURL)
def __init__(self, context, request):
self.context = context
self.request = request
def __call__(self):
return '%s/%s' % (self.request.URL, self.context.name)
class PageURL(WikiURL):
def __call__(self):
return '%s/%s' % (WikiURL(self.context.getWiki(), self.request)(),
self.context.name)
class Test(unittest.TestCase):
@ -26,6 +51,9 @@ class Test(unittest.TestCase):
def setUp(testCase):
component.provideAdapter(WikiURL, (IWiki, IBrowserRequest), IAbsoluteURL)
component.provideAdapter(PageURL, (IWikiPage, IBrowserRequest), IAbsoluteURL)
component.provideUtility(IntIdsStub())
component.provideUtility(WikiConfiguration())
component.provideUtility(DocutilsHTMLWriter(), name='docutils.html')
component.provideUtility(DocutilsRstxParser(), name='docutils.rstx')