work in progress: wiki implementation for Zope 2 based on generic object access interfaces
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@3610 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
a08922f990
commit
df079e9955
15 changed files with 482 additions and 10 deletions
|
@ -38,17 +38,36 @@ class WikiConfigInfo(dict):
|
|||
return self.get(attr, None)
|
||||
|
||||
|
||||
class BaseConfigurator(object):
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
def initialize(self):
|
||||
ci = WikiConfigInfo()
|
||||
self.context._configInfo = ci
|
||||
return ci
|
||||
|
||||
def getConfigInfo(self):
|
||||
return self.context._configInfo
|
||||
|
||||
|
||||
class BaseConfiguration(object):
|
||||
""" The base class for all wiki configuration implementations.
|
||||
"""
|
||||
|
||||
implements(IWikiConfiguration)
|
||||
|
||||
configurator = BaseConfigurator
|
||||
|
||||
_configInfo = None
|
||||
|
||||
def getConfigInfo(self):
|
||||
return self.configurator(self).getConfigInfo()
|
||||
|
||||
def getConfig(self, functionality):
|
||||
c = None
|
||||
ci = self._configInfo
|
||||
ci = self.getConfigInfo()
|
||||
if ci is not None:
|
||||
c = ci.get(functionality)
|
||||
if c is None:
|
||||
|
@ -58,9 +77,10 @@ class BaseConfiguration(object):
|
|||
return c
|
||||
|
||||
def setConfig(self, functionality, value):
|
||||
if self._configInfo is None:
|
||||
self._configInfo = WikiConfigInfo()
|
||||
self._configInfo.set(functionality, value)
|
||||
ci = self.getConfigInfo()
|
||||
if ci is None:
|
||||
ci = self.configurator(self).initialize()
|
||||
ci.set(functionality, value)
|
||||
|
||||
def getConfigParent(self):
|
||||
return None
|
||||
|
|
|
@ -38,15 +38,16 @@ class WikiManager(BaseConfiguration):
|
|||
|
||||
implements(IWikiManager)
|
||||
|
||||
def __init__(self):
|
||||
def setup(self):
|
||||
self.wikis = {}
|
||||
self.plugins = {}
|
||||
|
||||
def addWiki(self, wiki):
|
||||
name = wiki.name
|
||||
if name in self.wikis:
|
||||
raise ValueError("Wiki '%s' already registered." % name)
|
||||
self.wikis[name] = wiki
|
||||
wiki.manager = self
|
||||
wiki.setManager(self)
|
||||
return wiki
|
||||
|
||||
def removeWiki(self, wiki):
|
||||
|
@ -57,16 +58,25 @@ class WikiManager(BaseConfiguration):
|
|||
def listWikis(self):
|
||||
return self.wikis.values()
|
||||
|
||||
def getPlugin(self, type, name):
|
||||
def getPlugin(self, type, name=None):
|
||||
plugins = self.getPlugins()
|
||||
if (type, name) in plugins:
|
||||
plugin = plugins[(type, name)]
|
||||
if type is None:
|
||||
return plugin
|
||||
return type(plugin)
|
||||
return component.getUtility(type, name=name)
|
||||
|
||||
def getPlugins(self):
|
||||
return self.plugins
|
||||
|
||||
def getUid(self, obj):
|
||||
return component.getUtility(IIntIds).getId(obj)
|
||||
return self.getPlugin(IIntIds).register(obj)
|
||||
|
||||
def getObject(self, uid):
|
||||
obj = self.resolveUid(uid)
|
||||
if obj is None:
|
||||
return component.getUtility(IIntIds).getObject(int(uid))
|
||||
return self.getPlugin(IIntIds).getObject(int(uid))
|
||||
return obj
|
||||
|
||||
def resolveUid(self, uid):
|
||||
|
@ -92,10 +102,19 @@ class Wiki(BaseConfiguration):
|
|||
self.name = name
|
||||
self.title = title or name
|
||||
self.pages = {}
|
||||
self.setup()
|
||||
|
||||
def setup(self):
|
||||
self.getManager().addWiki(self)
|
||||
self.createPage('StartPage', u'Start Page',
|
||||
u'The text of the **Start Page**')
|
||||
|
||||
def getManager(self):
|
||||
return self.manager
|
||||
|
||||
def setManager(self, manager):
|
||||
self.manager = manager
|
||||
|
||||
def createPage(self, name, title=None):
|
||||
if name in self.pages:
|
||||
raise ValueError("Name '%s' already present." % name)
|
||||
|
@ -138,6 +157,10 @@ class WikiPage(BaseConfiguration):
|
|||
def __init__(self, name, title=None):
|
||||
self.name = name
|
||||
self.title = title or name
|
||||
self.setup()
|
||||
|
||||
def setup(self):
|
||||
pass
|
||||
|
||||
def getWiki(self):
|
||||
return self.wiki
|
||||
|
|
3
wiki/browser/__init__.py
Normal file
3
wiki/browser/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
$Id$
|
||||
"""
|
3
wiki/generic/__init__.py
Normal file
3
wiki/generic/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
$Id$
|
||||
"""
|
179
wiki/generic/wiki.py
Normal file
179
wiki/generic/wiki.py
Normal file
|
@ -0,0 +1,179 @@
|
|||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
"""
|
||||
Wiki implementation = adapters for Zope2 content objects.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from Acquisition import aq_inner, aq_parent
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
from BTrees.OOBTree import OOBTree
|
||||
from persistent.mapping import PersistentMapping
|
||||
from zope.app.intid import IntIds
|
||||
from zope.app.intid.interfaces import IIntIds
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope import component
|
||||
from zope.component import adapts
|
||||
from zope.interface import implements
|
||||
|
||||
from cybertools.util.generic.interfaces import IGenericObject, IGenericFolder
|
||||
from cybertools.wiki.base.config import WikiConfigInfo, BaseConfigurator
|
||||
from cybertools.wiki.base.wiki import WikiManager as BaseWikiManager
|
||||
from cybertools.wiki.base.wiki import Wiki as BaseWiki
|
||||
from cybertools.wiki.base.wiki import WikiPage as BaseWikiPage
|
||||
from cybertools.wiki.interfaces import ILinkManager, IWikiConfigInfo
|
||||
from cybertools.wiki.tracking.link import Link, TrackingStorage
|
||||
|
||||
|
||||
class PersistentConfigInfo(PersistentMapping):
|
||||
|
||||
implements(IWikiConfigInfo)
|
||||
|
||||
def set(self, functionality, value):
|
||||
self[functionality] = value
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return self.get(attr, None)
|
||||
|
||||
|
||||
class GenericConfigurator(BaseConfigurator):
|
||||
|
||||
def initialize(self):
|
||||
ci = PersistentConfigInfo()
|
||||
self.context.context.setGenericAttribute('configInfo', ci)
|
||||
return ci
|
||||
|
||||
def getConfigInfo(self):
|
||||
return self.context.context.getGenericAttribute('configInfo', None)
|
||||
|
||||
|
||||
class WikiManager(BaseWikiManager):
|
||||
|
||||
adapts(IGenericObject)
|
||||
|
||||
configurator = GenericConfigurator
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
def setup(self):
|
||||
self.context.setGenericAttribute('wikis', IOTreeSet())
|
||||
plugins = self.context.setGenericAttribute('plugins', PersistentMapping())
|
||||
plugins[(IIntIds, None)] = IntIds()
|
||||
linkStorage = TrackingStorage(trackFactory=Link)
|
||||
plugins[(ILinkManager, 'tracking')] = linkStorage
|
||||
self.setConfig('linkManager', 'tracking')
|
||||
|
||||
def addWiki(self, wiki):
|
||||
uid = self.getUid(wiki)
|
||||
self.wikiUids.insert(uid)
|
||||
wiki.setManager(self)
|
||||
return wiki
|
||||
|
||||
def removeWiki(self, wiki):
|
||||
uid = self.getUid(wiki)
|
||||
if uid in self.wikiUids:
|
||||
self.wikiUids.remove(uid)
|
||||
|
||||
def listWikis(self):
|
||||
for uid in self.wikiUids:
|
||||
yield self.getObject(uid)
|
||||
|
||||
@Lazy
|
||||
def wikiUids(self):
|
||||
return self.context.getGenericAttribute('wikis')
|
||||
|
||||
def getPlugins(self):
|
||||
return self.context.getGenericAttribute('plugins')
|
||||
|
||||
def getUid(self, obj):
|
||||
return super(WikiManager, self).getUid(obj.context)
|
||||
|
||||
def getObject(self, uid):
|
||||
obj = self.resolveUid(uid)
|
||||
if obj is None:
|
||||
co = super(WikiManager, self).getObject(uid)
|
||||
return co.typeInterface(co)
|
||||
return obj
|
||||
|
||||
|
||||
class Wiki(BaseWiki):
|
||||
|
||||
adapts(IGenericFolder)
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.context.getId()
|
||||
|
||||
@property
|
||||
def pages(self):
|
||||
# TODO: restrict to wiki page objects; use generic access methods
|
||||
return dict((k, WikiPage(v)) for k, v in self.context.objectItems())
|
||||
|
||||
def createPage(self, name, title, text=u''):
|
||||
obj = self.context.pageFactory(name)
|
||||
page = WikiPage(obj)
|
||||
page.title = title
|
||||
page.text = text
|
||||
self.context._setObject(name, obj)
|
||||
return getattr(self.context, name)
|
||||
|
||||
def getManager(self):
|
||||
# TODO: fetch tool/utility in a generic way
|
||||
co = self.context.portal_wikimanager
|
||||
return co.typeInterface(co)
|
||||
|
||||
def absolute_url(self):
|
||||
return self.context.absolute_url()
|
||||
|
||||
|
||||
class WikiPage(BaseWikiPage):
|
||||
|
||||
adapts(IGenericFolder)
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
def getTitle(self):
|
||||
return self.context.title
|
||||
def setTitle(self, title):
|
||||
self.context.title = title
|
||||
title = property(getTitle, setTitle)
|
||||
|
||||
def getText(self):
|
||||
return self.context.text
|
||||
def setText(self, text):
|
||||
if self.context.getProperty('text') is None:
|
||||
self.context.manage_addProperty('text', text, 'text')
|
||||
else:
|
||||
self.context.manage_changeProperties(id='text', value=text)
|
||||
text = property(getText, setText)
|
||||
|
||||
def getWiki(self):
|
||||
# TODO: fetch wiki in a generic way
|
||||
return Wiki(aq_parent(aq_inner(self.context)))
|
||||
#return Wiki(getParent(self.context))
|
||||
|
||||
def absolute_url(self):
|
||||
return self.context.absolute_url()
|
||||
|
|
@ -23,6 +23,7 @@ $Id$
|
|||
"""
|
||||
|
||||
from zope.interface import Interface, Attribute
|
||||
from zope import schema
|
||||
|
||||
|
||||
class IWikiConfigInfo(Interface):
|
||||
|
@ -96,6 +97,8 @@ class IWiki(Interface):
|
|||
""" A collection of wiki pages, or - more generally - wiki components.
|
||||
"""
|
||||
|
||||
title = schema.TextLine(title=u'Title')
|
||||
|
||||
name = Attribute('The name or address of the wiki unique within the '
|
||||
'scope of the wiki manager.')
|
||||
pages = Attribute('')
|
||||
|
|
|
@ -53,7 +53,7 @@ class Test(unittest.TestCase):
|
|||
def setUp(testCase):
|
||||
component.provideAdapter(WikiURL, (IWiki, IBrowserRequest), IAbsoluteURL)
|
||||
component.provideAdapter(PageURL, (IWikiPage, IBrowserRequest), IAbsoluteURL)
|
||||
component.provideUtility(IntIdsStub())
|
||||
#component.provideUtility(IntIdsStub())
|
||||
component.provideUtility(WikiConfiguration())
|
||||
component.provideUtility(DocutilsHTMLWriter(), name='docutils.html')
|
||||
component.provideUtility(DocutilsRstxParser(), name='docutils.rstx')
|
||||
|
@ -62,6 +62,8 @@ def setUp(testCase):
|
|||
component.provideAdapter(link.LinkManager)
|
||||
links = link.setupLinkManager(None)
|
||||
component.provideUtility(links, name='tracking')
|
||||
from cybertools.wiki.generic import wiki
|
||||
wiki.IntIds = IntIdsStub
|
||||
|
||||
|
||||
def test_suite():
|
||||
|
|
|
@ -135,6 +135,7 @@ class LinkManager(BaseLinkManager):
|
|||
return self.context
|
||||
|
||||
|
||||
# for testing only:
|
||||
def setupLinkManager(manager):
|
||||
ts = TrackingStorage(trackFactory=Link)
|
||||
return ILinkManager(ts)
|
||||
|
|
10
z2/README.txt
Normal file
10
z2/README.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
=================================================================
|
||||
Supporting the Zope2 Environment for Zope3/ZTK-based Applications
|
||||
=================================================================
|
||||
|
||||
($Id$)
|
||||
|
||||
>>> from zope import component
|
||||
>>> from zope.publisher.browser import TestRequest
|
||||
|
||||
|
3
z2/__init__.py
Normal file
3
z2/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
$Id$
|
||||
"""
|
48
z2/browser.py
Normal file
48
z2/browser.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
"""
|
||||
Base classes for views.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope import component
|
||||
from Products.Five import BrowserView
|
||||
|
||||
|
||||
class GenericView(BrowserView):
|
||||
|
||||
name = 'index_html'
|
||||
|
||||
@Lazy
|
||||
def object(self):
|
||||
return self.context.typeInterface(self.context)
|
||||
|
||||
@Lazy
|
||||
def objectView(self):
|
||||
return component.getMultiAdapter((self.object, self.request), name=self.name)
|
||||
|
||||
def __call__(self):
|
||||
return self.objectView()
|
||||
|
||||
|
||||
class GenericAddForm(GenericView):
|
||||
|
||||
name = 'create.html'
|
29
z2/configure.zcml
Normal file
29
z2/configure.zcml
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!-- $Id$ -->
|
||||
|
||||
<configure
|
||||
xmlns:zope="http://namespaces.zope.org/zope"
|
||||
xmlns:browser="http://namespaces.zope.org/browser">
|
||||
|
||||
<!-- <zope:adapter
|
||||
for="cybertools.z2.interfaces.IBase
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.publisher.interfaces.browser.IBrowserPublisher"
|
||||
factory="cybertools.z2.traversal.Traverser" />
|
||||
|
||||
<zope:adapter factory="cybertools.z2.base.BaseObject" />
|
||||
<zope:adapter factory="cybertools.z2.base.BaseFolder"
|
||||
provides="cybertools.util.generic.interfaces.IGenericFolder" />-->
|
||||
|
||||
<browser:page
|
||||
name="index_html"
|
||||
for="cybertools.util.generic.interfaces.IGeneric"
|
||||
class="cybertools.z2.browser.GenericView"
|
||||
permission="zope2.View" />
|
||||
|
||||
<browser:page
|
||||
name="create.html"
|
||||
for="cybertools.util.generic.interfaces.IGeneric"
|
||||
class="cybertools.z2.browser.GenericAddForm"
|
||||
permission="zope2.ManageProperties" />
|
||||
|
||||
</configure>
|
75
z2/generic.py
Normal file
75
z2/generic.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
"""
|
||||
Base classes.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from persistent.mapping import PersistentMapping
|
||||
from zope.app.container.interfaces import IObjectAddedEvent
|
||||
from zope import component
|
||||
from zope.interface import implements
|
||||
|
||||
from cybertools.util.generic.interfaces import IGeneric
|
||||
from cybertools.util.generic.interfaces import IGenericObject, IGenericFolder
|
||||
|
||||
|
||||
_not_found = object()
|
||||
|
||||
|
||||
class GenericObject(object):
|
||||
""" A mixin class supporting generic attribute access and other
|
||||
basic or common functionality when combined with Zope2's
|
||||
SimpleItem.
|
||||
"""
|
||||
|
||||
implements(IGenericObject)
|
||||
|
||||
typeInterface = None
|
||||
|
||||
def setup(self):
|
||||
self.__generic_attributes__ = PersistentMapping()
|
||||
if self.typeInterface:
|
||||
obj = self.typeInterface(self)
|
||||
obj.setup()
|
||||
|
||||
def getGenericAttribute(self, attr, default=_not_found):
|
||||
value = self.__generic_attributes__.get(attr, default)
|
||||
if value is _not_found:
|
||||
raise AttributeError(attr)
|
||||
return value
|
||||
|
||||
def setGenericAttribute(self, attr, value):
|
||||
self.__generic_attributes__[attr] = value
|
||||
return value
|
||||
|
||||
|
||||
class GenericFolder(GenericObject):
|
||||
""" Provide generic (i.e. dictionary-like) folder access to Zope2's
|
||||
Folder or BTreeFolder.
|
||||
"""
|
||||
|
||||
implements(IGenericFolder)
|
||||
|
||||
|
||||
@component.adapter(IGeneric, IObjectAddedEvent)
|
||||
def setup(obj, event):
|
||||
obj.setup()
|
||||
component.provideHandler(setup)
|
28
z2/tests.py
Executable file
28
z2/tests.py
Executable file
|
@ -0,0 +1,28 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
"""
|
||||
Tests for the 'cybertools.z2' package.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
import unittest, doctest
|
||||
from zope.testing.doctestunit import DocFileSuite
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
"Basic tests for the wiki package."
|
||||
|
||||
def testBasicStuff(self):
|
||||
pass
|
||||
|
||||
|
||||
def test_suite():
|
||||
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(Test),
|
||||
DocFileSuite('README.txt', optionflags=flags),
|
||||
))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='test_suite')
|
45
z2/traversal.py
Normal file
45
z2/traversal.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
#
|
||||
# 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
|
||||
#
|
||||
"""
|
||||
IPublishTraverse adapter for Zope 2 objects providing IBase.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope import component
|
||||
from ZPublisher.BaseRequest import DefaultPublishTraverse
|
||||
from ZPublisher.HTTPRequest import HTTPRequest
|
||||
|
||||
from cybertools.util.generic.interfaces import IGeneric
|
||||
|
||||
|
||||
class Traverser(DefaultPublishTraverse):
|
||||
|
||||
def publishTraverse(self, request, name):
|
||||
typeInterface = getattr(self.context, 'typeInterface', None)
|
||||
if typeInterface is not None:
|
||||
genObj = IGeneric(self.context, None)
|
||||
if genObj is not None:
|
||||
obj = typeInterface(genObj, None)
|
||||
if obj is not None:
|
||||
view = component.queryMultiAdapter((obj, request), name=name)
|
||||
#print '*** obj', obj, view
|
||||
if view is not None:
|
||||
return view
|
||||
return super(Traverser, self).publishTraverse(request, name)
|
||||
|
Loading…
Add table
Reference in a new issue