[gen] Searches can now be grouped into groups of groups of..., creating a tree structure in the portlet.
This commit is contained in:
parent
6cf29778b6
commit
4872e5d8b8
Binary file not shown.
|
@ -233,24 +233,26 @@ class Group:
|
|||
if self.group and (self.group not in walkedGroups) and \
|
||||
not self.group.label:
|
||||
# We remember walked groups for avoiding infinite recursion.
|
||||
self.group.generateLabels(messages, classDescr, walkedGroups)
|
||||
self.group.generateLabels(messages, classDescr, walkedGroups,
|
||||
forSearch=forSearch)
|
||||
|
||||
def insertInto(self, widgets, groupDescrs, page, metaType):
|
||||
def insertInto(self, widgets, groupDescrs, page, metaType, forSearch=False):
|
||||
'''Inserts the GroupDescr instance corresponding to this Group instance
|
||||
into p_widgets, the recursive structure used for displaying all
|
||||
widgets in a given p_page, and returns this GroupDescr instance.'''
|
||||
widgets in a given p_page (or all searches), and returns this
|
||||
GroupDescr instance.'''
|
||||
# First, create the corresponding GroupDescr if not already in
|
||||
# p_groupDescrs.
|
||||
if self.name not in groupDescrs:
|
||||
groupDescr = groupDescrs[self.name] = GroupDescr(self, page,
|
||||
metaType).get()
|
||||
groupDescr = groupDescrs[self.name] = \
|
||||
GroupDescr(self, page, metaType, forSearch=forSearch).get()
|
||||
# Insert the group at the higher level (ie, directly in p_widgets)
|
||||
# if the group is not itself in a group.
|
||||
if not self.group:
|
||||
widgets.append(groupDescr)
|
||||
else:
|
||||
outerGroupDescr = self.group.insertInto(widgets, groupDescrs,
|
||||
page, metaType)
|
||||
page, metaType, forSearch=forSearch)
|
||||
GroupDescr.addWidget(outerGroupDescr, groupDescr)
|
||||
else:
|
||||
groupDescr = groupDescrs[self.name]
|
||||
|
@ -291,7 +293,7 @@ class Import:
|
|||
class Search:
|
||||
'''Used for specifying a search for a given type.'''
|
||||
def __init__(self, name, group=None, sortBy='', sortOrder='asc', limit=None,
|
||||
default=False, **fields):
|
||||
default=False, colspan=1, **fields):
|
||||
self.name = name
|
||||
# Searches may be visually grouped in the portlet.
|
||||
self.group = Group.get(group)
|
||||
|
@ -301,6 +303,7 @@ class Search:
|
|||
# If this search is the default one, it will be triggered by clicking
|
||||
# on main link.
|
||||
self.default = default
|
||||
self.colspan = colspan
|
||||
# In the dict below, keys are indexed field names and values are
|
||||
# search values.
|
||||
self.fields = fields
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
import os, os.path, sys, re, time, random, types, base64, urllib
|
||||
from appy import Object
|
||||
import appy.gen
|
||||
from appy.gen import Type, Search, Selection, String
|
||||
from appy.gen.utils import SomeObjects, getClassName
|
||||
from appy.gen import Type, Search, Selection, String, Page
|
||||
from appy.gen.utils import SomeObjects, getClassName, GroupDescr
|
||||
from appy.gen.mixins import BaseMixin
|
||||
from appy.gen.wrappers import AbstractWrapper
|
||||
from appy.gen.descriptors import ClassDescriptor
|
||||
|
@ -672,7 +672,7 @@ class ToolMixin(BaseMixin):
|
|||
obj = self.getObject(objectUid)
|
||||
return obj, fieldName
|
||||
|
||||
def getSearches(self, contentType):
|
||||
def getGroupedSearches(self, contentType):
|
||||
'''Returns an object with 2 attributes:
|
||||
* "searches" stores the searches that are defined for p_contentType;
|
||||
* "default" stores the search defined as the default one.
|
||||
|
@ -680,37 +680,27 @@ class ToolMixin(BaseMixin):
|
|||
search or about a group of searches.
|
||||
'''
|
||||
appyClass = self.getAppyClass(contentType)
|
||||
searches = []
|
||||
res = []
|
||||
default = None # Also retrieve the default one here.
|
||||
visitedGroups = {} # Names of already visited search groups
|
||||
groups = {} # The already encountered groups
|
||||
page = Page('main') # A dummy page required by class GroupDescr
|
||||
for search in ClassDescriptor.getSearches(appyClass):
|
||||
# Determine first group label, we will need it.
|
||||
groupLabel = None
|
||||
groupName = None
|
||||
if search.group:
|
||||
groupName = search.group.name
|
||||
groupLabel = '%s_searchgroup_%s' % (contentType, groupName)
|
||||
# Add an item representing the search group if relevant
|
||||
if groupName and (groupName not in visitedGroups):
|
||||
group = {'name': groupName, 'isGroup': True,
|
||||
'labelId': groupLabel, 'searches': [],
|
||||
'label': self.translate(groupLabel),
|
||||
'descr': self.translate('%s_descr' % groupLabel),
|
||||
}
|
||||
searches.append(group)
|
||||
visitedGroups[groupName] = group
|
||||
# Add the search itself
|
||||
# Compute the dict representing this search
|
||||
searchLabel = '%s_search_%s' % (contentType, search.name)
|
||||
dSearch = {'name': search.name, 'isGroup': False,
|
||||
dSearch = {'name': search.name, 'type': 'search',
|
||||
'colspan': search.colspan,
|
||||
'label': self.translate(searchLabel),
|
||||
'descr': self.translate('%s_descr' % searchLabel)}
|
||||
if groupName:
|
||||
visitedGroups[groupName]['searches'].append(dSearch)
|
||||
if not search.group:
|
||||
# Insert the search at the highest level, not in any group.
|
||||
res.append(dSearch)
|
||||
else:
|
||||
searches.append(dSearch)
|
||||
if search.default:
|
||||
default = dSearch
|
||||
return Object(searches=searches, default=default).__dict__
|
||||
groupDescr = search.group.insertInto(res, groups, page,
|
||||
contentType, forSearch=True)
|
||||
GroupDescr.addWidget(groupDescr, dSearch)
|
||||
# Is this search the default search?
|
||||
if search.default: default = dSearch
|
||||
return Object(searches=res, default=default).__dict__
|
||||
|
||||
def getQueryUrl(self, contentType, searchName, startNumber=None):
|
||||
'''This method creates the URL that allows to perform a (non-Ajax)
|
||||
|
|
|
@ -80,7 +80,7 @@ img { border: 0; vertical-align: middle}
|
|||
.portletPage { font-style: italic; }
|
||||
.portletGroup { font-variant: small-caps; font-weight: bold; font-style: normal;
|
||||
margin-top: 0.1em }
|
||||
.portletSearch { font-size: 90%; font-style: italic; padding-left: 1em}
|
||||
.portletSearch { font-size: 90%; font-style: italic }
|
||||
.phase { border-style: dashed; border-width: thin; padding: 4px 0.6em 5px 1em;}
|
||||
.phaseSelected { background-color: #F4F5F6; }
|
||||
.content { padding: 14px 14px 9px 15px; background-color: #f1f1f1 }
|
||||
|
|
|
@ -1,9 +1,51 @@
|
|||
<tal:comment replace="nothing">
|
||||
This macro displays the content of the application portlet.
|
||||
</tal:comment>
|
||||
<tal:comment replace="nothing">Macro for displaying a search</tal:comment>
|
||||
<div metal:define-macro="search" class="portletSearch">
|
||||
<a tal:attributes="href python: '%s?className=%s&search=%s' % (queryUrl, rootClass, search['name']);
|
||||
title search/descr;
|
||||
class python: test(search['name'] == currentSearch, 'portletCurrent', '');"
|
||||
tal:content="structure search/label"></a>
|
||||
</div>
|
||||
|
||||
<tal:comment replace="nothing">Macro for displaying a group of searches</tal:comment>
|
||||
<metal:group define-macro="group"
|
||||
tal:define="expanded python: request.get(widget['labelId'], 'collapsed') == 'expanded'">
|
||||
<tal:comment replace="nothing">Group name</tal:comment>
|
||||
<div class="portletGroup">
|
||||
<img style="cursor:pointer; margin-right: 3px"
|
||||
tal:attributes="id python: '%s_img' % widget['labelId'];
|
||||
src python:test(expanded, 'ui/collapse.gif', 'ui/expand.gif');
|
||||
onClick python:'toggleCookie(\'%s\')' % widget['labelId'];
|
||||
align dleft"/>
|
||||
<span tal:replace="python: _(widget['labelId'])"/>
|
||||
</div>
|
||||
<tal:comment replace="nothing">Group content</tal:comment>
|
||||
<div tal:define="display python:test(expanded, 'display:block', 'display:none')"
|
||||
tal:attributes="id widget/labelId;
|
||||
style python: 'padding-left: 10px;; %s' % display">
|
||||
<tal:searchRows repeat="searches widget/widgets">
|
||||
<tal:searchElem repeat="searchElem searches">
|
||||
<tal:comment replace="nothing">An inner group within this group</tal:comment>
|
||||
<tal:group condition="python: searchElem['type'] == 'group'">
|
||||
<tal:g define="widget searchElem">
|
||||
<metal:s use-macro="app/ui/portlet/macros/group"/>
|
||||
</tal:g>
|
||||
</tal:group>
|
||||
<tal:search condition="python: searchElem['type'] != 'group'">
|
||||
<tal:s define="search searchElem">
|
||||
<metal:s use-macro="app/ui/portlet/macros/search"/>
|
||||
</tal:s>
|
||||
</tal:search>
|
||||
</tal:searchElem>
|
||||
</tal:searchRows>
|
||||
</div>
|
||||
</metal:group>
|
||||
|
||||
<tal:comment replace="nothing">Macro displaying the whole portlet</tal:comment>
|
||||
<metal:portlet define-macro="portlet"
|
||||
tal:define="queryUrl python: '%s/ui/query' % tool.absolute_url();
|
||||
toolUrl tool/absolute_url;
|
||||
app tool/getApp;
|
||||
appUrl app/absolute_url;
|
||||
currentSearch req/search|nothing;
|
||||
currentClass req/className|nothing;
|
||||
contextObj tool/getPublishedObject;
|
||||
|
@ -16,7 +58,7 @@
|
|||
<img tal:attributes="src string: $appUrl/ui/gotoSource.png"/>
|
||||
</a>
|
||||
</div>
|
||||
<metal:phases use-macro="here/ui/portlet/macros/phases"/>
|
||||
<metal:phases use-macro="app/ui/portlet/macros/phases"/>
|
||||
</div>
|
||||
|
||||
<tal:comment replace="nothing">One section for every searchable root class.</tal:comment>
|
||||
|
@ -26,7 +68,7 @@
|
|||
<div class="portletSep" tal:define="nb repeat/rootClass/number"
|
||||
tal:condition="python: (nb == 1 and contextObj) or (nb != 1)"></div>
|
||||
|
||||
<div class="portletContent" tal:define="searchInfo python: tool.getSearches(rootClass)">
|
||||
<div class="portletContent" tal:define="searchInfo python: tool.getGroupedSearches(rootClass)">
|
||||
<tal:comment replace="nothing">Section title (link triggers the default search), with action icons</tal:comment>
|
||||
<a tal:define="queryParam python: searchInfo['default'] and ('&search=%s' % searchInfo['default']['name']) or ''"
|
||||
tal:attributes="href python: '%s?className=%s%s' % (queryUrl, rootClass, queryParam);
|
||||
|
@ -56,40 +98,17 @@
|
|||
<img tal:attributes="src string: $appUrl/ui/search.gif"/>
|
||||
</a>
|
||||
</span>
|
||||
<tal:comment replace="nothing">Searches for this content type.</tal:comment>
|
||||
<tal:searchOrGroup repeat="searchOrGroup searchInfo/searches">
|
||||
<tal:group condition="searchOrGroup/isGroup">
|
||||
<tal:expanded define="group searchOrGroup;
|
||||
expanded python: request.get(group['labelId'], 'collapsed') == 'expanded'">
|
||||
<tal:comment replace="nothing">Group name</tal:comment>
|
||||
<div class="portletGroup">
|
||||
<img style="cursor:pointer; margin-right: 3px"
|
||||
tal:attributes="id python: '%s_img' % group['labelId'];
|
||||
src python:test(expanded, 'ui/collapse.gif', 'ui/expand.gif');
|
||||
onClick python:'toggleCookie(\'%s\')' % group['labelId'];
|
||||
align dleft"/>
|
||||
<span tal:replace="group/label"/>
|
||||
</div>
|
||||
<tal:comment replace="nothing">Group searches</tal:comment>
|
||||
<div tal:attributes="id group/labelId;
|
||||
style python:test(expanded, 'display:block', 'display:none')">
|
||||
<div class="portletSearch" tal:repeat="search group/searches">
|
||||
<a tal:attributes="href python: '%s?className=%s&search=%s' % (queryUrl, rootClass, search['name']);
|
||||
title search/descr;
|
||||
class python: test(search['name'] == currentSearch, 'portletCurrent', '');"
|
||||
tal:content="structure search/label"></a>
|
||||
</div>
|
||||
</div>
|
||||
</tal:expanded>
|
||||
<tal:comment replace="nothing">Searches for this content type</tal:comment>
|
||||
<tal:widget repeat="widget searchInfo/searches">
|
||||
<tal:group condition="python: widget['type'] == 'group'">
|
||||
<metal:s use-macro="app/ui/portlet/macros/group"/>
|
||||
</tal:group>
|
||||
<dt tal:define="search searchOrGroup" tal:condition="not: searchOrGroup/isGroup"
|
||||
class="portletAppyItem portletSearch">
|
||||
<a tal:attributes="href python: '%s?className=%s&search=%s' % (queryUrl, rootClass, search['name']);
|
||||
title search/descr;
|
||||
class python: test(search['name'] == currentSearch, 'portletCurrent', '');"
|
||||
tal:content="structure search/label"></a>
|
||||
</dt>
|
||||
</tal:searchOrGroup>
|
||||
<tal:search condition="python: widget['type'] != 'group'">
|
||||
<tal:s define="search widget">
|
||||
<metal:s use-macro="app/ui/portlet/macros/search"/>
|
||||
</tal:s>
|
||||
</tal:search>
|
||||
</tal:widget>
|
||||
</div>
|
||||
</tal:section>
|
||||
</metal:portlet>
|
||||
|
|
|
@ -59,8 +59,8 @@ class Descr:
|
|||
def get(self): return self.__dict__
|
||||
|
||||
class GroupDescr(Descr):
|
||||
def __init__(self, group, page, metaType):
|
||||
'''Creates the data structure manipulated in ZPTs from p_group, the
|
||||
def __init__(self, group, page, metaType, forSearch=False):
|
||||
'''Creates the data structure manipulated in ZPTs for p_group, the
|
||||
Group instance used in the field definition.'''
|
||||
self.type = 'group'
|
||||
# All p_group attributes become self attributes.
|
||||
|
@ -77,7 +77,9 @@ class GroupDescr(Descr):
|
|||
else: # It is a tuple (metaType, name)
|
||||
if group.label[1]: labelName = group.label[1]
|
||||
if group.label[0]: prefix = group.label[0]
|
||||
self.labelId = '%s_group_%s' % (prefix, labelName)
|
||||
if forSearch: gp = 'searchgroup'
|
||||
else: gp = 'group'
|
||||
self.labelId = '%s_%s_%s' % (prefix, gp, labelName)
|
||||
self.descrId = self.labelId + '_descr'
|
||||
self.helpId = self.labelId + '_help'
|
||||
# The name of the page where the group lies
|
||||
|
|
Loading…
Reference in a new issue