Some macros and images were improved, enhanced search capabilities.

This commit is contained in:
Gaetan Delannay 2009-11-24 22:41:42 +01:00
parent 7435ff1601
commit 253e61612d
9 changed files with 65 additions and 29 deletions

View file

@ -12,7 +12,8 @@ import appy.gen
import appy.gen.descriptors import appy.gen.descriptors
from appy.gen.po import PoMessage from appy.gen.po import PoMessage
from appy.gen import Date, String, State, Transition, Type, Search from appy.gen import Date, String, State, Transition, Type, Search
from appy.gen.utils import GroupDescr, PageDescr, produceNiceMessage from appy.gen.utils import GroupDescr, PageDescr, produceNiceMessage, \
sequenceTypes
TABS = 4 # Number of blanks in a Python indentation. TABS = 4 # Number of blanks in a Python indentation.
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -100,6 +101,9 @@ class ArchetypeFieldDescriptor:
self.fieldType = 'LinesField' self.fieldType = 'LinesField'
self.widgetType = 'MultiSelectionWidget' self.widgetType = 'MultiSelectionWidget'
self.fieldParams['multiValued'] = True self.fieldParams['multiValued'] = True
if (type(self.appyType.validator) in sequenceTypes) and \
len(self.appyType.validator) <= 5:
self.widgetParams['format'] = 'checkbox'
else: else:
self.fieldType = 'StringField' self.fieldType = 'StringField'
self.widgetType = 'SelectionWidget' self.widgetType = 'SelectionWidget'

View file

@ -106,7 +106,8 @@ class ToolMixin(AbstractMixin):
_sortFields = {'title': 'sortable_title'} _sortFields = {'title': 'sortable_title'}
def executeQuery(self, contentType, flavourNumber=1, searchName=None, def executeQuery(self, contentType, flavourNumber=1, searchName=None,
startNumber=0, search=None, remember=False): startNumber=0, search=None, remember=False,
brainsOnly=False, maxResults=None):
'''Executes a query on a given p_contentType (or several, separated '''Executes a query on a given p_contentType (or several, separated
with commas) in Plone's portal_catalog. Portal types are from the with commas) in Plone's portal_catalog. Portal types are from the
flavour numbered p_flavourNumber. If p_searchName is specified, it flavour numbered p_flavourNumber. If p_searchName is specified, it
@ -115,7 +116,16 @@ class ToolMixin(AbstractMixin):
p_startNumber. If p_search is defined, it corresponds to a custom p_startNumber. If p_search is defined, it corresponds to a custom
Search instance (instead of a predefined named search like in Search instance (instead of a predefined named search like in
p_searchName). If both p_searchName and p_search are given, p_search p_searchName). If both p_searchName and p_search are given, p_search
is ignored.''' is ignored. This method returns a list of objects in the form of the
__dict__ attribute of an instance of SomeObjects (see in
appy.gen.utils). We return the __dict__ attribute instead of real
instance: that way, it can be used in ZPTs without security problems.
If p_brainsOnly is True, it returns a list of brains instead (can be
useful for some usages like knowing the number of objects without
needing to get information about them). If no p_maxResults is
specified, the method returns maximum
self.getNumberOfResultsPerPage(). p_maxResults is ignored if
p_brainsOnly is True.'''
# Is there one or several content types ? # Is there one or several content types ?
if contentType.find(',') != -1: if contentType.find(',') != -1:
# Several content types are specified # Several content types are specified
@ -125,7 +135,8 @@ class ToolMixin(AbstractMixin):
for pt in portalTypes] for pt in portalTypes]
else: else:
portalTypes = contentType portalTypes = contentType
params = {'portal_type': portalTypes, 'batch': True} params = {'portal_type': portalTypes}
if not brainsOnly: params['batch'] = True
# Manage additional criteria from a search when relevant # Manage additional criteria from a search when relevant
if searchName or search: if searchName or search:
# In this case, contentType must contain a single content type. # In this case, contentType must contain a single content type.
@ -151,7 +162,9 @@ class ToolMixin(AbstractMixin):
if self._sortFields.has_key(sb): sb = self._sortFields[sb] if self._sortFields.has_key(sb): sb = self._sortFields[sb]
params['sort_on'] = sb params['sort_on'] = sb
brains = self.portal_catalog.searchResults(**params) brains = self.portal_catalog.searchResults(**params)
res = SomeObjects(brains, self.getNumberOfResultsPerPage(), startNumber) if brainsOnly: return brains
if not maxResults: maxResults = self.getNumberOfResultsPerPage()
res = SomeObjects(brains, maxResults, startNumber)
res.brainsToObjects() res.brainsToObjects()
# In some cases (p_remember=True), we need to keep some information # In some cases (p_remember=True), we need to keep some information
# about the query results in the current user's session, allowing him # about the query results in the current user's session, allowing him

