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:
helmutm 2009-10-29 17:24:45 +00:00
parent a08922f990
commit df079e9955
15 changed files with 482 additions and 10 deletions

View file

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

View file

@ -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
View file

@ -0,0 +1,3 @@
"""
$Id$
"""

3
wiki/generic/__init__.py Normal file
View file

@ -0,0 +1,3 @@
"""
$Id$
"""

179
wiki/generic/wiki.py Normal file
View 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()

View file

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

View file

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

View file

@ -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
View 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
View file

@ -0,0 +1,3 @@
"""
$Id$
"""

48
z2/browser.py Normal file
View 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
View 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
View 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
View 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
View 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)