class MethodWidget(Widget.TextWidget): default = fields.MethodField('default', title='Default', default="", required=0) def render(self, field, key, value, REQUEST): if value == None: method_name = field.get_value('default') else: if value != "": method_name = value.method_name else: method_name = "" return Widget.TextWidget.render(self, field, key, method_name, REQUEST)
class TALESWidget(Widget.TextWidget): default = fields.MethodField('default', title='Default', default="", required=0) def render(self, field, key, value, REQUEST): if value == None: text = field.get_value('default') else: if value != "": text = value._text else: text = "" return Widget.TextWidget.render(self, field, key, text, REQUEST)
class SuppressValidator(ValidatorBase): """A validator that is actually not used. """ property_names = ValidatorBase.property_names + ['external_validator'] external_validator = fields.MethodField( 'external_validator', title="External Validator", description=("Ignored, as a validator isn't used here."), default="", required=0, enabled=0) def need_validate(self, field, key, REQUEST): """Don't ever validate; suppress result in output. """ return 0
class Validator(ValidatorBase): """Validates input and possibly transforms it to output. """ property_names = ValidatorBase.property_names + ['external_validator'] message_names = ValidatorBase.message_names + ['external_validator_failed'] external_validator = fields.MethodField( 'external_validator', title="External Validator", description=( "When a method name is supplied, this method will be " "called each time this field is being validated. All other " "validation code is called first, however. The value (result of " "previous validation) and the REQUEST object will be passed as " "arguments to this method. Your method should return true if the " "validation succeeded. Anything else will cause " "'external_validator_failed' to be raised."), default="", required=0) external_validator_failed = _('The input failed the external validator.')
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', 'context_getter_id', 'catalog_index', 'relation_setter_id', 'relation_form_id', 'columns', 'sort', 'parameter_list', 'list_method', 'first_item', 'items', 'proxy_listbox_ids', 'size', 'extra_item', ] property_names = ( lambda name_list, name_set=set(): # delete double (but preserve order) in order to keep a usable ZMI... [x for x in name_list if not (x in name_set or name_set.add(x))] )(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=0) 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) context_getter_id = fields.StringField( 'context_getter_id', title='Context Getter Method', description=("The method to call to get the context."), 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) relation_form_id = fields.StringField( 'relation_form_id', title='Relation Form', description=("Form to display relation choices"), 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) proxy_listbox_ids = fields.ListTextAreaField( 'proxy_listbox_ids', title='Proxy Listbox IDs', description=('A list of listbox that can be used as proxy'), default='', required=0) default_widget_rendering_instance = Widget.LinesTextAreaWidgetInstance def _getContextValue(self, field, REQUEST): """Return result of evaluated method defined by context_getter_id or here. """ context_getter_id = field.get_value('context_getter_id') here = REQUEST['here'] if context_getter_id: return getattr(here, context_getter_id)() return here def _generateRenderValueList(self, field, key, value_list, REQUEST): if isinstance(value_list, basestring): # Value is a string, reformat it correctly value_list = value_list.split("\n") else: # We get a list # rather than displaying nothing, display a marker when the # property is not set # XXX Translate ? value_list = [(x or NO_VALUE) for x in value_list] generate_subfield_key = field.generate_subfield_key need_validation = False result_list = [] for index, value in enumerate(value_list): relation_item_list = REQUEST.get( generate_subfield_key( "%s_%s" % (ITEM_ID, index), key=key, ), None, ) # If we get a empty string, display nothing ! if value: need_validation |= relation_item_list is not None result_list.append(( Widget.TextWidgetInstance, generate_subfield_key( "%s_%s" % (SUB_FIELD_ID, index), key=key, ), relation_item_list, value, index, ), ) if need_validation: return result_list return [(Widget.LinesTextAreaWidgetInstance, None, [], value_list, None)] def render(self, field, key, value, REQUEST, render_prefix=None): """ Render text input field. """ portal = self._getContextValue(field, REQUEST).getPortalObject() autocomplete_enabled = getattr( portal.portal_skins, 'erp5_autocompletion_ui', None, ) is not None relation_field_index = REQUEST.get('_v_relation_field_index', 0) html_string_list = [] for (widget_instance, relation_field_id, relation_item_list, value_instance, sub_index) in self._generateRenderValueList( field, key, value, REQUEST, ): sub_html_string = widget_instance.render( field, key, value_instance, REQUEST, ) if autocomplete_enabled: sub_html_string += self.render_autocomplete(field, key) if relation_item_list is not None: if not autocomplete_enabled: sub_html_string += self.render_wheel( field, value_instance, REQUEST, relation_index=relation_field_index, sub_index=sub_index, ) if relation_item_list: REQUEST['relation_item_list'] = relation_item_list sub_html_string += NBSP + Widget.ListWidgetInstance.render( field, relation_field_id, None, REQUEST, ) + NBSP REQUEST['relation_item_list'] = None html_string_list.append(sub_html_string) html_string = '<br/>'.join(html_string_list) 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 + relation_html_string REQUEST.set('_v_relation_field_index', relation_field_index + 1) return html_string def render_view(self, field, value, REQUEST=None, render_prefix=None): """ Render read only field. """ if (value not in ((), [], None, '')) and field.get_value('allow_jump'): if not isinstance(value, (list, tuple)): value = value, html_string = '<br />'.join( '<a class="relationfieldlink" href="%s">%s</a>' % ( escape(jump_reference.absolute_url()), escape(display_value), ) for jump_reference, display_value in zip( getattr( self._getContextValue(field, REQUEST), 'get%sValueList' % ''.join(part.capitalize() for part in field.get_value( 'base_category').split('_')))( portal_type=[ x[0] for x in field.get_value('portal_type') ], filter=dict(field.get_value('parameter_list')), ), value, )) else: html_string = self.default_widget_rendering_instance.render_view( field, value, REQUEST=REQUEST, ) if REQUEST is None: REQUEST = get_request() relation_html_string = self.render_relation_link( field, value, REQUEST) if relation_html_string: html_string += NBSP + NBSP + relation_html_string extra = field.get_value('extra') if extra not in (None, ''): html_string = "<div %s>%s</div>" % (extra, html_string) css_class = field.get_value('css_class') if css_class not in ('', None): html_string = '<span class="%s">%s</span>' % ( escape(css_class), html_string, ) return html_string def render_autocomplete(self, field, key): """ Use jquery-ui autocompletion for all relation fields by default, requiring only erp5_autocompletion_ui bt5 to be installed """ # XXX: Allow to specify more parameters to jquery-ui autocomplete widget? return """ <script type="text/javascript"> $(document).ready(function() { $("input[name='%s']").ERP5Autocomplete({search_portal_type: %s, search_catalog_key: "%s"}); }); </script>""" % ( escape(key), escape(json.dumps([x[0] for x in field.get_value('portal_type')])), escape(field.get_value('catalog_index')), ) def render_wheel(self, field, value, REQUEST, relation_index=0, sub_index=None, render_prefix=None): """ Render wheel used to display a listbox """ here = self._getContextValue(field, REQUEST) portal_url = here.getPortalObject().portal_url if sub_index is None: sub_index_string = '' else: sub_index_string = '_%s' % sub_index return ' <input type="image" ' \ 'src="%s/images/exec16.png" alt="update..." ' \ 'name="%s/viewSearchRelatedDocumentDialog%s%s' \ ':method"/>' % ( escape(portal_url()), escape(portal_url.getRelativeContentURL(here.portal_selections)), escape(str(relation_index)), escape(sub_index_string), ) def render_relation_link(self, field, value, REQUEST, render_prefix=None): """ Render link to the related object. """ if value not in ((), [], None, '') and field.get_value('allow_jump'): # If we this relation field is used as a listbox/matrixbox editable # field, then the context of this cell is set in REQUEST. XXX this is not # 100% reliable way, maybe we need something to know that the field is # beeing rendered as an editable field. cell = REQUEST.get('cell') here = (cell if cell is not None else self._getContextValue( field, REQUEST)) # Keep the selection name in the URL selection_name = REQUEST.get('selection_name') if selection_name is not None: selection_name_html = '&selection_name=%s&selection_index=%s' % ( escape(selection_name), escape(str(REQUEST.get('selection_index', 0))), ) else: selection_name_html = '' ignore_layout = REQUEST.get('ignore_layout') if ignore_layout is not None: selection_name_html += '&ignore_layout:int=%s' % int( ignore_layout) # Generate plan link return '<a href="%s/%s?field_id=%s&form_id=%s%s">' \ '<img src="%s/images/jump.png" alt="jump" />' \ '</a>' % ( escape(here.absolute_url()), escape(field.get_value('jump_method')), escape(field.id), escape(field.aq_parent.id), escape(selection_name_html), escape(here.getPortalObject().portal_url()), ) return ''
class MatrixBoxWidget(Widget.Widget): """ An UI widget which displays a matrix Don't forget that you can use tales expressions for every field, so this is really usefull if you want to use fonctions instead of predefined variables. A function is provided to - access a cell - modify a cell """ property_names = Widget.Widget.property_names +\ ['cell_base_id', 'cell_portal_type', 'lines', 'columns', 'tabs', 'as_cell_range_script_id', 'getter_method', 'editable_attributes', 'global_attributes', 'cell_getter_method', 'update_cell_range', 'url_cells' ] default = fields.TextAreaField( 'default', title='Default', description=("Default value of the text in the widget."), default="", width=20, height=3, required=0) as_cell_range_script_id = fields.StringField( 'as_cell_range_script_id', title='Cell range method', description=( "Method returning columns, lines and tabs. The method is passed" " matrixbox=True, base_id=base_id as arguments."), default='', required=0) columns = fields.ListTextAreaField( 'columns', title="Columns", description=( "This defines columns of the matrixbox. " "This should be a list of couples, " "couple[0] is the variation, and couple[1] is the name displayed " "to the user.\n" "For example (('color/blue', 'Bleu'), ('color/red','Red')).\n" " Deprecated, use cell range method instead"), default=[], required=0) lines = fields.ListTextAreaField( 'lines', title="Lines", description= ("This defines lines of the matrixbox. This should be a list of couples, " "couple[0] is the variation, and couple[1] is the name displayed " "to the user.\n" "For example (('size/baby/02','baby/02'),('size/baby/03','baby/03')).\n" "Deprecated, use cell range method instead"), default=[], required=0) tabs = fields.ListTextAreaField( 'tabs', title="Tabs", description=( "This defines tabs. You can use it with the same way as Lines " "and Columns.\n" "This is used only if you have more than 2 kinds of variations.\n" "Deprecated, use cell range method instead"), default=[], required=0) # XXX ListTextAreaField ? cell_range = fields.ListTextAreaField( 'cell_range', title="Cell Range", description=("This defines the range of the matrix."), default=[], required=0) getter_method = fields.StringField( 'getter_method', title='Getter method', description= ("You can specify a specific method in order to retrieve the context. " "This field can be empty, if so the MatrixBox will use the default " "context."), default='', required=0) cell_getter_method = fields.StringField( 'cell_getter_method', title='Cell Getter method', description= ("You can specify a method in order to retrieve cells. This field can " "be empty, if so the MatrixBox will use the default method : getCell." ), default='', required=0) new_cell_method = fields.MethodField( 'new_cell_method', title='New Cell method', description=( "You can specify a specific method in order to create cells. " "This field can be empty, if so the MatrixBox will use the default " "method : newCell."), default='', required=0) editable_attributes = fields.ListTextAreaField( 'editable_attributes', title="Editable Properties", description=( "A list of attributes which are set by hidden fields called " "matrixbox_attribute_name. This is used " "when we want to specify a computed value for each cell"), default=[], required=0) global_attributes = fields.ListTextAreaField( 'global_attributes', title="Global Properties", description=( "An optional list of globals attributes which are set by hidden " "fields and which are applied to each cell. " "This is used if we want to set the same value for every cell"), default=[], required=0) cell_base_id = fields.StringField( 'cell_base_id', title='Base id for cells', description=( "The Base id for cells : this is the name used to store cells, " "we usually use names like : 'movement', 'path', ... "), default='cell', required=0) cell_portal_type = fields.StringField( 'cell_portal_type', title='Portal Type for cells', description=( "The Portal Type for cells : This is the portal type used to " "create a new cell."), default='Mapped Value', required=0) update_cell_range = fields.CheckBoxField( 'update_cell_range', title="Update Cell Range", description=("The cell range should be updated upon edit."), default=0) url_cells = fields.ListTextAreaField( 'url_cells', title="URL Cells", description=( "An optional list of cells which can provide a custom URL." "If no url cell is used, then no link is displayed."), default=[], required=0) def render(self, field, key, value, REQUEST, render_format='html', render_prefix=None): """ This is where most things happen. This method renders a list of items """ # First grasp the variables we may need here = REQUEST['here'] form = field.aq_parent field_title = field.get_value('title') cell_base_id = field.get_value('cell_base_id') context = here getter_method_id = field.get_value('getter_method') if getter_method_id not in (None, ''): context = getattr(here, getter_method_id)() if context is None: return '' as_cell_range_script_id = field.get_value('as_cell_range_script_id') extra_dimension_category_list_list = [None] if as_cell_range_script_id: lines = [] columns = [] tabs = [] dimension_list = guarded_getattr(context, as_cell_range_script_id)( matrixbox=True, base_id=cell_base_id) len_dimension_list = len(dimension_list) if len_dimension_list: if len_dimension_list == 1: lines, = dimension_list elif len_dimension_list == 2: lines, columns = dimension_list elif len_dimension_list >= 3: lines, columns, tabs = dimension_list[:3] if len_dimension_list > 3: extra_dimension_list = dimension_list[3:] extra_dimension_category_label_dict = {} extra_dimension_category_index_dict = {} for extra_dimension in extra_dimension_list: for index, (category, label) in enumerate(extra_dimension): extra_dimension_category_label_dict[ category] = label extra_dimension_category_index_dict[ category] = index from Products.ERP5Type.Utils import cartesianProduct extra_dimension_category_list_list = cartesianProduct( [[category for category, label in extra_dimension] for extra_dimension in extra_dimension_list]) else: lines = field.get_value('lines') columns = field.get_value('columns') tabs = field.get_value('tabs') field_errors = REQUEST.get('field_errors', {}) cell_getter_method_id = field.get_value('cell_getter_method') if cell_getter_method_id not in (None, ''): cell_getter_method = getattr(context, cell_getter_method_id) else: cell_getter_method = context.getCell editable_attributes = field.get_value('editable_attributes') url_cells = field.get_value('url_cells') url_cell_dict = dict(url_cells) # This is required when we have no tabs if len(tabs) == 0: tabs = [(None, None)] # This is required when we have no columns if len(columns) == 0: columns = [(None, None)] column_ids = [x[0] for x in columns] line_ids = [x[0] for x in lines] tab_ids = [x[0] for x in tabs] editable_attribute_ids = [x[0] for x in editable_attributes] # THIS MUST BE REMOVED - WHY IS THIS BAD ? # IT IS BAD BECAUSE TAB_IDS DO NOT DEFINE A RANGE.... # here.setCellRange(line_ids, column_ids, base_id=cell_base_id) # result for the list render list_result = [] url = REQUEST.URL list_html = '' for extra_dimension_category_list in extra_dimension_category_list_list: if extra_dimension_category_list is None: extra_dimension_label = '' extra_dimension_position = () extra_dimension_index = '' extra_dimension_category_list = [] else: extra_dimension_label = ',' + ','.join([ extra_dimension_category_label_dict[category] for category in extra_dimension_category_list ]) extra_dimension_position = tuple([ extra_dimension_category_index_dict[category] for category in extra_dimension_category_list ]) extra_dimension_index = '_' + '_'.join( map(str, extra_dimension_position)) # Create one table per tab k = 0 kwd = dict(base_id=cell_base_id) for tab in tabs: tab_id = tab[0] if (tab_id is not None) and \ (not isinstance(tab_id, (list, tuple))): tab_id = [tab_id] if render_format == 'list': list_result_tab = [[tab[1]]] # Create the header of the table - this should probably become DTML first_tab = tab[1] or '' header = """\ <!-- Matrix Content --> <div class="matrixbox_label_tab">%s</div> <div class="MatrixContent"> <table> """ % (first_tab + extra_dimension_label) # Create the footer. This should be replaced by DTML # And work as some kind of parameter footer = """\ <tr> <td colspan="%i" class="Data footer"> </td> </tr> </table> </div> """ % (len(columns) + 1) list_header = """\ <tr class="matrixbox_label_line"><td class=\"Data\"></td> """ for cname in columns: first_column = cname[1] or '' list_header = list_header + ( "<td class=\"Data\">%s</td>\n" % first_column) if render_format == 'list': list_result_tab[0].append(cname[1]) list_header = list_header + "</tr>" # Build Lines i = 0 j = 0 list_body = '' for l in lines: if not i % 2: td_css = 'DataA' else: td_css = 'DataB' list_body = list_body + '\n<tr class=\"%s\"><td class=\"matrixbox_label_column\">%s</td>' % ( td_css, str(l[1])) j = 0 if render_format == 'list': list_result_lines = [str(l[1])] for c in columns: has_error = False column_id = c[0] if (column_id is not None) and \ (not isinstance(column_id, (list, tuple))): column_id = [column_id] if column_id is None: kw = [l[0]] elif tab_id is None: kw = [l[0], c[0]] else: kw = [l[0], c[0] ] + tab_id + extra_dimension_category_list cell = cell_getter_method(*kw, **kwd) REQUEST['cell'] = cell REQUEST['cell_index'] = kw cell_body = '' cell_url = None for attribute_id in editable_attribute_ids: if attribute_id in url_cell_dict: url_method_id = url_cell_dict[attribute_id] if url_method_id not in (None, ''): url_method = getattr( cell, url_method_id, None) if url_method is not None: try: cell_url = url_method( brain=cell, cell_index=kw, cell_position=( (i, j, k) + extra_dimension_position)) except (ConflictError, RuntimeError): raise except: LOG('MatrixBox', WARNING, 'Could not evaluate the url ' 'method %r with %r' % (url_method, cell), error=sys.exc_info()) else: LOG( 'MatrixBox', WARNING, 'Could not find the url method %s' % (url_method_id, )) my_field_id = '%s_%s' % (field.id, attribute_id) if form.has_field(my_field_id): my_field = form.get_field(my_field_id) key = my_field.id + '_cell_%s_%s_%s%s' % ( i, j, k, extra_dimension_index) default_value = my_field.get_value( 'default', cell=cell, cell_index=kw, cell_position=((i, j, k) + extra_dimension_position)) display_value = default_value if field_errors: # Display previous value in case of any error in this form because # we have no cell to get value from display_value = REQUEST.get( 'field_%s' % key, default_value) if cell is not None: if render_format == 'html': cell_html = my_field.render( value=display_value, REQUEST=REQUEST, key=key) if cell_url: # don't make a link if widget is editable if not my_field.get_value( 'editable', cell=cell, cell_index=kw, cell_position=( (i, j, k) + extra_dimension_position )): cell_html = "<a href='%s'>%s</a>" % ( cell_url, cell_html) if key in field_errors: # Display error message if this cell has an error has_error = True cell_body += '<span class="input">%s</span>%s' % ( cell_html, translateString( field_errors[key]. error_text)) else: cell_body += '<span class="input">%s</span>' % ( cell_html) else: if render_format == 'html': if key in field_errors: # Display error message if this cell has an error has_error = True cell_body += '<span class="input">%s</span>%s' % ( my_field.render( value=display_value, REQUEST=REQUEST, key=key), translateString( field_errors[key]. error_text)) else: cell_body += '<span class="input">%s</span>' %\ my_field.render( value=display_value, REQUEST=REQUEST, key=key) if render_format == 'list': # list rendering doesn't make difference when cell exists or not list_result_lines.append({ 'default': default_value, 'value': display_value, 'key': key, 'type': my_field.meta_type if my_field.meta_type != "ProxyField" else my_field. getRecursiveTemplateField().meta_type, 'field_id': my_field.id, 'error_text': u"%s" % (translateString( field_errors[key].error_text) if key in field_errors else '') }) css = td_css if has_error: css = 'error' list_body = list_body + \ ('<td class=\"%s\">%s</td>' % (css, cell_body)) j += 1 list_body = list_body + '</tr>' i += 1 if render_format == 'list': list_result_tab.append(list_result_lines) list_html += header + list_header + \ list_body + footer k += 1 if render_format == 'list': list_result.append(list_result_tab) # XXX Does not leave garbage in REQUEST['cell'], because some other # fields also use that key... REQUEST.other.pop('cell', None) REQUEST.other.pop('cell_index', None) if render_format == 'list': return list_result return list_html
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', 'context_getter_id', 'catalog_index', 'relation_setter_id', 'relation_form_id', 'columns', 'sort', 'parameter_list', 'list_method', 'first_item', 'items', 'proxy_listbox_ids', '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) context_getter_id = fields.StringField( 'context_getter_id', title='Context Getter Method', description=("The method to call to get the context."), 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) relation_form_id = fields.StringField( 'relation_form_id', title='Relation Form', description=("Form to display relation choices"), 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) proxy_listbox_ids = fields.ListTextAreaField( 'proxy_listbox_ids', title='Proxy Listbox IDs', description=('A list of listbox that can be used as proxy'), 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 _getContextValue(self, field, REQUEST): """Return result of evaluated method defined by context_getter_id or here. """ context_getter_id = field.get_value('context_getter_id') here = REQUEST['here'] if context_getter_id: return getattr(here, context_getter_id)() return here 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") else: # We get a list # rather than displaying nothing, display a marker when the # property is not set # XXX Translate ? value_list = [(x or NO_VALUE) for x in value_list] # 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 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, render_prefix=None): """ Render text input field. """ html_string = '' relation_field_index = REQUEST.get('_v_relation_field_index', 0) 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) here = self._getContextValue(field, REQUEST) portal = here.getPortalObject() autocomplete_enabled = getattr(portal.portal_skins, 'erp5_autocompletion_ui', None) if autocomplete_enabled: sub_html_string += self.render_autocomplete(field, key) if relation_item_list is not None: #################################### # Render wheel #################################### if not autocomplete_enabled: sub_html_string += self.render_wheel( field, value_instance, REQUEST, relation_index=relation_field_index, sub_index=sub_index) if relation_item_list: #################################### # Render listfield #################################### REQUEST['relation_item_list'] = relation_item_list sub_html_string += ' %s ' % \ Widget.ListWidgetInstance.render( field, relation_field_id, None, REQUEST) REQUEST['relation_item_list'] = None 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 += ' %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, REQUEST=None, render_prefix=None): """ Render read only field. """ html_string = '' here = self._getContextValue(field, REQUEST) portal_url = here.getPortalObject().portal_url portal_url_string = portal_url() if (value not in ((), [], None, '')) and \ field.get_value('allow_jump'): string_list = [] base_category = field.get_value('base_category') portal_type = map(lambda x: x[0], field.get_value('portal_type')) kw = {} for k, v in field.get_value('parameter_list'): kw[k] = v accessor_name = 'get%sValueList' % ''.join( [part.capitalize() for part in base_category.split('_')]) jump_reference_list = getattr(here, accessor_name)( portal_type=portal_type, filter=kw) if not isinstance(value, (list, tuple)): value = value, for jump_reference, display_value in zip(jump_reference_list, value): string_list.append('<a class="relationfieldlink" href="%s">%s</a>' % \ (jump_reference.absolute_url(), display_value)) html_string = '<br />'.join(string_list) else: html_string = self.default_widget_rendering_instance.render_view( field, value, REQUEST=REQUEST) if REQUEST is None: REQUEST = get_request() relation_html_string = self.render_relation_link( field, value, REQUEST) if relation_html_string != '': html_string += ' %s' % relation_html_string extra = field.get_value('extra') if extra not in (None, ''): html_string = "<div %s>%s</div>" % (extra, html_string) css_class = field.get_value('css_class') if css_class not in ('', None): # All strings should be escaped before rendering in HTML # except for editor field html_string = "<span class='%s'>%s</span>" % (css_class, html_string) return html_string def render_autocomplete(self, field, key): """ Use jquery-ui autocompletion for all relation fields by default, requiring only erp5_autocompletion_ui bt5 to be installed """ # XXX: Allow to specify more parameters to jquery-ui autocomplete widget? import json return """ <script type="text/javascript"> $(document).ready(function() { $("input[name='%s']").ERP5Autocomplete({search_portal_type: %s, search_catalog_key: "%s"}); }); </script>""" % (key, json.dumps(map(lambda x: x[0], field.get_value('portal_type'))), field.get_value('catalog_index')) def render_wheel(self, field, value, REQUEST, relation_index=0, sub_index=None, render_prefix=None): """ Render wheel used to display a listbox """ here = self._getContextValue(field, REQUEST) portal_url = here.getPortalObject().portal_url portal_url_string = portal_url() portal_selections_url_string = here.portal_url.getRelativeContentURL( here.portal_selections) if sub_index is None: sub_index_string = '' else: sub_index_string = '_%s' % sub_index return ' <input type="image" ' \ 'src="%s/images/exec16.png" value="update..." ' \ 'name="%s/viewSearchRelatedDocumentDialog%s%s' \ ':method"/>' % \ (portal_url_string, portal_selections_url_string, relation_index, sub_index_string) def render_relation_link(self, field, value, REQUEST, render_prefix=None): """ Render link to the related object. """ html_string = '' here = REQUEST.get('cell', self._getContextValue(field, REQUEST)) portal_url = here.getPortalObject().portal_url portal_url_string = portal_url() if (value not in ((), [], None, '')) and \ field.get_value('allow_jump'): # 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 = '' if REQUEST.get('ignore_layout') is not None: selection_name_html += '&ignore_layout:int=%s' % int( REQUEST.get('ignore_layout', 0)) # Generate plan link html_string += '<a href="%s/%s?field_id=%s&form_id=%s%s">' \ '<img src="%s/images/jump.png" alt="jump" />' \ '</a>' % \ (here.absolute_url(), field.get_value('jump_method'), field.id, field.aq_parent.id, selection_name_html, portal_url_string) return html_string