View file

@ -54,9 +54,8 @@ class AbstractMixin:
pass pass
# Manage "add" permissions # Manage "add" permissions
obj._appy_managePermissions() obj._appy_managePermissions()
# Re/unindex object # Reindex object
if obj._appy_meta_type == 'tool': self.unindexObject() obj.reindexObject()
else: obj.reindexObject()
return obj return obj
def delete(self): def delete(self):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 232 B

BIN
gen/plone25/skin/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

View file

@ -118,8 +118,8 @@
<metal:showDate define-macro="showDateField" <metal:showDate define-macro="showDateField"
tal:define="v python: field.getAccessor(contextObj)()"> tal:define="v python: field.getAccessor(contextObj)()">
<span tal:condition="showLabel" tal:content="label" class="appyLabel"></span> <span tal:condition="showLabel" tal:content="label" class="appyLabel"></span>
<span tal:content="python: v.strftime('%d/%m/') + str(v.year())"></span> <span tal:condition="v" tal:content="python: v.strftime('%d/%m/') + str(v.year())"></span>
<span tal:condition="python: appyType['format'] == 0" <span tal:condition="python: v and (appyType['format'] == 0)"
tal:content="python: v.strftime('%H:%M')"></span> tal:content="python: v.strftime('%H:%M')"></span>
</metal:showDate> </metal:showDate>
@ -132,7 +132,7 @@
<span tal:condition="showLabel" tal:content="label" class="appyLabel" <span tal:condition="showLabel" tal:content="label" class="appyLabel"
tal:attributes="class python: 'appyLabel ' + contextObj.getCssClasses(appyType, asSlave=False); tal:attributes="class python: 'appyLabel ' + contextObj.getCssClasses(appyType, asSlave=False);
id python: v"></span> id python: v"></span>
<tal:severalValues condition="severalValues"> <tal:severalValues condition="python: v and severalValues">
<ul class="appyList"> <ul class="appyList">
<tal:items repeat="sv v"> <tal:items repeat="sv v">
<tal:select condition="appyType/isSelect"> <tal:select condition="appyType/isSelect">
@ -146,7 +146,7 @@
</tal:items> </tal:items>
</ul> </ul>
</tal:severalValues> </tal:severalValues>
<tal:singleValue condition="not: severalValues"> <tal:singleValue condition="python: v and not severalValues">
<tal:select condition="appyType/isSelect"> <tal:select condition="appyType/isSelect">
<span tal:replace="python: tool.translate('%s_%s_list_%s' % (contextObj.meta_type, field.getName(), v))"/> <span tal:replace="python: tool.translate('%s_%s_list_%s' % (contextObj.meta_type, field.getName(), v))"/>
</tal:select> </tal:select>
@ -617,7 +617,7 @@
tal:condition="python: searchName and descr"> tal:condition="python: searchName and descr">
<span class="discreet" tal:content="descr"></span><br/><br/> <span class="discreet" tal:content="descr"></span><br/><br/>
</td> </td>
<td align="right"> <td align="right" width="25%">
<tal:comment replace="nothing">Appy (top) navigation</tal:comment> <tal:comment replace="nothing">Appy (top) navigation</tal:comment>
<metal:nav use-macro="here/skyn/macros/macros/appyNavigate"/> <metal:nav use-macro="here/skyn/macros/macros/appyNavigate"/>
</td> </td>
@ -723,8 +723,9 @@
<tal:comment replace="nothing">Delete the element</tal:comment> <tal:comment replace="nothing">Delete the element</tal:comment>
<td class="noPadding"> <td class="noPadding">
<img tal:condition="python: member.has_permission('Delete objects', obj)" <img tal:condition="python: member.has_permission('Delete objects', obj)"
src="delete_icon.gif" title="Delete" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer" title="Delete" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
tal:attributes="onClick python:'javascript:onDeleteObject(\'%s\')' % obj.UID()"/> tal:attributes="src string: $portal_url/skyn/delete.png;
onClick python:'javascript:onDeleteObject(\'%s\')' % obj.UID()"/>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -18,17 +18,6 @@
referenced object (edit, delete, etc).</tal:comment> referenced object (edit, delete, etc).</tal:comment>
<table class="no-style-table" cellpadding="0" cellspacing="0"> <table class="no-style-table" cellpadding="0" cellspacing="0">
<tr> <tr>
<tal:comment replace="nothing">Edit the element</tal:comment>
<td class="noPadding"><a tal:attributes="href python: obj.absolute_url() + '/skyn/edit'"
tal:condition="python: member.has_permission('Modify portal content', obj)">
<img src="edit.gif" title="label_edit" i18n:domain="plone" i18n:attributes="title" />
</a></td>
<tal:comment replace="nothing">Delete the element</tal:comment>
<td class="noPadding">
<img tal:condition="python: member.has_permission('Delete objects', obj)"
src="delete_icon.gif" title="Delete" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
tal:attributes="onClick python:'javascript:onDeleteObject(\'%s\')' % obj.UID()"/>
</td>
<tal:comment replace="nothing">Arrows for moving objects up or down</tal:comment> <tal:comment replace="nothing">Arrows for moving objects up or down</tal:comment>
<td class="noPadding" tal:condition="python: (len(objs)&gt;1) and member.has_permission('Modify portal content', obj)"> <td class="noPadding" tal:condition="python: (len(objs)&gt;1) and member.has_permission('Modify portal content', obj)">
<tal:moveRef define="objectIndex python:contextObj.getAppyRefIndex(fieldName, obj); <tal:moveRef define="objectIndex python:contextObj.getAppyRefIndex(fieldName, obj);
@ -47,6 +36,18 @@
style="cursor:pointer"/> style="cursor:pointer"/>
</tal:moveRef> </tal:moveRef>
</td> </td>
<tal:comment replace="nothing">Edit the element</tal:comment>
<td class="noPadding"><a tal:attributes="href python: obj.absolute_url() + '/skyn/edit'"
tal:condition="python: member.has_permission('Modify portal content', obj)">
<img src="edit.gif" title="label_edit" i18n:domain="plone" i18n:attributes="title" />
</a></td>
<tal:comment replace="nothing">Delete the element</tal:comment>
<td class="noPadding">
<img tal:condition="python: member.has_permission('Delete objects', obj)"
title="Delete" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
tal:attributes="src string: $portal_url/skyn/delete.png;
onClick python:'javascript:onDeleteObject(\'%s\')' % obj.UID()"/>
</td>
</tr> </tr>
</table> </table>
</metal:objectActions> </metal:objectActions>

