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:
parent
5c5260c3e3
commit
28c88e7176
8 changed files with 188 additions and 50 deletions
|
@ -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>
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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('?'))
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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).
|
||||
"""
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Add table
Reference in a new issue