work in progress: personal filters
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@4034 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
0bbea0f45b
commit
64db0029e6
11 changed files with 298 additions and 11 deletions
|
@ -12,9 +12,9 @@
|
||||||
<h1><a tal:omit-tag="python: level > 1"
|
<h1><a tal:omit-tag="python: level > 1"
|
||||||
tal:attributes="href request/URL"
|
tal:attributes="href request/URL"
|
||||||
tal:content="item/title">Title</a></h1>
|
tal:content="item/title">Title</a></h1>
|
||||||
<p tal:define="description description|item/description"
|
<p tal:define="description description|item/renderedDescription"
|
||||||
tal:condition="description">
|
tal:condition="description">
|
||||||
<i tal:content="description">Description</i></p>
|
<i tal:content="structure description">Description</i></p>
|
||||||
<metal:fields define-slot="fields" />
|
<metal:fields define-slot="fields" />
|
||||||
<div class="content-1" id="1.body"
|
<div class="content-1" id="1.body"
|
||||||
tal:attributes="id id;"
|
tal:attributes="id id;"
|
||||||
|
|
|
@ -74,7 +74,8 @@ textarea {
|
||||||
|
|
||||||
table.listing {
|
table.listing {
|
||||||
margin: 1px;
|
margin: 1px;
|
||||||
margin-top: 6px;
|
/*margin-top: 0.5em; */
|
||||||
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.listing th {
|
table.listing th {
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="change_password.html"
|
<li><a href="change_password.html"
|
||||||
i18n:translate="">Change Password</a></li>
|
i18n:translate="">Change Password</a></li>
|
||||||
|
<li><a href="change_password.html"
|
||||||
|
tal:condition="python:item.globalOptions('organize.useFilters')"
|
||||||
|
i18n:translate="">Edit Filters</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</metal:block>
|
</metal:block>
|
||||||
</metal:block>
|
</metal:block>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
|
# Copyright (c) 2010 Helmut Merz helmutm@cy55.de
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -29,6 +29,7 @@ from zope.cachedescriptors.property import Lazy
|
||||||
from zope.traversing.browser.absoluteurl import absoluteURL
|
from zope.traversing.browser.absoluteurl import absoluteURL
|
||||||
|
|
||||||
from cybertools.browser.configurator import ViewConfigurator, MacroViewProperty
|
from cybertools.browser.configurator import ViewConfigurator, MacroViewProperty
|
||||||
|
from cybertools.meta.interfaces import IOptions
|
||||||
from loops.organize.party import getPersonForUser
|
from loops.organize.party import getPersonForUser
|
||||||
from loops.util import _
|
from loops.util import _
|
||||||
|
|
||||||
|
@ -44,17 +45,32 @@ class PortletConfigurator(ViewConfigurator):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.request = request
|
self.request = request
|
||||||
|
|
||||||
|
@property
|
||||||
|
def viewProperties(self):
|
||||||
|
return self.favorites + self.filters
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def records(self):
|
||||||
|
return self.context.getLoopsRoot().getRecordManager()
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def person(self):
|
||||||
|
return getPersonForUser(self.context, self.request)
|
||||||
|
|
||||||
def hasFavorites(self):
|
def hasFavorites(self):
|
||||||
records = self.context.getLoopsRoot().getRecordManager()
|
if self.records is not None:
|
||||||
if records is not None:
|
return 'favorites' in self.records
|
||||||
return 'favorites' in records
|
return False
|
||||||
|
|
||||||
|
def hasFilters(self):
|
||||||
|
if (IOptions(self.context.getLoopsRoot()).organize.useFilters and
|
||||||
|
self.records is not None):
|
||||||
|
return 'filters' in self.records
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def viewProperties(self):
|
def favorites(self):
|
||||||
if (not self.hasFavorites()
|
if (not self.hasFavorites() or self.person is None):
|
||||||
or getPersonForUser(self.context, self.request) is None):
|
|
||||||
#if IUnauthenticatedPrincipal.providedBy(self.request.principal):
|
|
||||||
return []
|
return []
|
||||||
favorites = MacroViewProperty(self.context, self.request)
|
favorites = MacroViewProperty(self.context, self.request)
|
||||||
favorites.setParams(dict(
|
favorites.setParams(dict(
|
||||||
|
@ -67,3 +83,17 @@ class PortletConfigurator(ViewConfigurator):
|
||||||
))
|
))
|
||||||
return [favorites]
|
return [favorites]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def filters(self):
|
||||||
|
if (not self.hasFilters() or self.person is None):
|
||||||
|
return []
|
||||||
|
filters = MacroViewProperty(self.context, self.request)
|
||||||
|
filters.setParams(dict(
|
||||||
|
slot='portlet_right',
|
||||||
|
identifier='loops.organize.filters',
|
||||||
|
title=_(u'Filters'),
|
||||||
|
subMacro=personal_macros.macros['filters_portlet'],
|
||||||
|
priority=195,
|
||||||
|
url=absoluteURL(self.context, self.request) + '/@@filters.html',
|
||||||
|
))
|
||||||
|
return [filters]
|
||||||
|
|
|
@ -20,4 +20,13 @@
|
||||||
<page name="removeFavorite.html" attribute="remove" />
|
<page name="removeFavorite.html" attribute="remove" />
|
||||||
</browser:pages>
|
</browser:pages>
|
||||||
|
|
||||||
|
<browser:pages
|
||||||
|
class="loops.organize.personal.browser.filter.FilterView"
|
||||||
|
for="loops.interfaces.INode"
|
||||||
|
permission="zope.View">
|
||||||
|
<page name="filters_view" />
|
||||||
|
<page name="addFilter.html" attribute="add" />
|
||||||
|
<page name="deactivateFilter.html" attribute="deactivate" />
|
||||||
|
</browser:pages>
|
||||||
|
|
||||||
</configure>
|
</configure>
|
||||||
|
|
87
organize/personal/browser/filter.py
Normal file
87
organize/personal/browser/filter.py
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2010 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
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
A view (to be used by listings, portlets, ...) for filters.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
from zope import component
|
||||||
|
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||||
|
from zope.cachedescriptors.property import Lazy
|
||||||
|
|
||||||
|
from cybertools.browser.configurator import ViewConfigurator, MacroViewProperty
|
||||||
|
from loops.browser.node import NodeView
|
||||||
|
from loops.organize.party import getPersonForUser
|
||||||
|
from loops.organize.personal.interfaces import IFilters
|
||||||
|
from loops import util
|
||||||
|
|
||||||
|
|
||||||
|
personal_macros = ViewPageTemplateFile('personal_macros.pt')
|
||||||
|
|
||||||
|
|
||||||
|
class FilterView(NodeView):
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def item(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def person(self):
|
||||||
|
return getPersonForUser(self.context, self.request)
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def filters(self):
|
||||||
|
records = self.loopsRoot.getRecordManager()
|
||||||
|
if records is not None:
|
||||||
|
storage = records.get('filters')
|
||||||
|
if storage is not None:
|
||||||
|
return IFilters(storage)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def listFilters(self):
|
||||||
|
if self.filters is None:
|
||||||
|
return
|
||||||
|
for uid in self.filters.list(self.person):
|
||||||
|
obj = util.getObjectForUid(uid)
|
||||||
|
if obj is not None:
|
||||||
|
yield dict(url=self.getUrlForTarget(obj),
|
||||||
|
uid=uid,
|
||||||
|
title=obj.title,
|
||||||
|
description=obj.description,
|
||||||
|
object=obj)
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
if self.filters is None:
|
||||||
|
return
|
||||||
|
uid = self.request.get('id')
|
||||||
|
if not uid:
|
||||||
|
return
|
||||||
|
obj = util.getObjectForUid(uid)
|
||||||
|
self.filters.add(obj, self.person)
|
||||||
|
self.request.response.redirect(self.virtualTargetUrl)
|
||||||
|
|
||||||
|
def deactivate(self):
|
||||||
|
if self.filters is None:
|
||||||
|
return
|
||||||
|
id = self.request.get('id')
|
||||||
|
if not id:
|
||||||
|
return
|
||||||
|
self.filters.deactivate(id)
|
||||||
|
self.request.response.redirect(self.virtualTargetUrl)
|
|
@ -22,3 +22,29 @@
|
||||||
i18n:attributes="title">Add to Favorites</a>
|
i18n:attributes="title">Add to Favorites</a>
|
||||||
</div>
|
</div>
|
||||||
</metal:actions>
|
</metal:actions>
|
||||||
|
|
||||||
|
|
||||||
|
<metal:actions define-macro="filters_portlet"
|
||||||
|
tal:define="view nocall:context/@@filters_view;
|
||||||
|
targetUid view/targetUid">
|
||||||
|
<div tal:repeat="item view/listFilters">
|
||||||
|
<span style="float:right" class="delete-item"> <a href="removeFilter.html"
|
||||||
|
tal:attributes="href
|
||||||
|
string:${view/url}/deactivateFilter.html?id=${item/uid};
|
||||||
|
title string:Deactivate filter"
|
||||||
|
i18n:attributes="title">X</a> </span>
|
||||||
|
<a href=""
|
||||||
|
tal:attributes="href item/url;
|
||||||
|
title item/description"
|
||||||
|
tal:content="item/title">Some object</a>
|
||||||
|
</div>
|
||||||
|
<div id="addFilter" class="action"
|
||||||
|
tal:condition="targetUid">
|
||||||
|
<a href="addFilter.html"
|
||||||
|
i18n:translate=""
|
||||||
|
tal:attributes="href
|
||||||
|
string:${view/virtualTargetUrl}/addFilter.html?id=$targetUid;
|
||||||
|
title string:Use current object as filter"
|
||||||
|
i18n:attributes="title">Add Filter</a>
|
||||||
|
</div>
|
||||||
|
</metal:actions>
|
||||||
|
|
|
@ -17,6 +17,19 @@
|
||||||
interface="loops.organize.personal.interfaces.IFavorites" />
|
interface="loops.organize.personal.interfaces.IFavorites" />
|
||||||
</class>
|
</class>
|
||||||
|
|
||||||
|
<class class="loops.organize.personal.filter.Filter">
|
||||||
|
<require permission="zope.View"
|
||||||
|
interface="loops.organize.personal.interfaces.IFilter" />
|
||||||
|
<require permission="zope.ManageContent"
|
||||||
|
set_schema="loops.organize.personal.interfaces.IFilter" />
|
||||||
|
</class>
|
||||||
|
|
||||||
|
<adapter factory="loops.organize.personal.filter.Filters" trusted="True" />
|
||||||
|
<class class="loops.organize.personal.filter.Filters">
|
||||||
|
<require permission="zope.View"
|
||||||
|
interface="loops.organize.personal.interfaces.IFilters" />
|
||||||
|
</class>
|
||||||
|
|
||||||
<adapter factory="loops.organize.personal.setup.SetupManager"
|
<adapter factory="loops.organize.personal.setup.SetupManager"
|
||||||
name="organize.personal" />
|
name="organize.personal" />
|
||||||
|
|
||||||
|
|
79
organize/personal/filter.py
Normal file
79
organize/personal/filter.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2010 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 filters.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
from zope.component import adapts
|
||||||
|
from zope.interface import implements
|
||||||
|
|
||||||
|
from cybertools.tracking.btree import Track
|
||||||
|
from cybertools.tracking.interfaces import ITrackingStorage
|
||||||
|
from loops.organize.personal.interfaces import IFilters, IFilter
|
||||||
|
from loops import util
|
||||||
|
|
||||||
|
|
||||||
|
class Filters(object):
|
||||||
|
|
||||||
|
implements(IFilters)
|
||||||
|
adapts(ITrackingStorage)
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
def list(self, person, activeOnly=True, sortKey=None):
|
||||||
|
for item in self.listTracks(person, sortKey):
|
||||||
|
yield item.taskId
|
||||||
|
|
||||||
|
def listTracks(self, person, sortKey=None):
|
||||||
|
if person is None:
|
||||||
|
return
|
||||||
|
personUid = util.getUidForObject(person)
|
||||||
|
if sortKey is None:
|
||||||
|
sortKey = lambda x: -x.timeStamp
|
||||||
|
for item in sorted(self.context.query(userName=personUid), key=sortKey):
|
||||||
|
yield item
|
||||||
|
|
||||||
|
def add(self, obj, person, data=None):
|
||||||
|
if None in (obj, person):
|
||||||
|
return False
|
||||||
|
uid = util.getUidForObject(obj)
|
||||||
|
personUid = util.getUidForObject(person)
|
||||||
|
if self.context.query(userName=personUid, taskId=uid):
|
||||||
|
return False
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
return self.context.saveUserTrack(uid, 0, personUid, data)
|
||||||
|
|
||||||
|
def remove(self, id):
|
||||||
|
changed = False
|
||||||
|
for t in self.context.query(userName=personUid, taskId=uid):
|
||||||
|
changed = True
|
||||||
|
self.context.removeTrack(t)
|
||||||
|
return changed
|
||||||
|
|
||||||
|
|
||||||
|
class Filter(Track):
|
||||||
|
|
||||||
|
implements(IFilter)
|
||||||
|
|
||||||
|
typeName = 'Filter'
|
||||||
|
|
|
@ -51,3 +51,39 @@ class IFavorite(ITrack):
|
||||||
the user/person for which the favorite is to be stored.
|
the user/person for which the favorite is to be stored.
|
||||||
The tracking storage's run management is not used.
|
The tracking storage's run management is not used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class IFilters(Interface):
|
||||||
|
""" A collection of filters.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def add(title, filter, person):
|
||||||
|
""" Add a filter specification (a mapping) to the person's
|
||||||
|
filters collection using the title given.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def activate(id):
|
||||||
|
""" Activate the filter specified by its ID.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def deactivate(id):
|
||||||
|
""" Deactivate the filter specified by its ID.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def remove(id):
|
||||||
|
""" Remove the filter specified by its ID from the person's
|
||||||
|
favorites collection.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def list(person, activeOnly=True, sortKey=None):
|
||||||
|
""" Return a list of filters for the person given.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class IFilter(ITrack):
|
||||||
|
""" A filter is a stored query that will be used for restricting the result
|
||||||
|
set of child and resource listings as well as explicit searches.
|
||||||
|
It usually references a parent concept via the task id attribute.
|
||||||
|
The user/person for which the filter is to be stored.
|
||||||
|
The tracking storage's run management is not used.
|
||||||
|
"""
|
||||||
|
|
|
@ -27,6 +27,7 @@ from zope.interface import implements, Interface
|
||||||
|
|
||||||
from cybertools.tracking.btree import TrackingStorage
|
from cybertools.tracking.btree import TrackingStorage
|
||||||
from loops.organize.personal.favorite import Favorite
|
from loops.organize.personal.favorite import Favorite
|
||||||
|
from loops.organize.personal.filter import Filter
|
||||||
from loops.setup import SetupManager as BaseSetupManager
|
from loops.setup import SetupManager as BaseSetupManager
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,3 +37,5 @@ class SetupManager(BaseSetupManager):
|
||||||
records = self.context.getRecordManager()
|
records = self.context.getRecordManager()
|
||||||
favorites = self.addObject(records, TrackingStorage, 'favorites',
|
favorites = self.addObject(records, TrackingStorage, 'favorites',
|
||||||
trackFactory=Favorite)
|
trackFactory=Favorite)
|
||||||
|
filters = self.addObject(records, TrackingStorage, 'filters',
|
||||||
|
trackFactory=Filter)
|
||||||
|
|
Loading…
Add table
Reference in a new issue