diff --git a/doc/AppyManifesto.odt b/doc/AppyManifesto.odt
index ddd08e9..418f670 100644
Binary files a/doc/AppyManifesto.odt and b/doc/AppyManifesto.odt differ
diff --git a/gen/__init__.py b/gen/__init__.py
index 8cae21c..4c448a3 100644
--- a/gen/__init__.py
+++ b/gen/__init__.py
@@ -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
diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py
index 2c63060..2313e75 100644
--- a/gen/mixins/ToolMixin.py
+++ b/gen/mixins/ToolMixin.py
@@ -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)
diff --git a/gen/ui/appy.css b/gen/ui/appy.css
index d8be619..e7f298e 100644
--- a/gen/ui/appy.css
+++ b/gen/ui/appy.css
@@ -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 }
diff --git a/gen/ui/portlet.pt b/gen/ui/portlet.pt
index d81795c..b5575bc 100644
--- a/gen/ui/portlet.pt
+++ b/gen/ui/portlet.pt
@@ -1,9 +1,51 @@
-