Commit dd102ec0 authored by Romain Courteaud's avatar Romain Courteaud

Unify RelationField and MultiRelationField (in order to fix both of them at the

same time).
Bug fix: do not write the relation_index in the ZODB anymore.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@6202 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent b9d9e7bf
......@@ -38,7 +38,6 @@ from urllib import quote
from Globals import InitializeClass, PersistentMapping, DTMLFile, get_request
from AccessControl import Unauthorized, getSecurityManager, ClassSecurityInfo
from ZODB.POSException import ConflictError
from Products.ERP5Type.Utils import UpperCase
import psyco
......@@ -111,6 +110,7 @@ def get_value(self, id, **kw):
else:
# get normal value
value = self.get_orig_value(id)
# For the 'default' value, we try to get a default value
if id == 'default':
if (value is None or value == '' or value == [] or value == ()) \
......@@ -355,7 +355,6 @@ class ERP5Form(ZMIForm, ZopePageTemplate):
# Proxy method to PageTemplate
def __call__(self, *args, **kwargs):
self._v_relation_field_index = 0 # We initialize here an index which is used to generate different method ids for every field
if not kwargs.has_key('args'):
kwargs['args'] = args
form = self
......@@ -370,6 +369,11 @@ class ERP5Form(ZMIForm, ZopePageTemplate):
extra_context['form'] = self
extra_context['container'] = container ## PROBLEM NOT TAKEN INTO ACCOUNT
extra_context['here'] = object
# We initialize here an index which is used to generate
# different method ids for every field
request = extra_context['request']
# XXX We must not use a counter, but a ID for each field
request.set('_v_relation_field_index', 0)
return pt.pt_render(extra_context=extra_context)
def _exec(self, bound_names, args, kw):
......@@ -456,6 +460,7 @@ class ERP5Form(ZMIForm, ZopePageTemplate):
# More optimizations
#psyco.bind(ERP5Field)
# XXX Not useful, as we patch those methods in FormulatorPatch
psyco.bind(Field.render)
psyco.bind(Field._render_helper)
psyco.bind(Field.get_value)
......
##############################################################################
#
# Copyright (c) 2002, 2004 Nexedi SARL and Contributors. All Rights Reserved.
# Copyright (c) 2002, 2004, 2006 Nexedi SARL and Contributors.
# All Rights Reserved.
# Jean-Paul Smets-Solanes <jp@nexedi.com>
# Romain Courteaud <romain@nexedi.com>
#
......@@ -29,19 +30,22 @@
from Products.Formulator import Widget, Validator
from Products.Formulator.Field import ZMIField
from Products.Formulator.DummyField import fields
from Products.ERP5Type.Utils import convertToUpperCase
from Products.CMFCore.utils import getToolByName
from Products.ERP5Form import RelationField
from Products.ERP5Form.RelationField import MAX_SELECT, new_content_prefix
from Globals import get_request
from Products.PythonScripts.Utility import allow_class
from Products.ERP5Type.Message import Message
from AccessControl import ClassSecurityInfo
import string
from types import StringType
from zLOG import LOG
#MAX_SELECT = 50 # Max. number of catalog result
#new_content_prefix = '_newContent_'
from Products.Formulator.DummyField import fields
from Globals import get_request
# Max. number of catalog result
MAX_SELECT = 30
NEW_CONTENT_PREFIX = '_newContent_'
# Key for sub listfield
SUB_FIELD_ID = 'relation'
ITEM_ID = 'item'
def checkSameKeys(a , b):
"""
......@@ -52,199 +56,352 @@ def checkSameKeys(a , b):
for ka in a:
if (not ka in b) and (ka != ''):
same = 0
for kb in b:
if (not kb in a) and (kb != ''):
same = 0
break
if same:
for kb in b:
if (not kb in a) and (kb != ''):
same = 0
break
return same
class MultiRelationStringFieldWidget(Widget.LinesTextAreaWidget, RelationField.RelationStringFieldWidget):
class MultiRelationStringFieldWidget(Widget.LinesTextAreaWidget,
Widget.TextWidget,
Widget.ListWidget):
"""
RelationStringField widget
Works like a string field but includes one buttons
- one search button which updates the field and sets a relation
- creates object if not there
"""
local_property_names = ['update_method', 'jump_method', 'allow_jump',
'base_category', 'portal_type', 'allow_creation',
'container_getter_id', 'catalog_index',
'relation_setter_id', 'columns', 'sort',
'parameter_list','list_method',
'first_item', 'items', 'size', 'extra_item']
property_names = Widget.LinesTextAreaWidget.property_names + \
Widget.TextWidget.property_names + \
local_property_names
# XXX Field to remove...
update_method = fields.StringField('update_method',
title='Update Method',
description=(
"The method to call to set the relation. Required."),
default="Base_validateRelation",
required=1)
jump_method = fields.StringField('jump_method',
title='Jump Method',
description=(
"The method to call to jump to the relation. Required."),
default="Base_jumpToRelatedDocument",
required=1)
allow_jump = fields.CheckBoxField('allow_jump',
title='Allow Jump',
description=(
"Do we allow to jump to the relation ?"),
default=1,
required=0)
base_category = fields.StringField('base_category',
title='Base Category',
description=(
"The method to call to set the relation. Required."),
default="",
required=1)
portal_type = fields.ListTextAreaField('portal_type',
title='Portal Type',
description=(
"The method to call to set the relation. Required."),
default="",
required=1)
allow_creation = fields.CheckBoxField('allow_creation',
title='Allow Creation',
description=(
"Do we allow to create new objects ?"),
default=1,
required=0)
container_getter_id = fields.StringField('container_getter_id',
title='Container Getter Method',
description=(
"The method to call to get a container object."),
default="",
required=0)
catalog_index = fields.StringField('catalog_index',
title='Catalog Index',
description=(
"The method to call to set the relation. Required."),
default="",
required=1)
# XXX Is it a good idea to keep such a field ??
# User can redefine setter method with a script (and so, don't use the API)
relation_setter_id = fields.StringField('relation_setter_id',
title='Relation Update Method',
description=(
"The method to invoke in order to update the relation"),
default="",
required=0)
size = fields.IntegerField('size',
title='Size',
description=(
"The display size in rows of the field. If set to 1, the "
"widget will be displayed as a drop down box by many browsers, "
"if set to something higher, a list will be shown. Required."),
default=1,
required=1)
columns = fields.ListTextAreaField('columns',
title="Columns",
description=(
"A list of attributes names to display."),
default=[],
required=0)
sort = fields.ListTextAreaField('sort',
title='Default Sort',
description=('The default sort keys and order'),
default=[],
required=0)
parameter_list = fields.ListTextAreaField('parameter_list',
title="Parameter List",
description=(
"A list of paramters used for the portal_catalog."),
default=[],
required=0)
list_method = fields.MethodField('list_method',
title='List Method',
description=('The method to use to list'
'objects'),
default='',
required=0)
# delete double in order to keep a usable ZMI...
# XXX need to keep order !
#property_names = dict([(i,0) for i in property_names]).keys()
_v_dict = {}
_v_property_name_list = []
for property_name in property_names:
if not _v_dict.has_key(property_name):
_v_property_name_list.append(property_name)
_v_dict[property_name] = 1
property_names = _v_property_name_list
default_widget_rendering_instance = Widget.LinesTextAreaWidgetInstance
def _generateRenderValueList(self, field, key, value_list, REQUEST):
result_list = []
need_validation = 0
####################################
# Check value
####################################
if isinstance(value_list, StringType):
# Value is a string, reformat it correctly
value_list = value_list.split("\n")
# Check all relation
for i in range(len(value_list)):
###################################
# Sub field
###################################
relation_field_id = field.generate_subfield_key("%s_%s" % \
(SUB_FIELD_ID, i),
key=key)
relation_item_id = field.generate_subfield_key("%s_%s" % \
(ITEM_ID, i),
key=key)
relation_item_list = REQUEST.get(relation_item_id, None)
value = value_list[i]
if (relation_item_list is not None) and \
(value != ''):
need_validation = 1
if value is None :
# rather than displaying nothing, display a marker when the
# property is not set
# XXX Translate ?
value = '??? (no value)'
# If we get a empty string, display nothing !
if value != '':
result_list.append((Widget.TextWidgetInstance, relation_field_id,
relation_item_list, value, i))
if not need_validation:
###################################
# Main field
###################################
result_list = [(Widget.LinesTextAreaWidgetInstance, None, [],
value_list, None)]
return result_list
def render(self, field, key, value, REQUEST):
"""
RelationStringField widget
Works like a string field but includes one buttons
- one search button which updates the field and sets a relation
- creates object if not there
Render text input field.
"""
property_names = Widget.LinesTextAreaWidget.property_names + \
RelationField.RelationStringFieldWidget.property_names
# delete double in order to keep a usable ZMI...
#property_names = dict([(i,0) for i in property_names]).keys() # XXX need to keep order !
_v_dict = {}
_v_property_name_list = []
for property_name in property_names:
if not _v_dict.has_key(property_name):
_v_property_name_list.append(property_name)
_v_dict[property_name] = 1
property_names = _v_property_name_list
def render(self, field, key, value, REQUEST):
"""
Render text input field.
"""
here = REQUEST['here']
relation_field_id = 'relation_%s' % key
relation_item_id = 'item_%s' % key
portal_url = getToolByName(here, 'portal_url')
portal_url_string = portal_url()
portal_object = portal_url.getPortalObject()
if type(value) == type(''):
# Value is a string, reformat it correctly
value_list = string.split(value, "\r\n")
else:
value_list = value
need_validation = 0
# Check all relation
for i in range( len(value_list) ):
relation_field_id = 'relation_%s_%s' % ( key, i )
relation_item_id = 'item_%s_%s' % ( key, i )
if REQUEST.has_key(relation_item_id) and value_list[i] != '':
need_validation = 1
break
html_string = ''
if need_validation:
# Check all relation
for i in range( len(value_list) ):
value = value_list[i]
relation_field_id = 'relation_%s_%s' % ( key, i )
relation_item_id = 'item_%s_%s' % ( key, i )
# If we get a empty string, display nothing !
if value == '':
pass
else:
html_string += Widget.TextWidget.render(self, field, key, value, REQUEST)
if REQUEST.has_key(relation_item_id):
relation_item_list = REQUEST.get(relation_item_id)
if relation_item_list != []:
# Define default tales on the fly
tales_expr = field.tales.get('items', None)
defined_tales = 0
if not tales_expr:
defined_tales = 1
from Products.Formulator.TALESField import TALESMethod
field.tales['items'] = TALESMethod('REQUEST/relation_item_list')
REQUEST['relation_item_list'] = relation_item_list
html_string += '&nbsp;%s&nbsp;' % Widget.ListWidget.render(self,
field, relation_field_id, None, REQUEST)
REQUEST['relation_item_list'] = None
if defined_tales:
# Delete default tales on the fly
field.tales['items'] = None
else:
html_string += '&nbsp;<input type="image" src="%s/images/exec16.png" value="update..." name="%s/portal_selections/viewSearchRelatedDocumentDialog%s_%s:method"/>' \
% (portal_url_string, portal_object.getPath(), field.aq_parent._v_relation_field_index, i)
html_string += '<br/>'
html_string = ''
relation_field_index = REQUEST.get('_v_relation_field_index')
render_parameter_list = self._generateRenderValueList(
field, key, value,
REQUEST)
####################################
# Render subfield
####################################
html_string_list = []
for widget_instance, relation_field_id, relation_item_list, \
value_instance, sub_index in render_parameter_list:
sub_html_string = widget_instance.render(field, key,
value_instance, REQUEST)
if relation_item_list is not None:
if relation_item_list != []:
####################################
# Render listfield
####################################
tales_expr = field.tales.get('items', None)
defined_tales = 0
if not tales_expr:
defined_tales = 1
from Products.Formulator.TALESField import TALESMethod
# XXX XXX Do not write in the ZODB
field.tales['items'] = TALESMethod('REQUEST/relation_item_list')
REQUEST['relation_item_list'] = relation_item_list
sub_html_string += '&nbsp;%s&nbsp;' % \
Widget.ListWidgetInstance.render(
field, relation_field_id, None, REQUEST)
REQUEST['relation_item_list'] = None
if defined_tales:
# Delete default tales on the fly
field.tales['items'] = None
else:
clean_value_list = []
for v in value_list :
# rather than displaying nothing, display a marker when the
# property is not set
if v is None : v = '??? (no value)'
clean_value_list += [v]
# no modification made, we can display only a lines text area widget
html_string += Widget.LinesTextAreaWidget.render(self, field, key, clean_value_list, REQUEST)
html_string += '&nbsp;<input type="image" src="%s/images/exec16.png" value="update..." name="%s/portal_selections/viewSearchRelatedDocumentDialog%s:method"/>' \
% (portal_url_string, portal_object.getPath(), field.aq_parent._v_relation_field_index)
if value_list not in ((), [], None, ['']) and value_list == field.get_value('default') and field.get_value('allow_jump') == 1 :
if REQUEST.get('selection_name') is not None:
html_string += '&nbsp;&nbsp;<a href="%s?field_id=%s&form_id=%s&selection_name=%s&selection_index=%s"><img src="%s/images/jump.png"/></a>' \
% (field.get_value('jump_method'), field.id, field.aq_parent.id, REQUEST.get('selection_name'), REQUEST.get('selection_index'),portal_url_string)
else:
html_string += '&nbsp;&nbsp;<a href="%s?field_id=%s&form_id=%s"><img src="%s/images/jump.png"/></a>' \
% (field.get_value('jump_method'), field.id, field.aq_parent.id,portal_url_string)
relation_field_index = getattr(field.aq_parent, '_v_relation_field_index', 0)
field.aq_parent._v_relation_field_index = relation_field_index + 1 # Increase index
return html_string
def render_view(self, field, value):
"""
Render text field.
"""
if field.get_value('allow_jump') == 0 :
return Widget.LinesTextAreaWidget.render_view(self, field, value)
REQUEST = get_request()
here = REQUEST['here']
portal_url = getToolByName(here, 'portal_url')
portal_url_string = portal_url()
# no modification made, we can display only a lines text area widget
html_string = Widget.LinesTextAreaWidget.render_view(self, field, value)
if value not in ((), [], None, ''):
if REQUEST.get('selection_name') is not None:
html_string += '&nbsp;&nbsp;<a href="%s?field_id=%s&form_id=%s&selection_name=%s&selection_index=%s"><img src="%s/images/jump.png"/></a>' \
% (field.get_value('jump_method'), field.id, field.aq_parent.id, REQUEST.get('selection_name'), REQUEST.get('selection_index'),portal_url_string)
else:
html_string += '&nbsp;&nbsp;<a href="%s?field_id=%s&form_id=%s"><img src="%s/images/jump.png"/></a>' \
% (field.get_value('jump_method'), field.id, field.aq_parent.id,portal_url_string)
return html_string
####################################
# Render wheel
####################################
sub_html_string += self.render_wheel(
field, value_instance, REQUEST,
relation_index=relation_field_index,
sub_index=sub_index)
html_string_list.append(sub_html_string)
####################################
# Generate html
####################################
html_string = '<br/>'.join(html_string_list)
####################################
# Render jump
####################################
if (value == field.get_value('default')):
# XXX Default rendering with value...
relation_html_string = self.render_relation_link(field, value,
REQUEST)
if relation_html_string != '':
html_string += '&nbsp;&nbsp;%s' % relation_html_string
####################################
# Update relation field index
####################################
REQUEST.set('_v_relation_field_index', relation_field_index + 1)
return html_string
def render_view(self, field, value):
"""
Render read only field.
"""
html_string = self.default_widget_rendering_instance.render_view(
field, value)
REQUEST = get_request()
relation_html_string = self.render_relation_link(field, value, REQUEST)
if relation_html_string != '':
html_string += '&nbsp;&nbsp;%s' % relation_html_string
return html_string
def render_wheel(self, field, value, REQUEST, relation_index=0,
sub_index=None):
"""
Render wheel used to display a listbox
"""
here = REQUEST['here']
portal_url = getToolByName(here, 'portal_url')
portal_url_string = portal_url()
portal_object = portal_url.getPortalObject()
if sub_index is None:
sub_index_string = ''
else:
sub_index_string = '_%s' % sub_index
return '&nbsp;<input type="image" ' \
'src="%s/images/exec16.png" value="update..." ' \
'name="%s/portal_selections/viewSearchRelatedDocumentDialog%s%s' \
':method"/>' % \
(portal_url_string, portal_object.getPath(),
relation_index, sub_index_string)
def render_relation_link(self, field, value, REQUEST):
"""
Render link to the related object.
"""
html_string = ''
here = REQUEST['here']
portal_url = getToolByName(here, 'portal_url')
portal_url_string = portal_url()
portal_object = portal_url.getPortalObject()
if (value not in ((), [], None, '')) and \
(field.get_value('allow_jump') == 1):
# Keep the selection name in the URL
if REQUEST.get('selection_name') is not None:
selection_name_html = '&selection_name=%s&selection_index=%s' % \
(REQUEST.get('selection_name'), REQUEST.get('selection_index'))
else:
selection_name_html = ''
# Generate plan link
html_string += '<a href="%s/%s?field_id=%s&form_id=%s%s">' \
'<img src="%s/images/jump.png" />' \
'</a>' % \
(here.absolute_url(),
field.get_value('jump_method'),
field.id, field.aq_parent.id,
selection_name_html,
portal_url_string)
return html_string
class MultiRelationEditor:
"""
A class holding all values required to update a relation
"""
def __init__(self, field_id, base_category, portal_type, portal_type_item, key, relation_setter_id, relation_editor_list):
def __init__(self, field_id, base_category,
portal_type_list,
portal_type_item, key, relation_setter_id,
relation_editor_list):
self.field_id = field_id
self.base_category = base_category
self.portal_type = portal_type
self.portal_type_list = portal_type_list
self.portal_type_item = portal_type_item
self.key = key
self.relation_setter_id = relation_setter_id
self.relation_editor_list = relation_editor_list
def __call__(self, REQUEST):
if self.relation_editor_list != None:
value_list = []
for i, value, uid, display_text in self.relation_editor_list:
for value, uid, display_text, relation_key, item_key in \
self.relation_editor_list:
value_list.append(value)
if uid is not None:
# Decorate the request so that we can display
# the select item in a popup
#relation_field_id = 'relation_%s_%s' % ( self.key, i )
#relation_item_id = 'item_%s_%s' % ( self.key, i )
relation_field_id = 'relation_field_%s_%s' % ( self.field_id, i )
relation_item_id = 'item_field_%s_%s' % ( self.field_id, i )
# XXX To be unified
relation_field_id = relation_key
relation_item_id = item_key
REQUEST.set(relation_item_id, ((display_text, uid),))
# XXX Is it useful ?
REQUEST.set(relation_field_id, uid)
REQUEST.set(self.field_id, value_list) # XXX Dirty
else:
# Make sure no default value appears
#REQUEST.set(self.field_id[len('field_'):], None)
REQUEST.set(self.field_id, None) # XXX Dirty
def view(self):
......@@ -255,276 +412,367 @@ class MultiRelationEditor:
relation_uid_list = []
relation_object_list = []
for i, value, uid, display_text in self.relation_editor_list:
for value, uid, display_text, relation_key, item_key in \
self.relation_editor_list:
if uid is not None:
if type(uid) is type('a') and uid.startswith(new_content_prefix):
if isinstance(uid, StringType) and \
uid.startswith(NEW_CONTENT_PREFIX):
# Create a new content
portal_type = uid[len(new_content_prefix):]
portal_type = uid[len(NEW_CONTENT_PREFIX):]
portal_module = None
for p_item in self.portal_type_item:
if p_item[0] == portal_type:
portal_module = o.getPortalObject().getDefaultModuleId( p_item[0] )
portal_module = o.getPortalObject().getDefaultModuleId(
p_item[0])
if portal_module is not None:
portal_module_object = getattr(o.getPortalObject(), portal_module)
portal_module_object = getattr(o.getPortalObject(),
portal_module)
kw ={}
#kw[self.key] = value
kw[self.key] = string.join( string.split(value,'%'), '' )
kw[self.key] = value.replace('%', '')
kw['portal_type'] = portal_type
kw['immediate_reindex'] = 1
new_object = portal_module_object.newContent(**kw)
uid = new_object.getUid()
else:
raise
relation_uid_list.append(int(uid))
relation_object_list.append( o.portal_catalog.getObject(uid) )
#if relation_uid_list != []:
relation_uid_list.append(int(uid))
relation_object_list.append( o.portal_catalog.getObject(uid))
# Edit relation
if self.relation_setter_id:
relation_setter = getattr(o, self.relation_setter_id)
relation_setter((), portal_type=self.portal_type)
relation_setter( relation_uid_list , portal_type=self.portal_type)
relation_setter((), portal_type=self.portal_type_list)
relation_setter(relation_uid_list,
portal_type=self.portal_type_list)
else:
# we could call a generic method which create the setter method name
set_method_name = '_set'+convertToUpperCase(self.base_category)+'ValueList'
getattr(o, set_method_name)( relation_object_list , portal_type=self.portal_type)
else:
# Nothing to do
pass
# # Delete relation
# if self.relation_setter_id:
# relation_setter = getattr(o, self.relation_setter_id)
# relation_setter((), portal_type=self.portal_type)
# else:
# o._setValueUids(self.base_category, (), portal_type=self.portal_type)
set_method_name = '_set%sValueList' % \
convertToUpperCase(self.base_category)
getattr(o, set_method_name)(relation_object_list,
portal_type=self.portal_type_list)
allow_class(MultiRelationEditor)
class MultiRelationStringFieldValidator(Validator.LinesValidator):
"""
Validation includes lookup of relared instances
"""
message_names = Validator.LinesValidator.message_names +\
['relation_result_too_long', 'relation_result_ambiguous',
'relation_result_empty',]
class MultiRelationStringFieldValidator(Validator.LinesValidator, RelationField.RelationStringFieldValidator):
"""
Validation includes lookup of relared instances
"""
message_names = Validator.LinesValidator.message_names + \
RelationField.RelationStringFieldValidator.message_names
# delete double in order to keep a usable ZMI...
#message_names = dict([(i,0) for i in message_names]).keys() # XXX need to keep order !
_v_dict = {}
_v_message_name_list = []
for message_name in message_names:
if not _v_dict.has_key(message_name):
_v_message_name_list.append(message_name)
_v_dict[message_name] = 1
message_names = _v_message_name_list
def validate(self, field, key, REQUEST):
portal_type = map(lambda x:x[0],field.get_value('portal_type'))
portal_type_item = field.get_value('portal_type')
base_category = field.get_value( 'base_category')
# If the value is different, build a query
portal_selections = getToolByName(field, 'portal_selections')
portal_catalog = getToolByName(field, 'portal_catalog')
# Get the current value
value_list = Validator.LinesValidator.validate(self, field, key, REQUEST)
# XXX Do we need to translate here ?
relation_result_too_long = "Too many documents were found."
relation_result_ambiguous = "Select appropriate document in the list."
relation_result_empty = "No such document was found."
# if type(value_list) == type(''):
# value_list = [value_list]
# Relation field variable
editor = MultiRelationEditor
default_validator_instance = Validator.LinesValidatorInstance
def _generateItemUidList(self, field, key, relation_uid_list, REQUEST=None):
"""
Generate tuple...
"""
result_list = []
for i in range(len(relation_uid_list)):
# Generate a Item id for each value.
relation_item_id = field.generate_subfield_key("%s_%s" % \
(ITEM_ID, i),
key=key)
relation_uid = relation_uid_list[i]
result_list.append((relation_item_id, relation_uid, None))
return result_list
def _generateFieldValueList(self, field, key,
value_list, current_value_list):
"""
Generate list of value, item_key
"""
item_value_list = []
if isinstance(current_value_list, StringType):
current_value_list = [current_value_list]
# Check value list
if not (checkSameKeys(value_list, current_value_list)):
for i in range(len(value_list)):
value = value_list[i]
relation_field_id = field.generate_subfield_key("%s_%s" % \
(SUB_FIELD_ID, i),
key=key)
relation_item_id = field.generate_subfield_key("%s_%s" % \
(ITEM_ID, i),
key=key)
item_value_list.append((relation_field_id, value, relation_item_id))
# Make possible to delete the content of the field.
if item_value_list == []:
relation_field_id = field.generate_subfield_key("%s" % \
SUB_FIELD_ID, key=key)
relation_item_key = field.generate_subfield_key(ITEM_ID, key=key)
item_value_list.append((relation_field_id, '', relation_item_key))
return item_value_list
def validate(self, field, key, REQUEST):
"""
Validate the field.
"""
raising_error_needed = 0
relation_editor_list = None
# Get some tool
catalog_index = field.get_value('catalog_index')
portal_type_list = [x[0] for x in field.get_value('portal_type')]
portal_catalog = getToolByName(field, 'portal_catalog')
####################################
# Check list input
####################################
relation_field_id = field.generate_subfield_key("%s" % \
SUB_FIELD_ID, key=key)
relation_uid_list = REQUEST.get(relation_field_id, None)
####################################
# User clicked on the wheel
####################################
need_to_revalidate = 1
if relation_uid_list is not None:
need_to_revalidate = 0
relation_editor_list = []
for relation_item_id, relation_uid, value in \
self._generateItemUidList(field, key, relation_uid_list,
REQUEST=REQUEST):
found = 0
try:
related_object = portal_catalog.getObject(relation_uid)
display_text = str(related_object.getProperty(catalog_index))
found = 1
except ValueError:
# Catch the error raised when the uid is a string
if relation_uid.startswith(NEW_CONTENT_PREFIX):
##############################
# New content was selected, but the
# form is not validated
##############################
portal_type = relation_uid[len(NEW_CONTENT_PREFIX):]
translated_portal_type = Message(domain='erp5_ui',
message=portal_type)
message = Message(
domain='erp5_ui', message='New ${portal_type}',
mapping={'portal_type': translated_portal_type})
display_text = message
else:
display_text = 'Object has been deleted'
################################
# Modify if user modified his value
################################
if (found == 1) and \
(value != display_text):
relation_editor_list = None
# import pdb; pdb.set_trace()
need_to_revalidate = 1
REQUEST.set(relation_field_id, None)
break
if value is None:
value = display_text
# Storing display_text as value is needed in this case
relation_editor_list.append((value,
relation_uid, display_text,
None, relation_item_id))
# str(relation_uid), display_text,
####################################
# User validate the form
####################################
if need_to_revalidate == 1:
# else:
####################################
# Check the default field
####################################
value_list = self.default_validator_instance.validate(field,
key, REQUEST)
# If the value is the same as the current field value, do nothing
current_value_list = field.get_value('default')
if type(current_value_list) == type(''):
current_value_list = [current_value_list]
catalog_index = field.get_value('catalog_index')
relation_setter_id = field.get_value('relation_setter_id')
relation_field_id = 'relation_%s' % ( key )
# we must know if user validate the form or click on the wheel button
relation_uid_list = REQUEST.get(relation_field_id, None)
relation_field_sub_id = 'relation_%s_0' % ( key )
if checkSameKeys( value_list, current_value_list ) and (relation_uid_list is None) and (not REQUEST.has_key( relation_field_sub_id )):
# Will be interpreted by Editor as "do nothing"
return MultiRelationEditor(field.id, base_category,
portal_type, portal_type_item,
catalog_index, relation_setter_id, None)
else:
relation_field_id = 'relation_%s' % ( key )
# We must be able to erase the relation
if (value_list == ['']) and (not REQUEST.has_key( relation_field_id )):
display_text = 'Delete the relation'
return MultiRelationEditor(field.id, base_category, portal_type, portal_type_item, catalog_index, relation_setter_id, [])
# return RelationEditor(key, base_category, portal_type, None,
# portal_type_item, catalog_index, value, relation_setter_id, display_text)
# Will be interpreted by Base_edit as "delete relation" (with no uid and value = '')
if REQUEST.has_key( relation_field_id ):
# we must know if user validate the form or click on the wheel button
relation_uid_list = REQUEST.get(relation_field_id, None)
if relation_uid_list != None:
relation_editor_list = []
for i in range( len(relation_uid_list) ):
relation_item_id = 'item_%s_%s' % ( key, i )
relation_uid = relation_uid_list[i]
related_object = portal_catalog.getObject(relation_uid)
field_value_list = self._generateFieldValueList(field, key, value_list,
current_value_list)
if len(field_value_list) != 0:
####################################
# Values were changed
####################################
relation_editor_list = []
for relation_field_id, value, relation_item_id in field_value_list:
if value == '':
####################################
# User want to delete this line
####################################
# Clean request if necessary
if REQUEST.has_key(relation_field_id):
for subdict_name in ['form', 'other']:
subdict = getattr(REQUEST, subdict_name)
if subdict.has_key(relation_field_id):
subdict.pop(relation_field_id)
display_text = 'Delete the relation'
relation_editor_list.append((value, None,
display_text, None, None))
# XXX RelationField implementation
# # We must be able to erase the relation
# display_text = 'Delete the relation'
# # Will be interpreted by Base_edit as "delete relation"
# # (with no uid and value = '')
# relation_editor_list = [(value, None,
# display_text, None, None)]
else:
relation_uid = REQUEST.get(relation_field_id, None)
# need_to_revalidate = 1
if relation_uid not in (None, ''):
# need_to_revalidate = 0
# found = 0
####################################
# User selected in a popup menu
####################################
if isinstance(relation_uid, (list, tuple)):
relation_uid = relation_uid[0]
try:
related_object = portal_catalog.getObject(relation_uid)
except ValueError:
# Catch the exception raised when the uid is a string
related_object = None
if related_object is not None:
display_text = str(related_object.getProperty(catalog_index))
# found = 1
else:
display_text = 'Object has been deleted'
# Check
REQUEST.set(relation_item_id, ( (display_text, relation_uid), ))
# Storing display_text as value is needded in this case
relation_editor_list.append( (i, display_text, str(relation_uid), display_text) )
return MultiRelationEditor(field.id, base_category, portal_type, portal_type_item, catalog_index, relation_setter_id, relation_editor_list)
else:
# User validate the form
relation_editor_list = []
raising_error_needed = 0
raising_error_value = ''
# Check all relation
for i in range( len(value_list) ):
relation_field_id = 'relation_%s_%s' % ( key, i )
relation_item_id = 'item_%s_%s' % ( key, i )
relation_uid = REQUEST.get(relation_field_id, None)
value = value_list[i]
# If we get a empty string, delete this line
if value == '':
# Clean request if necessary
if REQUEST.has_key( relation_field_id):
for subdict_name in ['form', 'other']:
subdict = getattr(REQUEST, subdict_name)
if subdict.has_key(relation_field_id):
subdict.pop(relation_field_id)
##############################
# New content was selected, but the
# form is not validated
##############################
if relation_uid.startswith(NEW_CONTENT_PREFIX):
##############################
# New content was selected, but the
# form is not validated
##############################
portal_type = relation_uid[len(NEW_CONTENT_PREFIX):]
translated_portal_type = Message(domain='erp5_ui',
message=portal_type)
message = Message(
domain='erp5_ui', message='New ${portal_type}',
mapping={'portal_type': translated_portal_type})
display_text = message
else:
display_text = 'Object has been deleted'
# ################################
# # Modify if user modified his value
# ################################
# if (found == 1) and \
# (value != display_text):
# REQUEST.set(relation_field_id, None)
# need_to_revalidate = 1
# else:
# # Check
# REQUEST.set(relation_item_id, ((display_text, relation_uid),))
# relation_editor_list.append((value, str(relation_uid),
# display_text, relation_field_id,
# relation_item_id))
REQUEST.set(relation_item_id, ((display_text, relation_uid),))
relation_editor_list.append((value, str(relation_uid),
display_text, relation_field_id,
relation_item_id))
# if need_to_revalidate == 1:
else:
# Got a true value
if relation_uid not in (None, ''):
# A value has been defined by the user in popup menu
if type(relation_uid) in (type([]), type(())): relation_uid = relation_uid[0]
try:
related_object = portal_catalog.getObject(relation_uid)
except ValueError:
# Catch the exception raised when the uid is a string
related_object = None
####################################
# User validate the form for this line
####################################
kw ={}
kw[catalog_index] = value
kw['portal_type'] = portal_type_list
kw['sort_on'] = catalog_index
# Get the query results
relation_list = portal_catalog(**kw)
relation_uid_list = [x.uid for x in relation_list]
menu_item_list = []
if len(relation_list) >= MAX_SELECT:
# If the length is long, raise an error
# This parameter means we need listbox help
# XXX XXX XXX Do we need to delete it ?
REQUEST.set(relation_item_id, [])
raising_error_needed = 1
raising_error_value = 'relation_result_too_long'
elif len(relation_list) == 1:
# If the length is 1, return uid
relation_uid = relation_uid_list[0]
related_object = portal_catalog.getObject(relation_uid)
if related_object is not None:
display_text = str(related_object.getProperty(catalog_index))
# Modify the value, in order to let the user
# modify it later...
value = display_text
else:
display_text = 'Object has been deleted'
# Check
REQUEST.set(relation_item_id, ( (display_text, relation_uid), ))
relation_editor_list.append( (i, value, str(relation_uid), display_text) )
# XXX XXX XXX
REQUEST.set(relation_item_id, ((display_text,
relation_uid),))
relation_editor_list.append((value, relation_uid,
display_text, None,
relation_item_id))
# relation_editor_list.append((0, value, relation_uid,
# display_text, None, None))
elif len(relation_list) == 0:
# If the length is 0, raise an error
if field.get_value('allow_creation') == 1 :
# XXX
for portal_type in portal_type_list:
translated_portal_type = Message(domain='erp5_ui',
message=portal_type)
message = Message(
domain='erp5_ui', message='New ${portal_type}',
mapping={'portal_type': translated_portal_type})
menu_item_list.append((message,
'%s%s' % (NEW_CONTENT_PREFIX,
portal_type)))
REQUEST.set(relation_item_id, menu_item_list)
raising_error_needed = 1
raising_error_value = 'relation_result_empty'
else:
kw ={}
kw[catalog_index] = value
kw['portal_type'] = portal_type
kw['sort_on'] = catalog_index
# Get the query results
relation_list = portal_catalog(**kw)
relation_uid_list = map(lambda x: x.uid, relation_list)
localizer = getToolByName( field
, 'Localizer'
, None
)
# Prepare a menu
if localizer is not None:
N_ = localizer.erp5_ui.gettext
else :
N_ = lambda msg, **kw: msg
menu_item_list = [('', '')]
new_object_menu_item_list = []
for p in portal_type:
new_object_menu_item_list += [ ( N_('New %s') % (N_(p))
, '%s%s' % (new_content_prefix,p)
)
]
if len(relation_list) >= MAX_SELECT:
# If the length is long, raise an error
# This parameter means we need listbox help
REQUEST.set(relation_item_id, [])
raising_error_needed = 1
raising_error_value = 'relation_result_too_long'
elif len(relation_list) == 1:
# If the length is 1, return uid
relation_uid = relation_uid_list[0]
related_object = portal_catalog.getObject(relation_uid)
if related_object is not None:
display_text = str(related_object.getProperty(catalog_index))
else:
display_text = 'Object has been deleted'
REQUEST.set(relation_item_id, ( (display_text, relation_uid), ))
relation_editor_list.append( (0, value, relation_uid, display_text) )
elif len(relation_list) == 0:
# If the length is 0, raise an error
if field.get_value('allow_creation') == 1 :
menu_item_list += new_object_menu_item_list
REQUEST.set(relation_item_id, menu_item_list)
raising_error_needed = 1
raising_error_value = 'relation_result_empty'
else:
# If the length is short, raise an error
# len(relation_list) < MAX_SELECT:
#menu_item_list += [('-', '')]
menu_item_list += map(lambda x: (x.getObject().getProperty(catalog_index), x.uid),
relation_list)
REQUEST.set(relation_item_id, menu_item_list)
raising_error_needed = 1
raising_error_value = 'relation_result_ambiguous'
# validate MultiRelation field
if raising_error_needed:
# Raise error
self.raise_error(raising_error_value, field)
return value_list
else:
# Can return editor
return MultiRelationEditor(field.id, base_category, portal_type, portal_type_item, catalog_index, relation_setter_id, relation_editor_list)
# If the length is short, raise an error
# len(relation_list) < MAX_SELECT:
menu_item_list.extend([(
x.getObject().getProperty(catalog_index),
x.uid) for x in relation_list])
REQUEST.set(relation_item_id, menu_item_list)
raising_error_needed = 1
raising_error_value = 'relation_result_ambiguous'
#####################################
# Validate MultiRelation field
#####################################
if raising_error_needed:
# Raise error
self.raise_error(raising_error_value, field)
return value_list
else:
# Can return editor
base_category = field.get_value('base_category')
portal_type_item = field.get_value('portal_type')
relation_setter_id = field.get_value('relation_setter_id')
return self.editor(field.id,
base_category,
portal_type_list,
portal_type_item, catalog_index,
relation_setter_id, relation_editor_list)
MultiRelationStringFieldWidgetInstance = MultiRelationStringFieldWidget()
MultiRelationStringFieldValidatorInstance = MultiRelationStringFieldValidator()
class MultiRelationStringField(ZMIField):
meta_type = "MultiRelationStringField"
security = ClassSecurityInfo()
widget = MultiRelationStringFieldWidgetInstance
validator = MultiRelationStringFieldValidatorInstance
meta_type = "MultiRelationStringField"
security = ClassSecurityInfo()
security.declareProtected('Access contents information', 'get_value')
def get_value(self, id, **kw):
"""Get value for id.
widget = MultiRelationStringFieldWidgetInstance
validator = MultiRelationStringFieldValidatorInstance
Optionally pass keyword arguments that get passed to TALES
expression.
"""
if id in ('is_relation_field', 'is_multi_relation_field'):
result = 1
else:
result = ZMIField.get_value(self, id, **kw)
return result
security.declareProtected('Access contents information', 'get_value')
def get_value(self, id, **kw):
"""
Get value for id.
Optionally pass keyword arguments that get passed to TALES
expression.
"""
if id in ('is_relation_field', 'is_multi_relation_field'):
result = 1
else:
result = ZMIField.get_value(self, id, **kw)
return result
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Copyright (c) 2002, 2006 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solanes <jp@nexedi.com>
# Romain Courteaud <romain@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
......@@ -31,448 +32,120 @@ from Products.Formulator.Field import ZMIField
from Products.Formulator.DummyField import fields
from Products.ERP5Type.Utils import convertToUpperCase
from Products.CMFCore.utils import getToolByName
from Globals import get_request
from Products.PythonScripts.Utility import allow_class
from Products.ERP5Type.Message import Message
import string
from Products.ERP5Form import MultiRelationField
from Products.ERP5Form.MultiRelationField import MAX_SELECT, \
NEW_CONTENT_PREFIX, \
SUB_FIELD_ID, ITEM_ID
from types import StringType
from AccessControl import ClassSecurityInfo
from zLOG import LOG
MAX_SELECT = 30 # Max. number of catalog result
new_content_prefix = '_newContent_'
class RelationStringFieldWidget(Widget.TextWidget, Widget.ListWidget):
"""
RelationStringField widget
Works like a string field but includes one buttons
- one search button which updates the field and sets a relation
- creates object if not there
"""
property_names = Widget.TextWidget.property_names + \
['update_method', 'jump_method', 'allow_jump', 'base_category',
'portal_type', 'allow_creation', 'container_getter_id', 'catalog_index',
'relation_setter_id', 'columns','sort','parameter_list','list_method',
'first_item', 'items', 'size', 'extra_item']
# XXX Field to remove...
update_method = fields.StringField('update_method',
title='Update Method',
description=(
"The method to call to set the relation. Required."),
default="Base_validateRelation",
required=1)
jump_method = fields.StringField('jump_method',
title='Jump Method',
description=(
"The method to call to jump to the relation. Required."),
default="Base_jumpToRelatedDocument",
required=1)
allow_jump = fields.CheckBoxField('allow_jump',
title='Allow Jump',
description=(
"Do we allow to jump to the relation ?"),
default=1,
required=0)
base_category = fields.StringField('base_category',
title='Base Category',
description=(
"The method to call to set the relation. Required."),
default="",
required=1)
portal_type = fields.ListTextAreaField('portal_type',
title='Portal Type',
description=(
"The method to call to set the relation. Required."),
default="",
required=1)
allow_creation = fields.CheckBoxField('allow_creation',
title='Allow Creation',
description=(
"Do we allow to create new objects ?"),
default=1,
required=0)
container_getter_id = fields.StringField('container_getter_id',
title='Container Getter Method',
description=(
"The method to call to get a container object."),
default="",
required=0)
catalog_index = fields.StringField('catalog_index',
title='Catalog Index',
description=(
"The method to call to set the relation. Required."),
default="",
required=1)
# XXX Is it a good idea to keep such a field ??
# User can redefine setter method with a script (and so, don't use the API)
relation_setter_id = fields.StringField('relation_setter_id',
title='Relation Update Method',
description=(
"The method to invoke in order to update the relation"),
default="",
required=0)
size = fields.IntegerField('size',
title='Size',
description=(
"The display size in rows of the field. If set to 1, the "
"widget will be displayed as a drop down box by many browsers, "
"if set to something higher, a list will be shown. Required."),
default=1,
required=1)
columns = fields.ListTextAreaField('columns',
title="Columns",
description=(
"A list of attributes names to display."),
default=[],
required=0)
sort = fields.ListTextAreaField('sort',
title='Default Sort',
description=('The default sort keys and order'),
default=[],
required=0)
parameter_list = fields.ListTextAreaField('parameter_list',
title="Parameter List",
description=(
"A list of paramters used for the portal_catalog."),
default=[],
required=0)
list_method = fields.MethodField('list_method',
title='List Method',
description=('The method to use to list'
'objects'),
default='',
required=0)
def render(self, field, key, value, REQUEST):
"""Render text input field.
"""
relation_field_id = 'relation_%s' % key
relation_item_id = 'item_%s' % key
here = REQUEST['here']
portal_url = getToolByName(here, 'portal_url')
portal_url_string = portal_url()
portal_object = portal_url.getPortalObject()
html_string = Widget.TextWidget.render(self, field, key, value,
REQUEST)
if REQUEST.has_key(relation_item_id):
# Define default tales on the fly
tales_expr = field.tales.get('items', None)
if not tales_expr:
from Products.Formulator.TALESField import TALESMethod
field.tales['items'] = TALESMethod('REQUEST/relation_item_list')
REQUEST['relation_item_list'] = REQUEST.get(relation_item_id)
html_string += '&nbsp;%s&nbsp;' % Widget.ListWidget.render(self,
field, relation_field_id, None, REQUEST)
REQUEST['relation_item_list'] = None
# We used to add a button which has a path reference to a base category...
# but it really created too many problems
# now we do it in another way
# we compare what has been changed in the relation update script
#elif value != field.get_value('default'):
else:
html_string += \
'&nbsp;<input type="image" ' \
'src="%s/images/exec16.png" value="update..." ' \
'name="%s/portal_selections/viewSearchRelatedDocumentDialog%s' \
':method"/>' % \
(portal_url_string, portal_object.getPath(),
getattr(field.aq_parent, '_v_relation_field_index', 0))
relation_field_index = getattr(field.aq_parent,
'_v_relation_field_index', 0)
# Increase index
field.aq_parent._v_relation_field_index = relation_field_index + 1
# import pdb; pdb.set_trace()
# field.get_value('default')
if (value not in ( None, '' )) and \
(not REQUEST.has_key(relation_item_id)) and \
(value == field.get_value('default')) and \
(field.get_value('allow_jump') == 1):
if REQUEST.get('selection_name') is not None:
html_string += '&nbsp;&nbsp;<a href="%s/%s?field_id=%s&' \
'form_id=%s&selection_name=%s&selection_index=%s' \
'"><img src="%s/images/jump.png"/></a>' % \
(here.absolute_url(), field.get_value('jump_method'),
field.id, field.aq_parent.id,
REQUEST.get('selection_name'),
REQUEST.get('selection_index'),
portal_url_string)
else:
html_string += '&nbsp;&nbsp;<a href="%s/%s?field_id=%s&' \
'form_id=%s"><img src="%s/images/jump.png"/></a>' \
% (here.absolute_url(), field.get_value('jump_method'),
field.id, field.aq_parent.id,portal_url_string)
return html_string
class RelationStringFieldWidget(
MultiRelationField.MultiRelationStringFieldWidget):
"""
RelationStringField widget
Works like a string field but includes one buttons
- one search button which updates the field and sets a relation
- creates object if not there
"""
property_names = Widget.TextWidget.property_names + \
MultiRelationField.MultiRelationStringFieldWidget.local_property_names
default_widget_rendering_instance = Widget.TextWidgetInstance
def _generateRenderValueList(self, field, key, value, REQUEST):
relation_field_id = field.generate_subfield_key(SUB_FIELD_ID, key=key)
relation_item_key = field.generate_subfield_key(ITEM_ID, key=key)
relation_item_list = REQUEST.get(relation_item_key, [])
return [(Widget.TextWidgetInstance, relation_field_id,
relation_item_list, value, None)]
class RelationEditor(MultiRelationField.MultiRelationEditor):
"""
A class holding all values required to update a relation
"""
def __call__(self, REQUEST):
MultiRelationField.MultiRelationEditor.__call__(self, REQUEST)
value = REQUEST.get(self.field_id)
if value is not None:
REQUEST.set(self.field_id, value[0])
def render_view(self, field, value):
"""Render text input field.
"""
if field.get_value('allow_jump') == 0 :
return Widget.TextWidget.render_view(self, field, value)
REQUEST = get_request()
here = REQUEST['here']
html_string = Widget.TextWidget.render_view(self, field, value)
portal_url_string = getToolByName(here, 'portal_url')()
if value not in ('', None):
html_string = '<a href="%s/%s?field_id=%s&form_id=%s">%s</a>' \
% (here.absolute_url(), field.get_value('jump_method'),
field.id, field.aq_parent.id, html_string)
html_string += '&nbsp;&nbsp;<a href="%s/%s?field_id=%s&form_id=%s">' \
'<img src="%s/images/jump.png"/></a>' % \
(here.absolute_url(), field.get_value('jump_method'),
field.id, field.aq_parent.id, portal_url_string)
return html_string
allow_class(RelationEditor)
class RelationEditor:
class RelationStringFieldValidator(
MultiRelationField.MultiRelationStringFieldValidator):
"""
Validation includes lookup of relared instances
"""
message_names = Validator.StringValidator.message_names + \
MultiRelationField.MultiRelationStringFieldValidator.message_names
# Delete double in order to keep a usable ZMI...
# Need to keep order !
_v_dict = {}
_v_message_name_list = []
for message_name in message_names:
if not _v_dict.has_key(message_name):
_v_message_name_list.append(message_name)
_v_dict[message_name] = 1
message_names = _v_message_name_list
# Relation field variable
editor = RelationEditor
default_validator_instance = Validator.StringValidatorInstance
def _generateItemUidList(self, field, key, relation_uid_list, REQUEST=None):
"""
A class holding all values required to update a relation
Generate list of uid, item_key
"""
def __init__(self, field_id, base_category, portal_type, uid,
portal_type_item, key, value, relation_setter_id,
container_getter_id, display_text):
self.field_id = field_id
self.uid = uid
self.base_category = base_category
self.portal_type = portal_type
self.portal_type_item = portal_type_item
self.key = key
self.value = value
self.relation_setter_id = relation_setter_id
self.container_getter_id = container_getter_id
self.display_text = display_text
def __call__(self, REQUEST):
if self.uid is not None:
# Decorate the request so that we can display
# the select item in a popup
relation_field_id = 'relation_%s' % self.field_id
relation_item_id = 'item_%s' % self.field_id
REQUEST.set(relation_item_id, ((self.display_text, self.uid),))
REQUEST.set(relation_field_id, self.uid)
# XXX Dirty
REQUEST.set(self.field_id[len('field_'):], self.value)
else:
# Make sure no default value appears
REQUEST.set(self.field_id[len('field_'):], None)
def view(self):
return self.__dict__
def edit(self, o):
if self.uid is not None:
if type(self.uid) is type('a') and \
self.uid.startswith(new_content_prefix):
# Create a new content
portal_type = self.uid[len(new_content_prefix):]
container = None
for p_item in self.portal_type_item:
if p_item[0] == portal_type:
if self.container_getter_id:
container = getattr(o, self.container_getter_id)(
portal_type=portal_type)
else:
portal_module = o.getPortalObject().getDefaultModuleId( p_item[0] )
container = getattr(o.getPortalObject(), portal_module)
if container is not None:
kw ={}
kw[self.key] = string.join( string.split(self.value,'%'), '' )
kw['portal_type'] = portal_type
kw['immediate_reindex'] = 1
new_object = container.newContent(**kw)
self.uid = new_object.getUid()
else:
raise
# Edit relation
if self.relation_setter_id:
relation_setter = getattr(o, self.relation_setter_id)
relation_setter((), portal_type=self.portal_type)
relation_setter((int(self.uid),), portal_type=self.portal_type)
else:
# we could call a generic method which create the setter method name
set_method_name = '_set'+convertToUpperCase(self.base_category)+'Value'
object = o.portal_catalog.getObject( self.uid )
getattr(o, set_method_name)( object,portal_type=self.portal_type )
else:
if self.value == '':
# Delete relation
if self.relation_setter_id:
relation_setter = getattr(o, self.relation_setter_id)
relation_setter((), portal_type=self.portal_type)
else:
# we could call a generic method which create the setter method name
set_method_name = '_set'+convertToUpperCase(self.base_category)
getattr(o, set_method_name)( None ,portal_type=self.portal_type)
allow_class(RelationEditor)
class RelationStringFieldValidator(Validator.StringValidator):
relation_item_id = field.generate_subfield_key(ITEM_ID,
key=key)
if isinstance(relation_uid_list, (list, tuple)):
try:
relation_uid_list = relation_uid_list[0]
except IndexError:
# No object was selected
return []
value = self.default_validator_instance.validate(field,
key, REQUEST)
return [(relation_item_id, relation_uid_list, value)]
def _generateFieldValueList(self, field, key,
value_list, current_value_list):
"""
Validation includes lookup of relared instances
Generate list of value, item_key
"""
message_names = Validator.StringValidator.message_names +\
['relation_result_too_long', 'relation_result_ambiguous',
'relation_result_empty',]
relation_result_too_long = "Too many documents were found."
relation_result_ambiguous = "Select appropriate document in the list."
relation_result_empty = "No such document was found."
def validate(self, field, key, REQUEST):
relation_field_id = 'relation_%s' % key
relation_item_id = 'item_%s' % key
portal_type = map(lambda x:x[0],field.get_value('portal_type'))
portal_type_item = field.get_value('portal_type')
base_category = field.get_value( 'base_category')
# If the value is different, build a query
portal_selections = getToolByName(field, 'portal_selections')
portal_catalog = getToolByName(field, 'portal_catalog')
# Get the current value
value = Validator.StringValidator.validate(self, field, key, REQUEST)
# If the value is the same as the current field value, do nothing
current_value = field.get_value('default')
# If a relation has been defined in a popup menu, use it
relation_uid = REQUEST.get(relation_field_id, None)
catalog_index = field.get_value('catalog_index')
parameter_list = field.get_value('parameter_list')
relation_setter_id = field.get_value('relation_setter_id')
container_getter_id = field.get_value('container_getter_id')
if (value == current_value) and (relation_uid is None):
# Will be interpreted by Editor as "do nothing"
return RelationEditor(key, base_category, portal_type, None,
portal_type_item, catalog_index, None,
relation_setter_id, container_getter_id, None)
if relation_uid not in (None, ''):
# A value has been defined by the user
if type(relation_uid) in (type([]), type(())):
if len( relation_uid ) == 0:
# No object was selected...
return None
else:
relation_uid = relation_uid[0]
try:
related_object = portal_catalog.getObject(relation_uid)
except ValueError:
# Catch the error raised when the uid is a string
related_object = None
if related_object is not None:
display_text = str(related_object.getProperty(catalog_index))
else:
display_text = 'Object has been deleted'
return RelationEditor(key, base_category, portal_type, relation_uid,
portal_type_item, catalog_index, value,
relation_setter_id, container_getter_id,
display_text)
# We must be able to erase the relation
if value == '':
display_text = 'Delete the relation'
# Will be interpreted by Base_edit as "delete relation"
# (with no uid and value = '')
return RelationEditor(key, base_category, portal_type, None,
portal_type_item, catalog_index, value,
relation_setter_id, container_getter_id, display_text)
kw ={}
kw[catalog_index] = value
kw['portal_type'] = portal_type
kw['sort_on'] = catalog_index
if len(parameter_list) > 0:
for k,v in parameter_list:
kw[k] = v
# Get the query results
relation_list = portal_catalog(**kw)
relation_uid_list = map(lambda x: x.uid, relation_list)
# Prepare a menu
menu_item_list = [('', '')]
new_object_menu_item_list = []
for p in portal_type:
translated_p = Message(domain='erp5_ui',message=p)
message = Message(domain='erp5_ui',message = 'New ${portal_type}',
mapping={'portal_type':translated_p})
new_object_menu_item_list += [ ( message
, '%s%s' % (new_content_prefix,p)
)
]
# If the length is 1, return uid
if len(relation_list) == 1:
relation_uid = relation_uid_list[0]
related_object = portal_catalog.getObject(relation_uid)
if related_object is not None:
display_text = str(related_object.getProperty(catalog_index))
else:
display_text = 'Object has been deleted'
return RelationEditor(
key, base_category, portal_type, relation_uid,
portal_type_item, catalog_index, value,
relation_setter_id, container_getter_id, display_text)
# If the length is 0, raise an error
elif len(relation_list) == 0:
if field.get_value('allow_creation') == 1 :
menu_item_list += new_object_menu_item_list
REQUEST.set(relation_item_id, menu_item_list)
self.raise_error('relation_result_empty', field)
# If the length is short, raise an error
elif len(relation_list) < MAX_SELECT:
#menu_item_list += [('-', '')]
menu_item_list.extend([(x.getObject().getProperty(catalog_index),
x.uid) for x in relation_list])
REQUEST.set(relation_item_id, menu_item_list)
self.raise_error('relation_result_ambiguous', field)
else:
# If the length is long, raise an error
# If this error is raise, we don t want to create a new object...
#REQUEST.set(relation_item_id, menu_item_list)
self.raise_error('relation_result_too_long', field)
if value_list == current_value_list:
return []
else:
relation_field_id = field.generate_subfield_key("%s" % \
SUB_FIELD_ID, key=key)
relation_item_key = field.generate_subfield_key(ITEM_ID, key=key)
return [(relation_field_id, value_list, relation_item_key)]
RelationStringFieldWidgetInstance = RelationStringFieldWidget()
RelationStringFieldValidatorInstance = RelationStringFieldValidator()
class RelationStringField(ZMIField):
meta_type = "RelationStringField"
security = ClassSecurityInfo()
widget = RelationStringFieldWidgetInstance
validator = RelationStringFieldValidatorInstance
meta_type = "RelationStringField"
security = ClassSecurityInfo()
security.declareProtected('Access contents information', 'get_value')
def get_value(self, id, **kw):
"""Get value for id.
widget = RelationStringFieldWidgetInstance
validator = RelationStringFieldValidatorInstance
Optionally pass keyword arguments that get passed to TALES
expression.
"""
if id == 'is_relation_field':
result = 1
elif id == 'is_multi_relation_field':
result = 0
else:
result = ZMIField.get_value(self, id, **kw)
return result
security.declareProtected('Access contents information', 'get_value')
def get_value(self, id, **kw):
"""
Get value for id.
Optionally pass keyword arguments that get passed to TALES
expression.
"""
if id == 'is_relation_field':
result = 1
elif id == 'is_multi_relation_field':
result = 0
else:
result = ZMIField.get_value(self, id, **kw)
return result
......@@ -52,6 +52,7 @@ import random
import string
from zLOG import LOG
from Acquisition import Implicit, aq_base
from Products.ERP5Type.Message import Message
class SelectionError( Exception ):
......@@ -920,7 +921,7 @@ class SelectionTool( UniqueObject, SimpleItem ):
return object
# Related document searching
def viewSearchRelatedDocumentDialog(self, index, form_id, REQUEST=None,
def viewSearchRelatedDocumentDialog(self, index, form_id, REQUEST=None,
sub_index=None, **kw):
"""
Returns a search related document dialog
......@@ -928,8 +929,7 @@ class SelectionTool( UniqueObject, SimpleItem ):
"""
if sub_index != None:
REQUEST.form['sub_index'] = sub_index
# Find the object which needs to be updated
# Find the object which needs to be updated
object_uid = REQUEST.get('object_uid', None)
object_path = REQUEST.get('object_path', None)
if object_uid is not None:
......@@ -942,72 +942,56 @@ class SelectionTool( UniqueObject, SimpleItem ):
if object_path is not None:
o = self.getPortalObject().restrictedTraverse(object_path)
if o is not None:
# XXX
o.immediateReindexObject()
object_uid = o.getUid()
else:
return "Sorrry, Error, the calling object was not catalogued. " \
"Do not know how to do ?"
raise SelectionError, \
"Sorrry, Error, the calling object was not catalogued. " \
"Do not know how to do ?"
# Find the field which was clicked on
# Important to get from the object instead of self
form = getattr(o, form_id)
field = None
# Search the correct field
relation_field_found = 0
relation_index = 0
# find the correct field
field_list = []
# XXX may be should support another parameter,
# like include_non_editable=0
for field in form.get_fields(include_disabled=0):
if field.get_value('editable',REQUEST=REQUEST):
field_list.append(field)
relation_field_found = 0
for field in field_list:
try:
dumb = field.get_value('is_relation_field')
# XXX FIXME Exception name is not in locals.
# This can be related to a bad python file import
# I already had this kind of error with another python software,
# and the only solution I found was to use ihooks to
# import python files.
# I have to check this.
# except KeyError:
except:
pass
# relation_index += 1
else:
if index == relation_index:
relation_field_found = 1
break
if field.get_value('editable', REQUEST=REQUEST):
try:
dumb = field.get_value('is_relation_field')
except:
# except KeyError:
# XXX FIXME Exception name is not in locals.
# Namespace seems a bit broken...
LOG("SelectionTool", 0, "Exception catched with broken namespace!")
pass
else:
relation_index += 1
# if getattr(field, 'is_relation_field', None):
# if index == relation_index:
# relation_field_found = 1
# break
# else:
# relation_index += 1
if index == relation_index:
relation_field_found = 1
break
else:
relation_index += 1
if not relation_field_found:
# We didn't find the field...
raise SelectionError, "SelectionTool: can not find the relation" \
" field %s" % index
field_value = REQUEST.form['field_%s' % field.id]
selection_name = 'Base_viewRelatedObjectList'
# reselt current selection
self.portal_selections.setSelectionFor(selection_name, None)
# XXX portal_status_message =
# "Please select one object to precise the value:
# '%s' in the field: '%s'" % (field_value, field.get_orig_value('title'))
portal_status_message = "Please select one object."
if field.get_value('is_multi_relation_field'):
if sub_index is None:
else:
# Field found
field_key = field.generate_field_key()
field_value = REQUEST.form[field_key]
# XXX Hardcoded form name
redirect_form_id = 'Base_viewRelatedObjectList'
redirect_form = getattr(o, redirect_form_id)
# XXX Hardcoded listbox field
selection_name = redirect_form.listbox.get_value('selection_name')
# Reset current selection
self.portal_selections.setSelectionFor(selection_name, None)
if (field.get_value('is_multi_relation_field')) and \
(sub_index is None):
# user click on the wheel, not on the validation button
# we need to facilitate user search
......@@ -1029,110 +1013,60 @@ class SelectionTool( UniqueObject, SimpleItem ):
self.portal_selections.setSelectionCheckedUidsFor(
selection_name,
current_uid_list)
REQUEST.form['field_%s' % field.id] = field_value
# XXX portal_status_message =
# "Please select one or more object to define the field:
# '%s'" % field.get_orig_value('title')
portal_status_message = "Please select one (or more) object."
# XXX
# field_value = ''
field_value = str(field_value).splitlines()
REQUEST.form[field_key] = field_value
portal_status_message = Message(
domain='erp5_ui',
message="Please select one (or more) object.")
else:
portal_status_message = Message(domain='erp5_ui',
message="Please select one object.")
# Save the current REQUEST form
# We can't put FileUpload instances because we can't pickle them
pickle_kw = {}
for key in REQUEST.form.keys():
if not isinstance(REQUEST.form[key],FileUpload):
pickle_kw[key] = REQUEST.form[key]
# Save the current REQUEST form
# We can't put FileUpload instances because we can't pickle them
pickle_kw = {}
for key in REQUEST.form.keys():
if not isinstance(REQUEST.form[key],FileUpload):
pickle_kw[key] = REQUEST.form[key]
form_pickle, form_signature = self.getPickleAndSignature(**pickle_kw)
REQUEST.form_pickle = form_pickle
REQUEST.form_signature = form_signature
base_category = None
kw = {}
kw['object_uid'] = object_uid
kw['form_id'] = 'Base_viewRelatedObjectList'
kw['selection_name'] = 'Base_viewRelatedObjectList'
kw['selection_index'] = 0 # We start on the first page
kw['field_id'] = field.id
kw['portal_type'] = map(lambda x:x[0],field.get_value('portal_type'))
parameter_list = field.get_value('parameter_list')
if len(parameter_list) > 0:
for k,v in parameter_list:
kw[k] = v
kw['reset'] = 0
kw['base_category'] = field.get_value( 'base_category')
kw['cancel_url'] = REQUEST.get('HTTP_REFERER')
kw['previous_form_id'] = form_id
kw[field.get_value('catalog_index')] = str(field_value).splitlines()
"""
# We work with strings - ie. single values
kw ={}
context.portal_selections.setSelectionParamsFor('Base_viewRelatedObjectList', kw.copy())
previous_uids = o.getValueUids(base_category, portal_type=portal_type)
relation_list = context.portal_catalog(**kw)
relation_uid_list = map(lambda x: x.uid, relation_list)
uids = []
"""
# Need to redirect, if we want listbox nextPage to work
kw['form_pickle'] = form_pickle
kw['form_signature'] = form_signature
kw['portal_status_message'] = portal_status_message
redirect_url = '%s/%s?%s' % ( o.absolute_url()
, 'Base_viewRelatedObjectList'
, make_query(kw)
)
REQUEST[ 'RESPONSE' ].redirect( redirect_url )
# # Empty the selection (uid)
# REQUEST.form = kw # New request form
#
# # Define new HTTP_REFERER
# REQUEST.HTTP_REFERER = '%s/Base_viewRelatedObjectList' % o.absolute_url()
#
# # Return the search dialog
# return o.Base_viewRelatedObjectList(REQUEST=REQUEST)
# XXX do not use this method, use aq_dynamic (JPS)
# def __getattr__(self, name):
# dynamic_method_name = 'viewSearchRelatedDocumentDialog'
# if name[:len(dynamic_method_name)] == dynamic_method_name:
# method_count_string = name[len(dynamic_method_name):]
# # be sure that method name is correct
# try:
# import string
# method_count = string.atoi(method_count_string)
# except:
# raise AttributeError, name
# else:
# # generate dynamicaly needed forwarder methods
# def viewSearchRelatedDocumentDialogWrapper(self, form_id, REQUEST=None, **kw):
# """
# viewSearchRelatedDocumentDialog Wrapper
# """
# return self.viewSearchRelatedDocumentDialog(method_count, form_id, REQUEST=REQUEST, **kw)
#
# setattr(self.__class__, name, viewSearchRelatedDocumentDialogWrapper)
#
# klass = self.__class__
# if hasattr(klass, 'security'):
# from Products.ERP5Type import Permissions as ERP5Permissions
# klass.security.declareProtected(ERP5Permissions.View, name)
# else:
# # XXX security declaration always failed....
# LOG('WARNING ERP5Form SelectionTool, security not defined on',0,klass.__name__)
#
# return getattr(self, name)
# else:
# raise AttributeError, name
form_pickle, form_signature = self.getPickleAndSignature(**pickle_kw)
REQUEST.form_pickle = form_pickle
REQUEST.form_signature = form_signature
base_category = None
kw = {}
kw['object_uid'] = object_uid
kw['form_id'] = redirect_form_id
kw['selection_name'] = selection_name
kw['selection_index'] = 0 # We start on the first page
kw['field_id'] = field.id
kw['portal_type'] = [x[0] for x in field.get_value('portal_type')]
parameter_list = field.get_value('parameter_list')
if len(parameter_list) > 0:
for k,v in parameter_list:
kw[k] = v
kw['reset'] = 0
kw['base_category'] = field.get_value( 'base_category')
kw['cancel_url'] = REQUEST.get('HTTP_REFERER')
kw['previous_form_id'] = form_id
# XXX
# kw[field.get_value('catalog_index')] = str(field_value).splitlines()
kw[field.get_value('catalog_index')] = field_value
# Need to redirect, if we want listbox nextPage to work
kw['form_pickle'] = form_pickle
kw['form_signature'] = form_signature
kw['portal_status_message'] = portal_status_message
redirect_url = '%s/%s?%s' % ( o.absolute_url()
, redirect_form_id
, make_query(kw)
)
REQUEST[ 'RESPONSE' ].redirect( redirect_url )
def _aq_dynamic(self, name):
"""
......@@ -1141,14 +1075,13 @@ class SelectionTool( UniqueObject, SimpleItem ):
"""
aq_base_name = getattr(aq_base(self), name, None)
if aq_base_name == None:
dynamic_method_name = 'viewSearchRelatedDocumentDialog'
DYNAMIC_METHOD_NAME = 'viewSearchRelatedDocumentDialog'
method_name_length = len(DYNAMIC_METHOD_NAME)
zope_security = '__roles__'
if (name[:len(dynamic_method_name)] == dynamic_method_name) and \
if (name[:method_name_length] == DYNAMIC_METHOD_NAME) and \
(name[-len(zope_security):] != zope_security):
method_count_string_list = string.split(
name[len(dynamic_method_name):],
'_')
method_count_string_list = name[method_name_length:].split('_')
method_count_string = method_count_string_list[0]
# be sure that method name is correct
try:
......@@ -1173,15 +1106,9 @@ class SelectionTool( UniqueObject, SimpleItem ):
"""
LOG('SelectionTool.viewSearchRelatedDocumentDialogWrapper, kw',
0, kw)
if sub_index == None:
return self.viewSearchRelatedDocumentDialog(
method_count, form_id,
REQUEST=REQUEST, **kw)
else:
return self.viewSearchRelatedDocumentDialog(
return self.viewSearchRelatedDocumentDialog(
method_count, form_id,
REQUEST=REQUEST, sub_index=sub_index, **kw)
setattr(self.__class__, name,
viewSearchRelatedDocumentDialogWrapper)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment