diff --git a/fields/__init__.py b/fields/__init__.py index 3da84a9..fd952ab 100644 --- a/fields/__init__.py +++ b/fields/__init__.py @@ -138,10 +138,11 @@ class Field: ''') def __init__(self, validator, multiplicity, default, show, page, group, - layouts, move, indexed, searchable, specificReadPermission, - specificWritePermission, width, height, maxChars, colspan, - master, masterValue, focus, historized, mapping, label, - sdefault, scolspan, swidth, sheight, persist, view, xml): + layouts, move, indexed, mustIndex, searchable, + specificReadPermission, specificWritePermission, width, height, + maxChars, colspan, master, masterValue, focus, historized, + mapping, label, sdefault, scolspan, swidth, sheight, persist, + view, xml): # The validator restricts which values may be defined. It can be an # interval (1,None), a list of string values ['choice1', 'choice2'], # a regular expression, a custom function, a Selection instance, etc. @@ -167,6 +168,12 @@ class Field: # If indexed is True, a database index will be set on the field for # fast access. self.indexed = indexed + # If "mustIndex", True by default, is specified, it must be a method + # returning a boolean value. Indexation will only occur when this value + # is True. + self.mustIndex = mustIndex + if not mustIndex and not callable(mustIndex): + raise Exception('Value for param "mustIndex" must be a method.') # If specified "searchable", the field will be added to some global # index allowing to perform application-wide, keyword searches. self.searchable = searchable @@ -605,6 +612,9 @@ class Field: If p_forSearch is True, it will return a "string" version of the index value suitable for a global search.''' + # Must we produce an index value? + if not self.getAttribute(obj, 'mustIndex'): return + # Start by getting the field value on p_obj res = self.getValue(obj) # Zope catalog does not like unicode strings if isinstance(res, unicode): res = res.encode('utf-8') diff --git a/fields/action.py b/fields/action.py index d0df2d0..c19bbe5 100644 --- a/fields/action.py +++ b/fields/action.py @@ -54,12 +54,12 @@ class Action(Field): def __init__(self, validator=None, multiplicity=(1,1), default=None, show=('view', 'result'), page='main', group=None, layouts=None, - move=0, indexed=False, searchable=False, - specificReadPermission=False, specificWritePermission=False, - width=None, height=None, maxChars=None, colspan=1, action=None, - result='computation', confirm=False, master=None, - masterValue=None, focus=False, historized=False, mapping=None, - label=None, icon=None, view=None, xml=None): + move=0, specificReadPermission=False, + specificWritePermission=False, width=None, height=None, + maxChars=None, colspan=1, action=None, result='computation', + confirm=False, master=None, masterValue=None, focus=False, + historized=False, mapping=None, label=None, icon=None, + view=None, xml=None): # Can be a single method or a list/tuple of methods self.action = action # For the 'result' param: @@ -77,7 +77,7 @@ class Action(Field): # If no p_icon is specified, "action.png" will be used self.icon = icon or 'action' Field.__init__(self, None, (0,1), default, show, page, group, layouts, - move, indexed, False, specificReadPermission, + move, False, True, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, mapping, label, None, None, None, None, False, view, xml) diff --git a/fields/boolean.py b/fields/boolean.py index c26a2e9..c4082c2 100644 --- a/fields/boolean.py +++ b/fields/boolean.py @@ -79,17 +79,18 @@ class Boolean(Field): def __init__(self, validator=None, multiplicity=(0,1), default=None, show=True, page='main', group=None, layouts = None, move=0, - indexed=False, searchable=False, specificReadPermission=False, - specificWritePermission=False, width=None, height=None, - maxChars=None, colspan=1, master=None, masterValue=None, - focus=False, historized=False, mapping=None, label=None, - sdefault=False, scolspan=1, swidth=None, sheight=None, - persist=True, render='checkbox', view=None, xml=None): + indexed=False, mustIndex=True, searchable=False, + specificReadPermission=False, specificWritePermission=False, + width=None, height=None, maxChars=None, colspan=1, master=None, + masterValue=None, focus=False, historized=False, mapping=None, + label=None, sdefault=False, scolspan=1, swidth=None, + sheight=None, persist=True, render='checkbox', view=None, + xml=None): # By default, a boolean is edited via a checkbox. It can also be edited # via 2 radio buttons (p_render="radios"). self.render = render Field.__init__(self, validator, multiplicity, default, show, page, - group, layouts, move, indexed, searchable, + group, layouts, move, indexed, mustIndex, searchable, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, mapping, label, sdefault, scolspan, swidth, diff --git a/fields/calendar.py b/fields/calendar.py index b73d39d..cdceec1 100644 --- a/fields/calendar.py +++ b/fields/calendar.py @@ -219,7 +219,7 @@ class Calendar(Field): endDate=None, defaultDate=None, preCompute=None, applicableEvents=None, view=None, xml=None): Field.__init__(self, validator, (0,1), default, show, page, group, - layouts, move, False, False, specificReadPermission, + layouts, move, False, True, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, False, mapping, label, None, None, None, None, True, view, xml) diff --git a/fields/computed.py b/fields/computed.py index ef18833..a4ee3d1 100644 --- a/fields/computed.py +++ b/fields/computed.py @@ -32,13 +32,13 @@ class Computed(Field): def __init__(self, validator=None, multiplicity=(0,1), default=None, show=None, page='main', group=None, layouts=None, move=0, - indexed=False, searchable=False, specificReadPermission=False, - specificWritePermission=False, width=None, height=None, - maxChars=None, colspan=1, method=None, formatMethod=None, - plainText=False, master=None, masterValue=None, focus=False, - historized=False, mapping=None, label=None, sdefault='', - scolspan=1, swidth=None, sheight=None, context=None, view=None, - xml=None): + indexed=False, mustIndex=True, searchable=False, + specificReadPermission=False, specificWritePermission=False, + width=None, height=None, maxChars=None, colspan=1, method=None, + formatMethod=None, plainText=False, master=None, + masterValue=None, focus=False, historized=False, mapping=None, + label=None, sdefault='', scolspan=1, swidth=None, sheight=None, + context=None, view=None, xml=None): # The Python method used for computing the field value, or a PX. self.method = method # A specific method for producing the formatted value of this field. @@ -65,7 +65,7 @@ class Computed(Field): # If method is a PX, its context can be given in p_context. self.context = context Field.__init__(self, None, multiplicity, default, show, page, group, - layouts, move, indexed, searchable, + layouts, move, indexed, mustIndex, searchable, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, mapping, label, sdefault, scolspan, swidth, diff --git a/fields/date.py b/fields/date.py index 3c14d0d..eb6cf95 100644 --- a/fields/date.py +++ b/fields/date.py @@ -165,12 +165,12 @@ class Date(Field): startYear=time.localtime()[0]-10, endYear=time.localtime()[0]+10, reverseYears=False, show=True, page='main', group=None, layouts=None, move=0, - indexed=False, searchable=False, specificReadPermission=False, - specificWritePermission=False, width=None, height=None, - maxChars=None, colspan=1, master=None, masterValue=None, - focus=False, historized=False, mapping=None, label=None, - sdefault=None, scolspan=1, swidth=None, sheight=None, - persist=True, view=None, xml=None): + indexed=False, mustIndex=True, searchable=False, + specificReadPermission=False, specificWritePermission=False, + width=None, height=None, maxChars=None, colspan=1, master=None, + masterValue=None, focus=False, historized=False, mapping=None, + label=None, sdefault=None, scolspan=1, swidth=None, + sheight=None, persist=True, view=None, xml=None): self.format = format self.calendar = calendar self.startYear = startYear @@ -179,7 +179,7 @@ class Date(Field): # self.startYear to self.endYear will be listed in reverse order. self.reverseYears = reverseYears Field.__init__(self, validator, multiplicity, default, show, page, - group, layouts, move, indexed, searchable, + group, layouts, move, indexed, mustIndex, searchable, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, mapping, label, sdefault, scolspan, swidth, diff --git a/fields/file.py b/fields/file.py index 15baa68..56cbf95 100644 --- a/fields/file.py +++ b/fields/file.py @@ -344,15 +344,14 @@ class File(Field): def __init__(self, validator=None, multiplicity=(0,1), default=None, show=True, page='main', group=None, layouts=None, move=0, - indexed=False, searchable=False, specificReadPermission=False, - specificWritePermission=False, width=None, height=None, - maxChars=None, colspan=1, master=None, masterValue=None, - focus=False, historized=False, mapping=None, label=None, - isImage=False, sdefault='', scolspan=1, swidth=None, - sheight=None, view=None, xml=None): + specificReadPermission=False, specificWritePermission=False, + width=None, height=None, maxChars=None, colspan=1, master=None, + masterValue=None, focus=False, historized=False, mapping=None, + label=None, isImage=False, sdefault='', scolspan=1, + swidth=None, sheight=None, view=None, xml=None): self.isImage = isImage Field.__init__(self, validator, multiplicity, default, show, page, - group, layouts, move, indexed, False, + group, layouts, move, False, True, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, mapping, label, sdefault, scolspan, swidth, diff --git a/fields/float.py b/fields/float.py index c9a7a86..7f88ae0 100644 --- a/fields/float.py +++ b/fields/float.py @@ -51,13 +51,13 @@ class Float(Field): def __init__(self, validator=None, multiplicity=(0,1), default=None, show=True, page='main', group=None, layouts=None, move=0, - indexed=False, searchable=False, specificReadPermission=False, - specificWritePermission=False, width=5, height=None, - maxChars=13, colspan=1, master=None, masterValue=None, - focus=False, historized=False, mapping=None, label=None, - sdefault=('',''), scolspan=1, swidth=None, sheight=None, - persist=True, precision=None, sep=(',', '.'), tsep=' ', - view=None, xml=None): + indexed=False, mustIndex=True, searchable=False, + specificReadPermission=False, specificWritePermission=False, + width=5, height=None, maxChars=13, colspan=1, master=None, + masterValue=None, focus=False, historized=False, mapping=None, + label=None, sdefault=('',''), scolspan=1, swidth=None, + sheight=None, persist=True, precision=None, sep=(',', '.'), + tsep=' ', view=None, xml=None): # The precision is the number of decimal digits. This number is used # for rendering the float, but the internal float representation is not # rounded. @@ -75,7 +75,7 @@ class Float(Field): 'separator.' % sep) self.tsep = tsep Field.__init__(self, validator, multiplicity, default, show, page, - group, layouts, move, indexed, False, + group, layouts, move, indexed, mustIndex, searchable, specificReadPermission, specificWritePermission, width, height, maxChars, colspan, master, masterValue, focus, historized, mapping, label, sdefault, scolspan, swidth, diff --git a/fields/info.py b/fields/info.py index 4de8355..684f525 100644 --- a/fields/info.py +++ b/fields/info.py @@ -26,13 +26,12 @@ class Info(Field): def __init__(self, validator=None, multiplicity=(1,1), default=None, show='view', page='main', group=None, layouts=None, move=0, - indexed=False, searchable=False, specificReadPermission=False, - specificWritePermission=False, width=None, height=None, - maxChars=None, colspan=1, master=None, masterValue=None, - focus=False, historized=False, mapping=None, label=None, - view=None, xml=None): + specificReadPermission=False, specificWritePermission=False, + width=None, height=None, maxChars=None, colspan=1, master=None, + masterValue=None, focus=False, historized=False, mapping=None, + label=None, view=None, xml=None): Field.__init__(self, None, (0,1), default, show, page, group, layouts, - move, indexed, False, specificReadPermission, + move, False, True, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, mapping, label, None, None, None, None, False, view, xml) diff --git a/fields/integer.py b/fields/integer.py index 322a4e0..653fec7 100644 --- a/fields/integer.py +++ b/fields/integer.py @@ -48,14 +48,14 @@ class Integer(Field): def __init__(self, validator=None, multiplicity=(0,1), default=None, show=True, page='main', group=None, layouts=None, move=0, - indexed=False, searchable=False, specificReadPermission=False, - specificWritePermission=False, width=5, height=None, - maxChars=13, colspan=1, master=None, masterValue=None, - focus=False, historized=False, mapping=None, label=None, - sdefault=('',''), scolspan=1, swidth=None, sheight=None, - persist=True, view=None, xml=None): + indexed=False, mustIndex=True, searchable=False, + specificReadPermission=False, specificWritePermission=False, + width=5, height=None, maxChars=13, colspan=1, master=None, + masterValue=None, focus=False, historized=False, mapping=None, + label=None, sdefault=('',''), scolspan=1, swidth=None, + sheight=None, persist=True, view=None, xml=None): Field.__init__(self, validator, multiplicity, default, show, page, - group, layouts, move, indexed, searchable, + group, layouts, move, indexed, mustIndex, searchable, specificReadPermission, specificWritePermission, width, height, maxChars, colspan, master, masterValue, focus, historized, mapping, label, sdefault, scolspan, swidth, diff --git a/fields/list.py b/fields/list.py index 279c3da..93b9eb5 100644 --- a/fields/list.py +++ b/fields/list.py @@ -74,14 +74,13 @@ class List(Field): def __init__(self, fields, validator=None, multiplicity=(0,1), default=None, show=True, page='main', group=None, layouts=None, move=0, - indexed=False, searchable=False, specificReadPermission=False, - specificWritePermission=False, width='', height=None, - maxChars=None, colspan=1, master=None, masterValue=None, - focus=False, historized=False, mapping=None, label=None, - subLayouts=Table('frv', width=None), widths=None, view=None, - xml=None): + specificReadPermission=False, specificWritePermission=False, + width='', height=None, maxChars=None, colspan=1, master=None, + masterValue=None, focus=False, historized=False, mapping=None, + label=None, subLayouts=Table('frv', width=None), widths=None, + view=None, xml=None): Field.__init__(self, validator, multiplicity, default, show, page, - group, layouts, move, indexed, False, + group, layouts, move, False, True, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, mapping, label, None, None, None, None, diff --git a/fields/ogone.py b/fields/ogone.py index d3a4ff7..af589cb 100644 --- a/fields/ogone.py +++ b/fields/ogone.py @@ -51,7 +51,7 @@ class Ogone(Field): colspan=1, master=None, masterValue=None, focus=False, mapping=None, label=None, view=None, xml=None): Field.__init__(self, None, (0,1), None, show, page, group, layouts, - move, False, False,specificReadPermission, + move, False, True, False,specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, False, mapping, label, None, None, None, None, False, view, xml) diff --git a/fields/pod.py b/fields/pod.py index 5fcea6f..fb57888 100644 --- a/fields/pod.py +++ b/fields/pod.py @@ -150,16 +150,15 @@ class Pod(Field): pxEdit = pxSearch = '' def __init__(self, validator=None, default=None, show=('view', 'result'), - page='main', group=None, layouts=None, move=0, indexed=False, - searchable=False, specificReadPermission=False, - specificWritePermission=False, width=None, height=None, - maxChars=None, colspan=1, master=None, masterValue=None, - focus=False, historized=False, mapping=None, label=None, - template=None, templateName=None, showTemplate=None, - freezeTemplate=None, maxPerRow=5, context=None, - stylesMapping={}, formats=None, getChecked=None, mailing=None, - mailingName=None, showMailing=None, mailingInfo=None, - view=None, xml=None): + page='main', group=None, layouts=None, move=0, + specificReadPermission=False, specificWritePermission=False, + width=None, height=None, maxChars=None, colspan=1, master=None, + masterValue=None, focus=False, historized=False, mapping=None, + label=None, template=None, templateName=None, + showTemplate=None, freezeTemplate=None, maxPerRow=5, + context=None, stylesMapping={}, formats=None, getChecked=None, + mailing=None, mailingName=None, showMailing=None, + mailingInfo=None, view=None, xml=None): # Param "template" stores the path to the pod template(s). If there is # a single template, a string is expected. Else, a list or tuple of # strings is expected. Every such path must be relative to your @@ -273,7 +272,7 @@ class Pod(Field): # self.mailing) and that returns an instance of class Mailing (above). self.mailingInfo = mailingInfo Field.__init__(self, None, (0,1), default, show, page, group, layouts, - move, indexed, searchable, specificReadPermission, + move, False, True, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, mapping, label, None, None, None, None, True, view, xml) diff --git a/fields/ref.py b/fields/ref.py index f91c348..a85be14 100644 --- a/fields/ref.py +++ b/fields/ref.py @@ -519,11 +519,11 @@ class Ref(Field): afterLink=None, afterUnlink=None, back=None, show=True, page='main', group=None, layouts=None, showHeaders=False, shownInfo=None, select=None, maxPerPage=30, move=0, - indexed=False, searchable=False, specificReadPermission=False, - specificWritePermission=False, width=None, height=5, - maxChars=None, colspan=1, master=None, masterValue=None, - focus=False, historized=False, mapping=None, label=None, - queryable=False, queryFields=None, queryNbCols=1, + indexed=False, mustIndex=True, searchable=False, + specificReadPermission=False, specificWritePermission=False, + width=None, height=5, maxChars=None, colspan=1, master=None, + masterValue=None, focus=False, historized=False, mapping=None, + label=None, queryable=False, queryFields=None, queryNbCols=1, navigable=False, changeOrder=True, numbered=False, checkboxes=True, checkboxesDefault=None, sdefault='', scolspan=1, swidth=None, sheight=None, sselect=None, @@ -710,7 +710,7 @@ class Ref(Field): # alternative URL for the tied object that is shown within the menu. self.menuUrlMethod = menuUrlMethod Field.__init__(self, validator, multiplicity, default, show, page, - group, layouts, move, indexed, False, + group, layouts, move, indexed, mustIndex, searchable, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, mapping, label, sdefault, scolspan, swidth, @@ -958,22 +958,29 @@ class Ref(Field): def getIndexType(self): return 'ListIndex' + def getValidCatalogValue(self, value): + '''p_value is the new value we want to index in the catalog, for this + field, for some object. p_value as is may not be an acceptable value + for the catalog: if it it an empty list, instead of using it, the + catalog will keep the previously catalogued value! For this case, + this method produces an "empty" value that will really overwrite the + previous one. Moreover, the catalog does not like persistent + lists.''' + # The index does not like persistent lists. Moreover, I don't want to + # give to anyone access to the persistent list in the DB. + if value: return list(value) + # Ugly catalog: if I return an empty list, the previous value is kept + return [''] + 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 list of the linked objects' titles instead.''' + # Must we produce an index value? + if not self.getAttribute(obj, 'mustIndex'): return if not forSearch: res = getattr(obj.aq_base, self.name, None) - if res: - # The index does not like persistent lists. Moreover, I don't - # want to give to anyone access to the persistent list in the - # DB. - res = list(res) - else: - # Ugly catalog: if I return an empty list, the previous value - # is kept. - res = [''] - return res + return self.getValidCatalogValue(res) else: # For the global search: return linked objects' titles return ' '.join([o.getShownValue('title') \ diff --git a/fields/string.py b/fields/string.py index 9d47a12..1bec50b 100644 --- a/fields/string.py +++ b/fields/string.py @@ -373,7 +373,7 @@ class String(Field): def __init__(self, validator=None, multiplicity=(0,1), default=None, format=LINE, show=True, page='main', group=None, layouts=None, - move=0, indexed=False, searchable=False, + move=0, indexed=False, mustIndex=True, searchable=False, specificReadPermission=False, specificWritePermission=False, width=None, height=None, maxChars=None, colspan=1, master=None, masterValue=None, focus=False, historized=False, mapping=None, @@ -421,7 +421,7 @@ class String(Field): # that can, for example, return an internationalized value. self.placeholder = placeholder Field.__init__(self, validator, multiplicity, default, show, page, - group, layouts, move, indexed, searchable, + group, layouts, move, indexed, mustIndex, searchable, specificReadPermission, specificWritePermission, width, height, maxChars, colspan, master, masterValue, focus, historized, mapping, label, sdefault, scolspan, swidth, @@ -684,12 +684,29 @@ class String(Field): '''Extracts pure text from XHTML p_value.''' return XhtmlTextExtractor(raiseOnError=False).parse('

