first steps towards getting favorites (and other personalization stuff) to work

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2394 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2008-02-12 18:53:22 +00:00
parent 535d06769d
commit ca273f78bc
16 changed files with 456 additions and 120 deletions

View file

@ -71,6 +71,9 @@ class Loops(Folder):
def getViewManager(self):
return self['views']
def getRecordManager(self):
return self['records']
def getLoopsUri(self, obj):
return str(loopsPrefix + getPath(obj)[len(getPath(self)):])

View file

@ -211,7 +211,7 @@ class ConceptView(BaseView):
def description(self):
return self.adapted.description
def fieldData(self):
def xxx_fieldData(self):
# obsolete - use getData() instead
# TODO: change view macros accordingly
ti = IType(self.context).typeInterface

View file

@ -18,6 +18,12 @@
<!-- new style pages -->
<page for="loops.interfaces.INode"
name="index.html"
class="loops.browser.node.NodeView"
permission="zope.Public"
/>
<page for="loops.interfaces.INode"
name="main.html"
class="loops.browser.node.NodeView"
@ -371,6 +377,20 @@
menu="zmi_views" title="Contents"
/>
<!-- record manager -->
<containerViews
for="loops.interfaces.IRecordManager"
index="zope.ManageContent"
/>
<menuItem
for="loops.interfaces.IRecordManager"
action="contents.html"
permission="zope.ManageContent"
menu="zmi_views" title="Contents"
/>
<!-- import/export -->
<pages for="loops.interfaces.IViewManager"
@ -401,10 +421,10 @@
menu="zmi_views" title="Contents"
/>
<containerViews
<!--<containerViews
for="loops.interfaces.INode"
index="zope.ManageContent"
/>
/>-->
<addform
label="Add Node"
@ -488,10 +508,10 @@
menu="zmi_views" title="View"
/>-->
<defaultView
<!--<defaultView
for="loops.interfaces.INode"
name="main.html"
/>
/>-->
<zope:adapter
name="listpages"
@ -718,6 +738,16 @@
provides="cybertools.browser.configurator.IViewConfigurator"
/>
<!-- manual setup action(s) -->
<page
name="setup_loopssite"
for="loops.interfaces.ILoops"
class="loops.setup.SetupView"
attribute="setupLoopsSite"
permission="zope.ManageSite"
/>
<include package=".skin" />
</configure>

View file