View file

@ -97,6 +97,10 @@ class AbstractWrapper:
klass = property(get_klass) klass = property(get_klass)
def get_url(self): return self.o.absolute_url()+'/skyn/view' def get_url(self): return self.o.absolute_url()+'/skyn/view'
url = property(get_url) url = property(get_url)
def get_history(self):
key = self.o.workflow_history.keys()[0]
return self.o.workflow_history[key]
history = property(get_history)
def link(self, fieldName, obj): def link(self, fieldName, obj):
'''This method links p_obj to this one through reference field '''This method links p_obj to this one through reference field
@ -245,19 +249,33 @@ class AbstractWrapper:
replaced with normal chars.''' replaced with normal chars.'''
return unicodedata.normalize('NFKD', s).encode("ascii","ignore") return unicodedata.normalize('NFKD', s).encode("ascii","ignore")
def search(self, klass, sortBy='', **fields): def search(self, klass, sortBy='', maxResults=None, **fields):
'''Searches objects of p_klass. p_sortBy must be the name of an indexed '''Searches objects of p_klass. p_sortBy must be the name of an indexed
field (declared with indexed=True); every param in p_fields must field (declared with indexed=True); every param in p_fields must
take the name of an indexed field and take a possible value of this take the name of an indexed field and take a possible value of this
field.''' field. You can optionally specify a maximum number of results in
p_maxResults.'''
# Find the content type corresponding to p_klass # Find the content type corresponding to p_klass
flavour = self.flavour flavour = self.flavour
contentType = flavour.o.getPortalType(klass) contentType = flavour.o.getPortalType(klass)
# Create the Search object # Create the Search object
search = Search('customSearch', sortBy=sortBy, **fields) search = Search('customSearch', sortBy=sortBy, **fields)
res = self.tool.o.executeQuery(contentType,flavour.number,search=search) res = self.tool.o.executeQuery(contentType,flavour.number,search=search,
maxResults=maxResults)
return [o.appy() for o in res['objects']] return [o.appy() for o in res['objects']]
def count(self, klass, **fields):
'''Identical to m_search above, but returns the number of objects that
match the search instead of returning the objects themselves. Use
this method instead of writing len(self.search(...)).'''
flavour = self.flavour
contentType = flavour.o.getPortalType(klass)
search = Search('customSearch', **fields)
res = self.tool.o.executeQuery(contentType,flavour.number,search=search,
brainsOnly=True)
if res: return res._len # It is a LazyMap instance
else: return 0
def reindex(self): def reindex(self):
'''Asks a direct object reindexing. In most cases you don't have to '''Asks a direct object reindexing. In most cases you don't have to
reindex objects "manually" with this method. When an object is reindex objects "manually" with this method. When an object is