%s

' % value) - emptyStringTuple = ('',) - emptyValuesCatalogIgnored = (None, '') + def getValidCatalogValue(self, value, forSearch): + '''p_value is the new value we want to index in the catalog, for this + field, for some object. p_value as is may not be an acceptable value + for the catalog: if it represents some empty value, like an empty + string, None or an empty tuple, instead of using it, the catalog will + keep the previously catalogued value! For those cases, this method + produces "empty" values that will really overwrite previous ones.''' + # Ugly catalog: if I give an empty tuple as index value, it keeps the + # previous value. If I give him a tuple containing an empty string, it + # is ok. + if isinstance(value, tuple) and not value: + value = forSearch and ' ' or ('',) + # Ugly catalog: if value is an empty string or None, it keeps the + # previous index value. + elif value in (None, ''): return ' ' + return value def getIndexValue(self, obj, forSearch=False): '''Pure text must be extracted from rich content; multilingual content must be concatenated.''' + # Must we produce an index value? + if not self.getAttribute(obj, 'mustIndex'): + return self.getValidCatalogValue(None, forSearch) isXhtml = self.format == String.XHTML if self.isMultilingual(obj): res = self.getValue(obj) @@ -703,14 +720,7 @@ class String(Field): else: res = Field.getIndexValue(self, obj, forSearch) if res and isXhtml: res = self.extractText(res) - # Ugly catalog: if I give an empty tuple as index value, it keeps the - # previous value. If I give him a tuple containing an empty string, it - # is ok. - if isinstance(res, tuple) and not res: res = self.emptyStringTuple - # Ugly catalog: if value is an empty string or None, it keeps the - # previous index value. - if res in self.emptyValuesCatalogIgnored: res = ' ' - return res + return self.getValidCatalogValue(res, forSearch) def getPossibleValues(self, obj, withTranslations=False, withBlankValue=False, className=None, diff --git a/shared/utils.py b/shared/utils.py index 0cdcc7e..bb64c1f 100644 --- a/shared/utils.py +++ b/shared/utils.py @@ -217,7 +217,7 @@ def executeCommand(cmd): return res # ------------------------------------------------------------------------------ -charsIgnore = u'.,:;*+=~?%^\'’"<>{}[]|\t\\' +charsIgnore = u'.,:;*+=~?%^\'’"<>{}[]|\t\\°' fileNameIgnore = charsIgnore + u' $£€/' extractIgnore = charsIgnore + '()' alphaRex = re.compile('[a-zA-Z]')