@ -24,41 +24,23 @@
type="zope.app.content.interfaces.IContentType" />
<class class="loops.base.Loops">
<implements
interface="zope.annotation.interfaces.IAttributeAnnotatable" />
<factory id="loops.Loops"
description="loops top-level container" />
<allow
attributes="getSiteManager" />
<require
permission="zope.ManageServices"
attributes="setSiteManager" />
<require
permission="zope.View"
interface="zope.app.container.interfaces.IReadContainer" />
<require
permission="zope.ManageContent"
interface="zope.app.container.interfaces.IWriteContainer" />
<!--<require
permission="zope.View"
interface="loops.interfaces.ILoops" />-->
<require
permission="zope.View"
attributes="getLoopsRoot getLoopsUri loopsTraverse getConceptManager
getResourceManager getViewManager skinName options" />
<require
permission="zope.ManageContent"
set_schema=".interfaces.ILoops" />
<implements interface="zope.annotation.interfaces.IAttributeAnnotatable" />
<factory id="loops.Loops" description="loops top-level container" />
<allow attributes="getSiteManager" />
<require permission="zope.ManageServices"
attributes="setSiteManager" />
<require permission="zope.View"
interface="zope.app.container.interfaces.IReadContainer" />
<require permission="zope.ManageContent"
interface="zope.app.container.interfaces.IWriteContainer" />
<!--<require permission="zope.View"
interface="loops.interfaces.ILoops" />-->
<require permission="zope.View"
attributes="getLoopsRoot getLoopsUri loopsTraverse getConceptManager
getResourceManager getViewManager getRecordManager
skinName options" />
<require permission="zope.ManageContent"
set_schema=".interfaces.ILoops" />
</class>
<!-- concept manager and concept -->
@ -67,28 +49,21 @@
type="zope.app.content.interfaces.IContentType" />
<class class=".concept.ConceptManager">
<factory id="loops.ConceptManager"
description="Concept manager" />
<factory id="loops.ConceptManager" description="Concept manager" />
<require
permission="zope.View"
interface="zope.app.container.interfaces.IReadContainer" />
<require
permission="zope.ManageContent"
interface="zope.app.container.interfaces.IWriteContainer" />
<require
permission="zope.View"
attributes="getTypePredicate getDefaultPredicate getTypeConcept
getPredicateType" />
</class>
<interface
interface=".interfaces.IConcept"
type="zope.app.content.interfaces.IContentType" />
<interface interface=".interfaces.IConcept"
type="zope.app.content.interfaces.IContentType" />
<class class=".concept.Concept">
<implements interface="zope.annotation.interfaces.IAttributeAnnotatable" />
@ -115,91 +90,59 @@
type="zope.app.content.interfaces.IContentType" />
<class class=".resource.ResourceManager">
<factory id="loops.ResourceManager"
description="Resource manager" />
<factory id="loops.ResourceManager" description="Resource manager" />
<require
permission="zope.View"
interface="zope.app.container.interfaces.IReadContainer"
attributes="getLoopsRoot" />
<require
permission="zope.ManageContent"
interface="zope.app.container.interfaces.IWriteContainer" />
</class>
<interface
interface=".interfaces.IResource"
type="zope.app.content.interfaces.IContentType" />
<interface interface=".interfaces.IResource"
type="zope.app.content.interfaces.IContentType" />
<class class=".resource.Resource">
<implements
interface="zope.annotation.interfaces.IAttributeAnnotatable" />
<factory
id="loops.Resource"
description="Document" />
<implements interface="zope.annotation.interfaces.IAttributeAnnotatable" />
<factory id="loops.Resource" description="Document" />
<require
permission="zope.View"
interface=".interfaces.IBaseResource
zope.size.interfaces.ISized" />
<require
permission="zope.ManageContent"
set_schema=".interfaces.IBaseResource" />
</class>
<interface
interface=".interfaces.IDocument"
type="zope.app.content.interfaces.IContentType" />
<interface interface=".interfaces.IDocument"
type="zope.app.content.interfaces.IContentType" />
<class class=".resource.Document">
<implements
interface="zope.annotation.interfaces.IAttributeAnnotatable" />
<factory
id="loops.Document"
description="Document" />
<implements interface="zope.annotation.interfaces.IAttributeAnnotatable" />
<factory id="loops.Document" description="Document" />
<require
permission="zope.View"
interface=".interfaces.IDocument
zope.size.interfaces.ISized" />
<require
permission="zope.ManageContent"
set_schema=".interfaces.IDocument" />
</class>
<interface
interface=".interfaces.IMediaAsset"
type="zope.app.content.interfaces.IContentType" />
<interface interface=".interfaces.IMediaAsset"
type="zope.app.content.interfaces.IContentType" />
<class class=".resource.MediaAsset">
<implements
interface="zope.annotation.interfaces.IAttributeAnnotatable" />
<factory
id="loops.MediaAsset"
description="Media Asset" />
<implements interface="zope.annotation.interfaces.IAttributeAnnotatable" />
<factory id="loops.MediaAsset" description="Media Asset" />
<require
permission="zope.View"
interface=".interfaces.IBaseResource
zope.size.interfaces.ISized" />
<require
permission="zope.ManageContent"
set_schema=".interfaces.IBaseResource" />
</class>
<!--<adapter
@ -214,49 +157,46 @@
type="zope.app.content.interfaces.IContentType" />
<class class=".view.ViewManager">
<factory id="loops.ViewManager"
description="View manager" />
<factory id="loops.ViewManager" description="View manager" />
<require
permission="zope.View"
interface="zope.app.container.interfaces.IReadContainer" />
<require
permission="zope.ManageContent"
interface="zope.app.container.interfaces.IWriteContainer"
attributes="updateOrder" />
</class>
<interface
interface=".interfaces.INode"
type="zope.app.content.interfaces.IContentType" />
<interface interface=".interfaces.INode"
type="zope.app.content.interfaces.IContentType" />
<class class=".view.Node">
<!--<implements
interface="loops.interfaces.ILoopsObject" />-->
<implements
interface="zope.annotation.interfaces.IAttributeAnnotatable" />
<factory
id="loops.Node"
description="Node" />
<implements interface="zope.annotation.interfaces.IAttributeAnnotatable" />
<factory id="loops.Node" description="Node" />
<require
permission="zope.View"
interface=".interfaces.INode" />
<require
permission="zope.ManageContent"
set_schema=".interfaces.INode" />
<!--<require
permission="zope.View"
interface="zope.app.container.interfaces.IReadContainer" />-->
</class>
<!-- record manager -->
<interface interface="loops.interfaces.IRecordManager"
type="zope.app.content.interfaces.IContentType" />
<class class="loops.record.RecordManager">
<factory id="loops.RecordManager" description="Record manager" />
<require
permission="zope.View"
interface="zope.app.container.interfaces.IReadContainer" />
<require
permission="zope.ManageContent"
interface="zope.app.container.interfaces.IWriteContainer" />
</class>
<!-- adapters -->

View file

