Wrong index type for selection strings that are not multivalued.

This commit is contained in:
Gaetan Delannay 2010-09-20 11:33:54 +02:00
parent 7c2b8fed11
commit 9f4db88bdf
9 changed files with 41 additions and 30 deletions

View file

@ -402,11 +402,12 @@ class Type:
res = True res = True
return res return res
def isSortable(self, usage='search'): def isSortable(self, usage):
'''Can fields of this type be used for sorting purposes (when sorting '''Can fields of this type be used for sorting purposes (when sorting
search results (p_usage="search") or when sorting reference fields search results (p_usage="search") or when sorting reference fields
(p_usage="ref")?''' (p_usage="ref")?'''
if usage == 'search': return self.indexed if usage == 'search':
return self.indexed and not self.isMultiValued()
elif usage == 'ref': elif usage == 'ref':
return self.type in ('Integer', 'Float', 'Boolean', 'Date') or \ return self.type in ('Integer', 'Float', 'Boolean', 'Date') or \
((self.type == 'String') and (self.format == 0)) ((self.type == 'String') and (self.format == 0))

View file

@ -120,7 +120,7 @@ class Table(LayoutElement):
'''Represents a table where to dispose graphical elements.''' '''Represents a table where to dispose graphical elements.'''
simpleParams = ('style', 'css_class', 'cellpadding', 'cellspacing', 'width', simpleParams = ('style', 'css_class', 'cellpadding', 'cellspacing', 'width',
'align') 'align')
derivedRepls = {'view': 'hrv', 'cell': 'l'} derivedRepls = {'view': 'hrv', 'cell': 'ld'}
def __init__(self, layoutString=None, style=None, css_class='', def __init__(self, layoutString=None, style=None, css_class='',
cellpadding=0, cellspacing=0, width='100%', align='left', cellpadding=0, cellspacing=0, width='100%', align='left',
other=None, derivedType=None): other=None, derivedType=None):
@ -191,7 +191,9 @@ class Table(LayoutElement):
if char in rowDelimiters: if char in rowDelimiters:
valign = rowDelimiters[char] valign = rowDelimiters[char]
if self.isHeaderRow(rowContent): if self.isHeaderRow(rowContent):
self.headerRow = Row(rowContent,valign,isHeader=True).get() if not self.headerRow:
self.headerRow = Row(rowContent, valign,
isHeader=True).get()
else: else:
self.rows.append(Row(rowContent, valign).get()) self.rows.append(Row(rowContent, valign).get())
rowContent = '' rowContent = ''

View file

@ -451,7 +451,8 @@ class PloneInstaller:
n = appyType.name n = appyType.name
indexName = 'get%s%s' % (n[0].upper(), n[1:]) indexName = 'get%s%s' % (n[0].upper(), n[1:])
indexType = 'FieldIndex' indexType = 'FieldIndex'
if (appyType.type == 'String') and appyType.isSelect: if (appyType.type == 'String') and appyType.isSelect and \
appyType.isMultiValued():
indexType = 'ZCTextIndex' indexType = 'ZCTextIndex'
indexInfo[indexName] = indexType indexInfo[indexName] = indexType
if indexInfo: if indexInfo:

View file

@ -275,6 +275,14 @@ class ToolMixin(AbstractMixin):
return value[:maxWidth] + '...' return value[:maxWidth] + '...'
return value return value
def truncateText(self, text, width=15):
'''Truncates p_text to max p_width chars. If the text is longer than
p_width, the truncated part is put in a "acronym" html tag.'''
if len(text) <= width: return text
else:
return '<acronym title="%s">%s</acronym>' % \
(text, text[:width] + '...')
translationMapping = {'portal_path': ''} translationMapping = {'portal_path': ''}
def translateWithMapping(self, label): def translateWithMapping(self, label):
'''Translates p_label in the application domain, with a default '''Translates p_label in the application domain, with a default
@ -336,15 +344,13 @@ class ToolMixin(AbstractMixin):
res[means.id] = means.__dict__ res[means.id] = means.__dict__
return res return res
def userMayAdd(self, rootClass): def userMaySearch(self, rootClass):
'''For deciding if a user may add a new instance of a class, beyond the '''This method checks if the currently logged user can trigger searches
permission-based check, we can have a custom method that proposes an on a given p_rootClass. This is done by calling method "maySearch"
additional condition. This method checks if there is such a custom on the class. If no such method exists, we return True.'''
method (must be named "mayCreate") define on p_rootClass, and calls
it if yes. If no, it returns True.'''
pythonClass = self.getAppyClass(rootClass) pythonClass = self.getAppyClass(rootClass)
if 'mayCreate' in pythonClass.__dict__: if 'maySearch' in pythonClass.__dict__:
return pythonClass.mayCreate(self.appy()) return pythonClass.maySearch(self.appy())
return True return True
def onImportObjects(self): def onImportObjects(self):
@ -369,6 +375,12 @@ class ToolMixin(AbstractMixin):
else: else:
return False return False
def isSortable(self, name, className, usage):
'''Is field p_name defined on p_metaType sortable for p_usage purposes
(p_usage can be "ref" or "search")?'''
appyType = self.getAppyType(name, className=className)
return appyType.isSortable(usage=usage)
def _searchValueIsEmpty(self, key): def _searchValueIsEmpty(self, key):
'''Returns True if request value in key p_key can be considered as '''Returns True if request value in key p_key can be considered as
empty.''' empty.'''

View file

@ -642,11 +642,6 @@ class AbstractMixin:
reverse = rq.get('reverse') == 'True' reverse = rq.get('reverse') == 'True'
self.appy().sort(fieldName, sortKey=sortKey, reverse=reverse) self.appy().sort(fieldName, sortKey=sortKey, reverse=reverse)
def isRefSortable(self, fieldName):
'''Can p_fieldName, which is a field defined on self, be used as a sort
key in a reference field?'''
return self.getAppyType(fieldName).isSortable(usage='ref')
def getWorkflow(self, appy=True): def getWorkflow(self, appy=True):
'''Returns the Appy workflow instance that is relevant for this '''Returns the Appy workflow instance that is relevant for this
object. If p_appy is False, it returns the DC workflow.''' object. If p_appy is False, it returns the DC workflow.'''
@ -969,6 +964,7 @@ class AbstractMixin:
if created and rq.get('nav', None): if created and rq.get('nav', None):
# Get the initiator # Get the initiator
splitted = rq['nav'].split('.') splitted = rq['nav'].split('.')
if splitted[0] == 'search': return # Not an initiator but a search.
initiator = self.uid_catalog.searchResults( initiator = self.uid_catalog.searchResults(
UID=splitted[1])[0].getObject() UID=splitted[1])[0].getObject()
fieldName = splitted[2].split(':')[1] fieldName = splitted[2].split(':')[1]

View file

@ -54,18 +54,18 @@
<tal:comment replace="nothing">Mandatory column "Title"/"Name"</tal:comment> <tal:comment replace="nothing">Mandatory column "Title"/"Name"</tal:comment>
<th tal:define="fieldName python:'title'; sortable python:True; filterable python:True"> <th tal:define="fieldName python:'title'; sortable python:True; filterable python:True">
<span tal:content="python: tool.translate('ref_name')"/> <span tal:replace="structure python: tool.truncateText(tool.translate('ref_name'))"/>
<metal:sortAndFilter use-macro="here/skyn/navigate/macros/sortAndFilter"/> <metal:sortAndFilter use-macro="here/skyn/navigate/macros/sortAndFilter"/>
</th> </th>
<tal:comment replace="nothing">Columns corresponding to other fields</tal:comment> <tal:comment replace="nothing">Columns corresponding to other fields</tal:comment>
<tal:columnHeader repeat="fieldDescr fieldDescrs"> <tal:columnHeader repeat="fieldDescr fieldDescrs">
<th tal:define="fieldName fieldDescr/name|string:workflow_state; <th tal:define="fieldName fieldDescr/name|string:workflow_state;
sortable fieldDescr/indexed|nothing; sortable python: tool.isSortable(fieldName, contentType, 'search');
filterable fieldDescr/filterable|nothing;"> filterable fieldDescr/filterable|nothing;">
<tal:comment replace="nothing">Display header for a "standard" field</tal:comment> <tal:comment replace="nothing">Display header for a "standard" field</tal:comment>
<tal:standardField condition="python: fieldName != 'workflow_state'"> <tal:standardField condition="python: fieldName != 'workflow_state'">
<span tal:replace="python: tool.translate(fieldDescr['labelId'])"/> <span tal:replace="structure python: tool.truncateText(tool.translate(fieldDescr['labelId']))"/>
</tal:standardField> </tal:standardField>
<tal:comment replace="nothing">Display header for the workflow state</tal:comment> <tal:comment replace="nothing">Display header for the workflow state</tal:comment>
<tal:workflowState condition="python: fieldName == 'workflow_state'"> <tal:workflowState condition="python: fieldName == 'workflow_state'">

View file

@ -12,11 +12,11 @@
title allows to see all root objects in the database.</tal:comment> title allows to see all root objects in the database.</tal:comment>
<table cellpadding="0" cellspacing="0" width="100%"> <table cellpadding="0" cellspacing="0" width="100%">
<tr> <tr>
<td> <td tal:define="titleIsClickable python: member.has_role('Manager') and rootClasses">
<a tal:condition="python: len(flavours)==1 and rootClasses" <a tal:condition="titleIsClickable"
tal:attributes="href python:'%s?type_name=%s&flavourNumber=1' % (queryUrl, ','.join(rootClasses))" tal:attributes="href python:'%s?type_name=%s&flavourNumber=1' % (queryUrl, ','.join(rootClasses))"
tal:content="python: tool.translate(appName)"></a> tal:content="python: tool.translate(appName)"></a>
<span tal:condition="python: len(flavours)&gt;1 or not rootClasses" <span tal:condition="not: titleIsClickable"
tal:replace="python: tool.translate(appName)"/> tal:replace="python: tool.translate(appName)"/>
</td> </td>
<td align="right"> <td align="right">
@ -36,14 +36,13 @@
<dt class="portletAppyItem"><metal:phases use-macro="here/skyn/portlet/macros/phases"/></dt> <dt class="portletAppyItem"><metal:phases use-macro="here/skyn/portlet/macros/phases"/></dt>
</tal:publishedObject> </tal:publishedObject>
<tal:comment replace="nothing">TODO: implement a widget for selecting the needed flavour.</tal:comment>
<tal:comment replace="nothing">Create a section for every root class.</tal:comment> <tal:comment replace="nothing">Create a section for every root class.</tal:comment>
<tal:section repeat="rootClass rootClasses" <tal:section repeat="rootClass rootClasses"
define="flavourNumber python:1; define="flavourNumber python:1;
flavour python: tool.getFlavour('Dummy_%d' % flavourNumber)"> flavour python: tool.getFlavour('Dummy_%d' % flavourNumber)">
<tal:comment replace="nothing">Section title, with action icons</tal:comment> <tal:comment replace="nothing">Section title, with action icons</tal:comment>
<dt tal:attributes="class python:test((repeat['rootClass'].number()==1) and not contextObj, 'portletAppyItem', 'portletAppyItem portletSep')"> <dt tal:condition="python: tool.userMaySearch(rootClass)"
tal:attributes="class python:test((repeat['rootClass'].number()==1) and not contextObj, 'portletAppyItem', 'portletAppyItem portletSep')">
<table width="100%" cellspacing="0" cellpadding="0" class="no-style-table"> <table width="100%" cellspacing="0" cellpadding="0" class="no-style-table">
<tr> <tr>
<td> <td>
@ -53,7 +52,7 @@
</td> </td>
<td align="right" <td align="right"
tal:define="addPermission python: '%s: Add %s' % (appName, rootClass); tal:define="addPermission python: '%s: Add %s' % (appName, rootClass);
userMayAdd python: member.has_permission(addPermission, appFolder) and tool.userMayAdd(rootClass); userMayAdd python: member.has_permission(addPermission, appFolder);
createMeans python: tool.getCreateMeans(rootClass)"> createMeans python: tool.getCreateMeans(rootClass)">
<tal:comment replace="nothing">Create a new object from a web form</tal:comment> <tal:comment replace="nothing">Create a new object from a web form</tal:comment>
<img style="cursor:pointer" <img style="cursor:pointer"

View file

@ -74,7 +74,7 @@
ref field according to the field that corresponds to this column. ref field according to the field that corresponds to this column.
</tal:comment> </tal:comment>
<metal:sortIcons define-macro="sortIcons" <metal:sortIcons define-macro="sortIcons"
tal:define="ajaxBaseCall python: navBaseCall.replace('**v**', '\'%s\',\'SortReference\', {\'sortKey\':\'%s\', \'reverse\':\'**v**\'}' % (startNumber, shownField))" tal:condition="python: canWrite and objs[0].isRefSortable(shownField)"> tal:define="ajaxBaseCall python: navBaseCall.replace('**v**', '\'%s\',\'SortReference\', {\'sortKey\':\'%s\', \'reverse\':\'**v**\'}' % (startNumber, shownField))" tal:condition="python: canWrite and tool.isSortable(shownField, objs[0].meta_type, 'ref')">
<img style="cursor:pointer" <img style="cursor:pointer"
tal:attributes="src string:$portal_url/skyn/sortAsc.png; tal:attributes="src string:$portal_url/skyn/sortAsc.png;
onClick python: ajaxBaseCall.replace('**v**', 'False')"/> onClick python: ajaxBaseCall.replace('**v**', 'False')"/>

View file

@ -46,7 +46,7 @@ class FlavourWrapper(AbstractWrapper):
p_attrName. p_attrName.
"resultColumns" "resultColumns"
Stores the list of columns that must be show when displaying Stores the list of columns that must be shown when displaying
instances of the a given root p_klass. instances of the a given root p_klass.
"enableAdvancedSearch" "enableAdvancedSearch"