예제 #1
0
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)
예제 #2
0
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)
예제 #3
0
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
예제 #4
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.')
예제 #5
0
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 '&nbsp;<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 = '&amp;selection_name=%s&amp;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 += '&amp;ignore_layout:int=%s' % int(
                    ignore_layout)
            # Generate plan link
            return '<a href="%s/%s?field_id=%s&amp;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 ''
예제 #6
0
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
예제 #7
0
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 += '&nbsp;%s&nbsp;' % \
                                          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 += '&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, 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 += '&nbsp;&nbsp;%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 '&nbsp;<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 = '&amp;selection_name=%s&amp;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 += '&amp;ignore_layout:int=%s' % int(
                    REQUEST.get('ignore_layout', 0))
            # Generate plan link
            html_string += '<a href="%s/%s?field_id=%s&amp;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