@ -31,10 +31,11 @@ from zope.app.file.interfaces import IImage as IBaseAsset
from zope.app.folder.interfaces import IFolder
from zope.component.interfaces import IObjectEvent
from zope.size.interfaces import ISized
from cybertools.relation.interfaces import IDyadicRelation
import util
from util import _
from cybertools.relation.interfaces import IDyadicRelation
from cybertools.tracking.interfaces import ITrackingStorage
from loops import util
from loops.util import _
# common interfaces
@ -516,6 +517,12 @@ class INodeContained(Interface):
containers(INode, IViewManager)
# record manager interfaces
class IRecordManager(ILoopsObject):
contains(ITrackingStorage)
# the loops top-level container
class ILoops(ILoopsObject):
@ -557,6 +564,10 @@ class ILoops(ILoopsObject):
""" Return the (default) view manager.
"""
def getRecordManager():
""" Return the (default) record manager.
"""
class ILoopsContained(Interface):
containers(ILoops)

View file

@ -108,4 +108,8 @@
<zope:adapter factory="loops.organize.setup.SetupManager"
name="organize" />
<!-- include -->
<include package=".personal" />
</configure>

View file

@ -0,0 +1,119 @@
===============================================================
loops - Linked Objects for Organization and Processing Services
===============================================================
($Id$)
Let's do some basic setup
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
>>> site = placefulSetUp(True)
>>> from zope import component, interface
and set up a simple loops site with a concept manager and some concepts
(with all the type machinery, what in real life is done via standard
ZCML setup):
>>> from loops.organize.setup import SetupManager
>>> component.provideAdapter(SetupManager, name='organize')
>>> from loops.organize.personal.setup import SetupManager
>>> component.provideAdapter(SetupManager, name='organize.personal')
>>> from loops.tests.setup import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
Favorites - Managed by a Tracking Storage
=========================================
>>> loopsRoot = concepts.getLoopsRoot()
>>> records = loopsRoot.getRecordManager()
>>> favorites = records['favorites']
User management setup
---------------------
In order to be able to login and store favorites and other personal data
we have to prepare our environment. We need some basic adapter registrations,
and a pluggable authentication utility with a principal folder.
>>> from loops.organize.tests import setupUtilitiesAndAdapters
>>> setupData = setupUtilitiesAndAdapters(loopsRoot)
>>> from zope.app.appsetup.bootstrap import ensureUtility
>>> from zope.app.authentication.authentication import PluggableAuthentication
>>> from zope.app.security.interfaces import IAuthentication
>>> ensureUtility(site, IAuthentication, '', PluggableAuthentication,
... copy_to_zlog=False, asObject=True)
<...PluggableAuthentication...>
>>> pau = component.getUtility(IAuthentication, context=site)
>>> from zope.app.authentication.principalfolder import PrincipalFolder
>>> from zope.app.authentication.interfaces import IAuthenticatorPlugin
>>> pFolder = PrincipalFolder('users.')
>>> pau['users'] = pFolder
>>> pau.authenticatorPlugins = ('users',)
So we can now register a user ...
>>> from zope.app.authentication.principalfolder import InternalPrincipal
>>> pFolder['john'] = InternalPrincipal('john', 'xx', u'John')
>>> from zope.app.authentication.principalfolder import FoundPrincipalFactory
>>> component.provideAdapter(FoundPrincipalFactory)
... and create a corresponding person.
>>> from loops.concept import Concept
>>> johnC = concepts['john'] = Concept(u'John')
>>> person = concepts['person']
>>> johnC.conceptType = person
>>> from loops.common import adapted
>>> adapted(johnC).userId = 'users.john'
Finally, we log in as the newly created user.
>>> from zope.app.authentication.principalfolder import Principal
>>> pJohn = Principal('users.john', 'xxx', u'John')
>>> from loops.tests.auth import login
>>> login(pJohn)
Working with the favorites storage
----------------------------------
The setup has provided us with a few resources, so there are objects we
can remember as favorites.
>>> list(resources.keys())
[u'd001.txt', u'd002.txt', u'd003.txt']
>>> from loops import util
>>> d001Id = util.getUidForObject(resources['d001.txt'])
>>> d003Id = util.getUidForObject(resources['d003.txt'])
>>> johnCId = util.getUidForObject(johnC)
(We always need a "run" - can we try to ignore this for favorites?)
>>> runId = favorites.startRun()
For favorites we don't need any data...
>>> favorites.saveUserTrack(d001Id, runId, johnCId, {})
'0000001'
>>> favorites.saveUserTrack(d003Id, runId, johnCId, {})
'0000002'
So we are now ready to query the favorites.
>>> favs = favorites.query(userName=johnCId)
>>> favs
[<Favorite ['27', 1, '33', '...']: {}>,
<Favorite ['31', 1, '33', '...']: {}>]
>>> util.getObjectForUid(favs[0].taskId) is resources['d001.txt']
True
User interface
--------------

