[gen] Now it is possible to index and search Ref fields.
This commit is contained in:
parent
bdaf1b4bbd
commit
5f530d9f9e
|
@ -1762,7 +1762,7 @@ class Ref(Type):
|
|||
maxChars=None, colspan=1, master=None, masterValue=None,
|
||||
focus=False, historized=False, mapping=None, label=None,
|
||||
queryable=False, queryFields=None, queryNbCols=1,
|
||||
navigable=False):
|
||||
navigable=False, searchSelect=None):
|
||||
self.klass = klass
|
||||
self.attribute = attribute
|
||||
# May the user add new objects through this ref ?
|
||||
|
@ -1830,6 +1830,11 @@ class Ref(Type):
|
|||
self.queryNbCols = queryNbCols
|
||||
# Within the portlet, will referred elements appear ?
|
||||
self.navigable = navigable
|
||||
# The search select method is used if self.indexed is True. In this
|
||||
# case, we need to know among which values we can search on this field,
|
||||
# in the search screen. Those values are returned by self.searchSelect,
|
||||
# which must be a static method accepting the tool as single arg.
|
||||
self.searchSelect = searchSelect
|
||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
||||
editDefault, show, page, group, layouts, move, indexed,
|
||||
False, specificReadPermission, specificWritePermission,
|
||||
|
@ -1911,6 +1916,23 @@ class Ref(Type):
|
|||
def getFormattedValue(self, obj, value):
|
||||
return value
|
||||
|
||||
def getIndexType(self): return 'ZCTextIndex'
|
||||
|
||||
def getIndexValue(self, obj, forSearch=False):
|
||||
'''Value for indexing is the list of UIDs of linked objects. If
|
||||
p_forSearch is True, it will return a "string" version made of the
|
||||
titles of linked objects.'''
|
||||
if not forSearch:
|
||||
res = getattr(obj.aq_base, self.name, '')
|
||||
if res: res = ' '.join(res)
|
||||
return res
|
||||
else:
|
||||
# For the global search: concatenate titles of linked objects
|
||||
titles = []
|
||||
for obj in self.getValue(type='objects'):
|
||||
titles.append(obj.title)
|
||||
return ' '.join(titles)
|
||||
|
||||
def validateValue(self, obj, value):
|
||||
if not self.link: return None
|
||||
# We only check "link" Refs because in edit views, "add" Refs are
|
||||
|
|
|
@ -250,7 +250,6 @@ class ZopeInstaller:
|
|||
|
||||
# Create the admin user if it does not exist.
|
||||
if not appyTool.count('User', noSecurity=True, login='admin'):
|
||||
print 'No admin!'
|
||||
appyTool.create('users', noSecurity=True, login='admin',
|
||||
password1='admin', password2='admin',
|
||||
email='admin@appyframework.org', roles=['Manager'])
|
||||
|
|
|
@ -1138,4 +1138,13 @@ class ToolMixin(BaseMixin):
|
|||
sendMail(appyTool, email, subject, body)
|
||||
os.remove(tokenFile)
|
||||
return self.goto(siteUrl, self.translate('new_password_sent'))
|
||||
|
||||
def getSearchValues(self, name, className):
|
||||
'''Gets the possible values for selecting a value for searching field
|
||||
p_name belonging to class p_className.'''
|
||||
klass = self.getAppyClass(className, wrapper=True)
|
||||
method = getattr(klass, name).searchSelect
|
||||
tool = self.appy()
|
||||
objects = method.__get__(tool)(tool)
|
||||
return [(o.uid, o) for o in objects]
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -518,17 +518,14 @@ class BaseMixin:
|
|||
return appyType.select(self.appy())
|
||||
|
||||
xhtmlToText = re.compile('<.*?>', re.S)
|
||||
def getReferenceLabel(self, name, refObject):
|
||||
def getReferenceLabel(self, name, refObject, className=None):
|
||||
'''p_name is the name of a Ref field with link=True. I need to display,
|
||||
on an edit view, the p_refObject in the listbox that will allow
|
||||
the user to choose which object(s) to link through the Ref.
|
||||
The information to display may only be the object title or more if
|
||||
field.shownInfo is used.'''
|
||||
appyType = self.getAppyType(name)
|
||||
res = refObject.title
|
||||
if 'title' in appyType.shownInfo:
|
||||
# We may place it at another place
|
||||
res = ''
|
||||
appyType = self.getAppyType(name, className=className)
|
||||
res = ''
|
||||
for fieldName in appyType.shownInfo:
|
||||
refType = refObject.o.getAppyType(fieldName)
|
||||
value = getattr(refObject, fieldName)
|
||||
|
|
|
@ -65,7 +65,7 @@ img { border: 0; vertical-align: middle}
|
|||
width: 600px; border: 1px #F0C36D solid; padding: 6px 16px;
|
||||
background-color: #F9EDBE; text-align: center;
|
||||
border-radius: 2px 2px 2px 2px; box-shadow: 0 2px 4px #A9A9A9;}
|
||||
.focus { font-size: 90%; padding: 6px 16px; background-color: #d7dee4;
|
||||
.focus { font-size: 90%; margin: 7px; background-color: #d7dee4;
|
||||
border-radius: 2px 2px 2px 2px; box-shadow: 0 2px 4px #A9A9A9;}
|
||||
.focus td { padding: 4px 0px 4px 4px }
|
||||
.discreet { font-size: 90%; }
|
||||
|
|
|
@ -254,4 +254,26 @@
|
|||
</metal:cell>
|
||||
|
||||
<tal:comment replace="nothing">Search macro for a Ref.</tal:comment>
|
||||
<metal:search define-macro="search"></metal:search>
|
||||
<metal:search define-macro="search">
|
||||
<label tal:attributes="for widgetName" tal:content="python: _(widget['labelId'])"></label><br>
|
||||
<tal:comment replace="nothing">The "and" / "or" radio buttons</tal:comment>
|
||||
<tal:operator define="operName python: 'o_%s' % name;
|
||||
orName python: '%s_or' % operName;
|
||||
andName python: '%s_and' % operName;"
|
||||
condition="python: widget['multiplicity'][1]!=1">
|
||||
<input type="radio" tal:attributes="name operName; id orName" checked="checked" value="or"/>
|
||||
<label tal:attributes="for orName" tal:content="python: _('search_or')"></label>
|
||||
<input type="radio" tal:attributes="name operName; id andName" value="and"/>
|
||||
<label tal:attributes="for andName" tal:content="python: _('search_and')"></label><br/>
|
||||
</tal:operator>
|
||||
<tal:comment replace="nothing">The list of values</tal:comment>
|
||||
<select tal:attributes="name widgetName; size widget/height" multiple="multiple">
|
||||
<tal:option repeat="v python: tool.getSearchValues(name, className)">
|
||||
<option tal:define="uid python: v[0];
|
||||
title python: tool.getReferenceLabel(name, v[1], className)"
|
||||
tal:attributes="value uid; title title"
|
||||
tal:content="python: tool.truncateValue(title, widget)">
|
||||
</option>
|
||||
</tal:option>
|
||||
</select>
|
||||
</metal:search>
|
||||
|
|
|
@ -169,8 +169,15 @@ class UserWrapper(AbstractWrapper):
|
|||
del self.o.__ac_local_roles__[None]
|
||||
return self._callCustom('onEdit', created)
|
||||
|
||||
def mayEdit(self): return self._callCustom('mayEdit')
|
||||
def mayDelete(self): return self._callCustom('mayDelete')
|
||||
def mayEdit(self):
|
||||
custom = self._getCustomMethod('mayEdit')
|
||||
if custom: return self._callCustom('mayEdit')
|
||||
else: return True
|
||||
|
||||
def mayDelete(self):
|
||||
custom = self._getCustomMethod('mayDelete')
|
||||
if custom: return self._callCustom('mayDelete')
|
||||
else: return True
|
||||
|
||||
def getZopeUser(self):
|
||||
'''Gets the Zope user corresponding to this user.'''
|
||||
|
|
|
@ -132,16 +132,21 @@ class AbstractWrapper(object):
|
|||
if other: return cmp(self.o, other.o)
|
||||
return 1
|
||||
|
||||
def _getCustomMethod(self, methodName):
|
||||
'''See docstring of _callCustom below.'''
|
||||
if len(self.__class__.__bases__) > 1:
|
||||
# There is a custom user class
|
||||
custom = self.__class__.__bases__[-1]
|
||||
if custom.__dict__.has_key(methodName):
|
||||
return custom.__dict__[methodName]
|
||||
|
||||
def _callCustom(self, methodName, *args, **kwargs):
|
||||
'''This wrapper implements some methods like "validate" and "onEdit".
|
||||
If the user has defined its own wrapper, its methods will not be
|
||||
called. So this method allows, from the methods here, to call the
|
||||
user versions.'''
|
||||
if len(self.__class__.__bases__) > 1:
|
||||
# There is a custom user class
|
||||
customUser = self.__class__.__bases__[-1]
|
||||
if customUser.__dict__.has_key(methodName):
|
||||
return customUser.__dict__[methodName](self, *args, **kwargs)
|
||||
custom = self._getCustomMethod(methodName)
|
||||
if custom: return custom(self, *args, **kwargs)
|
||||
|
||||
def getField(self, name): return self.o.getAppyType(name)
|
||||
def isEmpty(self, name):
|
||||
|
|
Loading…
Reference in a new issue