View file

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

View file

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

View file

@ -0,0 +1,28 @@
<!-- $Id$ -->
<configure
xmlns="http://namespaces.zope.org/zope"
i18n_domain="zope">
<class class="cybertools.tracking.btree.TrackingStorage">
<require permission="zope.View"
interface="zope.app.container.interfaces.IReadContainer" />
<require permission="zope.View"
interface="cybertools.tracking.interfaces.ITrackingStorage" />
<require permission="zope.ManageContent"
interface="zope.app.container.interfaces.IWriteContainer" />
<require permission="zope.ManageContent"
set_schema="cybertools.tracking.interfaces.ITrackingStorage" />
</class>
<class class="loops.organize.personal.favorite.Favorite">
<require permission="zope.View"
interface="loops.organize.personal.interfaces.IFavorite" />
<require permission="zope.ManageContent"
set_schema="loops.organize.personal.interfaces.IFavorite" />
</class>
<adapter factory="loops.organize.personal.setup.SetupManager"
name="organize.personal" />
</configure>

View file

@ -0,0 +1,35 @@
#
# Copyright (c) 2008 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 a notification framework.
$Id$
"""
from zope.interface import implements
from cybertools.tracking.btree import Track
from loops.organize.personal.interfaces import IFavorite
class Favorite(Track):
implements(IFavorite)
typeName = 'Favorite'

View file

@ -0,0 +1,37 @@
#
# Copyright (c) 2008 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
#
"""
Interface definitions for favorites/clipboard/navigation history.
$Id$
"""
from zope.interface import Interface, Attribute
from zope import schema
from cybertools.tracking.interfaces import ITrack
class IFavorite(ITrack):
""" A favorite references a content object via the
task id attribute; the user name references
the user/person for which the favorite is to be stored.
The tracking storage's run management is not used.
"""

View file

@ -0,0 +1,38 @@
#
# Copyright (c) 2008 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
#
"""
Automatic setup of a loops site for the organize.personal package.
$Id$
"""
from zope.component import adapts
from zope.interface import implements, Interface
from cybertools.tracking.btree import TrackingStorage
from loops.organize.personal.favorite import Favorite
from loops.setup import SetupManager as BaseSetupManager
class SetupManager(BaseSetupManager):
def setup(self):
records = self.context.getRecordManager()
favorites = self.addObject(records, TrackingStorage, 'favorites',
trackFactory=Favorite)

22
organize/personal/tests.py Executable file
View file

@ -0,0 +1,22 @@
# $Id$
import unittest, doctest
from zope.testing.doctestunit import DocFileSuite
class Test(unittest.TestCase):
"Basic tests for the loops.organize.personal package."
def testBasics(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')

43
record.py Normal file
View file

@ -0,0 +1,43 @@
#
# Copyright (c) 2008 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
#
"""
Definition of the View and related classses.
$Id$
"""
from zope.app.container.btree import BTreeContainer
from zope.interface import implements
from zope.traversing.api import getParent
from cybertools.util.jeep import Jeep
from loops.interfaces import ILoopsContained
from loops.interfaces import IRecordManager
class RecordManager(BTreeContainer):
implements(IRecordManager, ILoopsContained)
def getLoopsRoot(self):
return getParent(self)
def getAllParents(self, collectGrants=False):
return Jeep()

View file

@ -36,6 +36,7 @@ from loops.concept import ConceptManager, Concept
from loops.interfaces import ILoops, ITypeConcept
from loops.interfaces import IFile, IImage, ITextDocument, INote
from loops.query import IQueryConcept
from loops.record import RecordManager
from loops.resource import ResourceManager, Resource
from loops.view import ViewManager, Node
@ -72,6 +73,7 @@ class SetupManager(object):
concepts = self.addObject(loopsRoot, ConceptManager, 'concepts')
resources = self.addObject(loopsRoot, ResourceManager, 'resources')
views = self.addObject(loopsRoot, ViewManager, 'views')
records = self.addObject(loopsRoot, RecordManager, 'records')
return concepts, resources, views
def setupCoreConcepts(self, conceptManager):
@ -222,3 +224,19 @@ def addAndConfigureObject(container, class_, name, **kw):
setattr(adapted, attr, kw[attr])
notify(ObjectModifiedEvent(obj))
return obj
class SetupView(object):
""" Allows to carry out setup actions manually.
"""
def __init__(self, context, request):
self.context = context
self.request = request
self.manager = ISetupManager(context)
def setupLoopsSite(self):
#self.manager.setupManagers()
self.manager.setup()
return 'Done'