Esempio n. 1
0
 def _save(self):
     form = self.request.form
     context = self.context
     label = form.get('label')
     _ = getToolByName(context, 'translation_service').utranslate
     if form.get('row-index') is not None and form.get('addLabel'):
         # adding new but not in the last line
         index = form.get('row-index')
         self.storage.add({'__label__': label,
                           '__uuid__': str(uuid.uuid4())}, index)
         self._addNewVersion(_(msgid="Label added",
                               domain="collective.tablepage",
                               context=context))
     elif form.get('row-index') is not None:
         # updating label
         index = form.get('row-index')
         self.storage.update(index, {'__label__': label})
         self._addNewVersion(_(msgid="Label changed",
                               domain="collective.tablepage",
                               context=context))
     else:
         self.storage.add({'__label__': label,
                           '__uuid__': str(uuid.uuid4())})
         self._addNewVersion(_(msgid="Label added",
                               domain="collective.tablepage",
                               context=context))
Esempio n. 2
0
 def __call__(self, *args, **kwargs):
     request = self.request
     form = request.form
     context = self.context
     putils = getToolByName(context, 'plone_utils')
     b_start = form.get('b_start', None)
     if not self.check_labeling_permission():
         raise Unauthorized("You can't modify the label")
     if form.get('cancel'):
         return request.response.redirect(
             "%s/edit-table%s" %
             (context.absolute_url(),
              b_start and '?b_start:int=%d' % b_start or ''))
     if form.get('form.submitted'):
         if not form.get('label'):
             putils.addPortalMessage(_('Label is required'), type="error")
             return request.response.redirect(
                 "%s/edit-label%s" %
                 (context.absolute_url(),
                  b_start and '?b_start:int=%d' % b_start or ''))
         # saving
         self._save()
         putils.addPortalMessage(_('Label has been saved'))
         return request.response.redirect(
             "%s/edit-table%s" %
             (context.absolute_url(),
              b_start and '?b_start:int=%d' % b_start or ''))
     elif form.get('row-index') is not None and not form.get('addLabel'):
         # load an existing row
         self.data = self.storage[form.get('row-index')].get(
             '__label__', '')
     return self.index()
Esempio n. 3
0
 def __call__(self, context):
     terms = [SimpleTerm(value='SearchableText', token='SearchableText',
                         title=_(u'Use in full text search')),
              SimpleTerm(value='single_value', token='single_value',
                         title=_(u'Single value search')),
              ]
     return SimpleVocabulary(terms)
Esempio n. 4
0
    def __call__(self):
        form = self.request.form
        context = self.context
        index = form.get('row-index')
        direction = form.get('direction')
        b_start = form.get('b_start', None)

        if index is not None and direction in ('up', 'down'):
            tp_catalog = getToolByName(context, config.CATALOG_ID)
            direction = direction=='up' and -1 or 1
            storage = self.storage
            row = storage[index]
            del storage[index]
            storage.add(row, index+direction)
            tp_catalog.reindex_rows(context, [row['__uuid__'], storage[index]['__uuid__']])
            putils = getToolByName(context, 'plone_utils')
            _ = getToolByName(context, 'translation_service').utranslate
            putils.addPortalMessage(_(msgid='Row has been moved',
                                      domain="collective.tablepage",
                                      context=context))
            # if the table is changed, the content is changed
            self._addNewVersion(_(msgid="Row moved",
                                  domain="collective.tablepage",
                                  context=context))
        self.request.response.redirect("%s/edit-table%s" % (context.absolute_url(),
                                                            b_start and '?b_start:int=%d' % b_start or ''))
Esempio n. 5
0
 def validate_pageColumns(self, value):
     """Need to check some table format"""
     ids = []
     for record in value:
         # do not validate the hidden empty row
         if record.get("orderindex_").isdigit():
             id = record.get("id", "")
             try:
                 ids.index(id)
                 return _(
                     "pagecolumn_validation_error_duplicated_id",
                     default=u'Id "${col_name}" is duplicated',
                     mapping={"col_name": id},
                 )
             except ValueError:
                 ids.append(id)
             if not re.match(r"^[a-zA-Z][a-zA-Z0-9.\-_]*$", id):
                 return _(
                     "pagecolumn_validation_error_id_format",
                     default=u'Invalid value: "${col_name}". "Column Id" must not contains special characters',
                     mapping={"col_name": id},
                 )
             if id in config.RESERVED_IDS:
                 return _(
                     "pagecolumn_validation_error_id_invalid", default=u'A reserved value has been used for "id"'
                 )
Esempio n. 6
0
    def render_view(self, data, index=None, storage=None):
        """When in view, render data in the proper monetary (and localized) format"""
        self.data = data or ''
        if self.data:

            try:
                float(self.data)
            except ValueError:
                # Do not continue
                return self.data

            parts = self.data.split('.')
            try:
                i_data = int(parts[0])
            except ValueError:
                return self.view_template(data='')
            i_data = self.intWithCommas(i_data)

            if len(parts) > 1:
                dec_data = parts[1]
            else:
                dec_data = '00'
            if len(dec_data) < 2:
                dec_data = dec_data + "0"
            dec_data = dec_data[0:2]

            decimal_separator = translate(_('decimal_separator', default="."),
                                          context=self.request)
            monetary_sign = translate(_('monetary_sign', default="$"),
                                      context=self.request)

            self.data = "%s %s%s%s" % (monetary_sign, i_data,
                                       decimal_separator, dec_data)
            return self.view_template(data=self.data)
        return ''
Esempio n. 7
0
 def _save(self):
     form = self.request.form
     context = self.context
     label = form.get('label')
     _ = getToolByName(context, 'translation_service').utranslate
     if form.get('row-index') is not None and form.get('addLabel'):
         # adding new but not in the last line
         index = form.get('row-index')
         self.storage.add(
             {
                 '__label__': label,
                 '__uuid__': str(uuid.uuid4())
             }, index)
         self._addNewVersion(
             _(msgid="Label added",
               domain="collective.tablepage",
               context=context))
     elif form.get('row-index') is not None:
         # updating label
         index = form.get('row-index')
         self.storage.update(index, {'__label__': label})
         self._addNewVersion(
             _(msgid="Label changed",
               domain="collective.tablepage",
               context=context))
     else:
         self.storage.add({
             '__label__': label,
             '__uuid__': str(uuid.uuid4())
         })
         self._addNewVersion(
             _(msgid="Label added",
               domain="collective.tablepage",
               context=context))
Esempio n. 8
0
 def __call__(self, *args, **kwargs):
     request = self.request
     form = request.form
     context = self.context
     putils = getToolByName(context, 'plone_utils')
     b_start = form.get('b_start', None)
     if not self.check_labeling_permission():
         raise Unauthorized("You can't modify the label")
     if form.get('cancel'):
         return request.response.redirect("%s/edit-table%s" % (context.absolute_url(),
                                                               b_start and '?b_start:int=%d' % b_start or ''))
     if form.get('form.submitted'):
         if not form.get('label'):
             putils.addPortalMessage(_('Label is required'), type="error")
             return request.response.redirect("%s/edit-label%s" % (context.absolute_url(),
                                                                   b_start and '?b_start:int=%d' % b_start or ''))
         # saving
         self._save()
         putils.addPortalMessage(_('Label has been saved'))
         return request.response.redirect("%s/edit-table%s" % (context.absolute_url(),
                                                               b_start and '?b_start:int=%d' % b_start or ''))
     elif form.get('row-index') is not None and not form.get('addLabel'):
         # load an existing row
         self.data = self.storage[form.get('row-index')].get('__label__', '')
     return self.index()
Esempio n. 9
0
    def __call__(self):
        form = self.request.form
        context = self.context
        index = form.get('row-index')
        direction = form.get('direction')
        b_start = form.get('b_start', None)

        if index is not None and direction in ('up', 'down'):
            tp_catalog = getToolByName(context, config.CATALOG_ID)
            direction = direction == 'up' and -1 or 1
            storage = self.storage
            row = storage[index]
            del storage[index]
            storage.add(row, index + direction)
            tp_catalog.reindex_rows(
                context, [row['__uuid__'], storage[index]['__uuid__']])
            putils = getToolByName(context, 'plone_utils')
            _ = getToolByName(context, 'translation_service').utranslate
            putils.addPortalMessage(
                _(msgid='Row has been moved',
                  domain="collective.tablepage",
                  context=context))
            # if the table is changed, the content is changed
            self._addNewVersion(
                _(msgid="Row moved",
                  domain="collective.tablepage",
                  context=context))
        self.request.response.redirect(
            "%s/edit-table%s" %
            (context.absolute_url(), b_start and '?b_start:int=%d' % b_start
             or ''))
Esempio n. 10
0
 def showHeadersVocabulary(self):
     return atapi.DisplayList(
         (
             ("view_only", _("... only on page view")),
             ("edit_only", _("... only when editing table")),
             ("always", _("... always display")),
         )
     )
Esempio n. 11
0
 def __call__(self, context):
     request = context.REQUEST
     adapters = getAdapters((context, context.REQUEST), IColumnField)
     elements = [a[0] for a in adapters]
     elements.sort(cmp=lambda x, y: cmp(translate(_(x), context=request),
                                        translate(_(y), context=request)))
     terms = [SimpleTerm(value=e, token=e, title=_(e)) for e in elements]
     return SimpleVocabulary(terms)
Esempio n. 12
0
 def __call__(self, context):
     request = context.REQUEST
     adapters = getAdapters((context, context.REQUEST), IColumnField)
     elements = [a[0] for a in adapters]
     elements.sort(cmp=lambda x,y: cmp(translate(_(x), context=request),
                                       translate(_(y), context=request)))
     terms = [SimpleTerm(value=e, token=e, title=_(e)) for e in elements]
     return SimpleVocabulary(terms)
Esempio n. 13
0
    def __call__(self, context):
        terms = [
                 SimpleTerm(value='required', token='required', title=_('row_options_required',
                                                                        default=u'Required')),
                 SimpleTerm(value='unique', token='unique', title=_('row_options_unique',
                                                                        default=u'Unique')),
#                 SimpleTerm(value='enforceVocabulary', token='enforceVocabulary', title=_('row_options_enforceVocabulary',
#                                                                                          default=u'Fulfil vocabulary')),
                 ]
        return SimpleVocabulary(terms)
Esempio n. 14
0
 def __call__(self, context):
     terms = [
         SimpleTerm(value='SearchableText',
                    token='SearchableText',
                    title=_(u'Use in full text search')),
         SimpleTerm(value='single_value',
                    token='single_value',
                    title=_(u'Single value search')),
     ]
     return SimpleVocabulary(terms)
Esempio n. 15
0
 def __call__(self, context):
     terms = [
         SimpleTerm(value='required',
                    token='required',
                    title=_('row_options_required', default=u'Required')),
         SimpleTerm(value='unique',
                    token='unique',
                    title=_('row_options_unique', default=u'Unique')),
         #                 SimpleTerm(value='enforceVocabulary', token='enforceVocabulary', title=_('row_options_enforceVocabulary',
         #                                                                                          default=u'Fulfil vocabulary')),
     ]
     return SimpleVocabulary(terms)
Esempio n. 16
0
    def __call__(self):
        context = self.context
        request = self.request
        indexes = self.request.form.get('row-index')
        b_start = request.form.get('b_start', None)
        tp_catalog = getToolByName(context, config.CATALOG_ID)

        if indexes is not None:
            member = getMultiAdapter((context, request),
                                     name=u'plone_portal_state').member()
            putils = getToolByName(context, 'plone_utils')
            _ = getToolByName(context, 'translation_service').utranslate
            sm = getSecurityManager()
            # check permissions: must be the owner user or have the "Manage table" permission
            storage = self.storage
            if isinstance(indexes, int):
                # we get an int when clicking on single row delete command
                indexes = [
                    indexes,
                ]
            for c, index in enumerate(indexes):
                if not sm.checkPermission(config.MANAGE_TABLE, context) \
                            and member.getId()!=storage[index-c].get('__creator__'):
                    raise Unauthorized("You can't delete that record")
                tp_catalog.uncatalog_row(context,
                                         storage[index - c].get('__uuid__'))
                del storage[index - c]

            # Now we need to reindex all rows that follow, to fill the hole
            min_index = min(indexes)
            self._reindex_following(min_index - c)

            if len(indexes) == 1:
                msg = _(msgid="Row deleted",
                        domain="collective.tablepage",
                        context=context)
            else:
                msg = _(msgid='${count} rows deleted',
                        domain="collective.tablepage",
                        mapping={'count': len(indexes)},
                        context=context)
            putils.addPortalMessage(msg)
            # if the table is changed, the content is changed
            self._addNewVersion(msg)

        request.response.redirect(
            "%s/edit-table%s" %
            (context.absolute_url(), b_start and '?b_start:int=%d' % b_start
             or ''))
Esempio n. 17
0
 def __call__(self, *args, **kwargs):
     context = self.context
     request = self.request
     form = request.form
     self.row_index = form.get('row-index', None)
     b_start = form.get('b_start', None)
     if form.get('cancel'):
         request.response.redirect(
             "%s/edit-table%s" %
             (context.absolute_url(),
              b_start and '?b_start:int=%d' % b_start or ''))
         return
     if form.get('form.submitted'):
         # saving
         putils = getToolByName(context, 'plone_utils')
         self._save()
         if self.errors:
             del form['form.submitted']
             self.data.update(**form)
             return self.index()
         putils.addPortalMessage(_('Row has been saved'))
         request.response.redirect(
             "%s/edit-table%s" %
             (context.absolute_url(),
              b_start and '?b_start:int=%d' % b_start or ''))
         return
     elif self.row_index is not None and not form.get('addRow'):
         # load an existing row
         if not self.check_manager_or_mine_record(self.row_index):
             raise Unauthorized("You can't modify that record")
         else:
             self.data = self.storage[self.row_index]
     return self.index()
Esempio n. 18
0
 def __call__(self, *args, **kwargs):
     context = self.context
     request = self.request
     catalog = getToolByName(context, config.CATALOG_ID)
     storage = IDataStorage(context)
     # now we load the tabel view and rebuild all rows by using the ignore_cache parameter
     table_view = getMultiAdapter((context, request), name=u'view-table')
     table_view.rows(ignore_cache=True)
     for index, row in enumerate(storage):
         uuid = row.get('__uuid__')
         if not uuid:
             # this should not happen
             logger.warning(
                 "Row without an uuid! index %d, document at %s" %
                 (index, context.absolute_url_path()))
             continue
         catalog.reindex_rows(context, uuid, storage)
         if index and index % 100 == 0:
             logger.info("Refreshing catalog (%d)" % index)
             transaction.savepoint()
     logger.info("Refreshing catalog and caches: done")
     getToolByName(context, 'plone_utils').addPortalMessage(
         _('reindex_performed_message',
           u'$count rows has been updated',
           mapping={'count': index + 1}))
     request.response.redirect('%s/edit-table' % context.absolute_url())
Esempio n. 19
0
 def __call__(self, *args, **kwargs):
     context = self.context
     request = self.request
     form = request.form
     self.row_index = form.get('row-index', None)
     b_start = form.get('b_start', None)
     if form.get('cancel'):
         request.response.redirect("%s/edit-table%s" % (context.absolute_url(),
                                                        b_start and '?b_start:int=%d' % b_start or ''))
         return
     if form.get('form.submitted'):
         # saving
         putils = getToolByName(context, 'plone_utils')
         self._save()
         if self.errors:
             del form['form.submitted']
             self.data.update(**form)
             return self.index()
         putils.addPortalMessage(_('Row has been saved'))
         request.response.redirect("%s/edit-table%s" % (context.absolute_url(),
                                                        b_start and '?b_start:int=%d' % b_start or ''))
         return
     elif self.row_index is not None and not form.get('addRow'):
         # load an existing row
         if not self.check_manager_or_mine_record(self.row_index):
             raise Unauthorized("You can't modify that record")
         else:
             self.data = self.storage[self.row_index]
     return self.index()
Esempio n. 20
0
 def validate(self, configuration, data=None):
     data = data or self.field.request.form.get(configuration['id'], '')
     if data:
         ptool = getToolByName(self.field.context, 'plone_utils')
         if ptool.validateEmailAddresses(data):
             return
         return _('error_field_not_email', default='The field "$name" is not a valid e-mail address',
                  mapping={'name': configuration.get('label', configuration['id']).decode('utf-8')})
Esempio n. 21
0
 def validate(self, configuration, data=None):
     if 'required' not in configuration.get('options', []):
         return None
     form = self.field.request.form
     field_id = configuration['id']
     if not form.get("%s_0" % field_id) and not form.get('existing_%s' % field_id) and not data:
         return _('error_field_required', default='The field "$name" is required',
                  mapping={'name': configuration.get('label', configuration['id']).decode('utf-8')})
Esempio n. 22
0
 def __call__(self, context):
     configuration = context.getPageColumns()
     adaptables = [x[0] for x in getUtilitiesFor(ISearchableColumn)]
     terms = [SimpleTerm(value='SearchableText', token='SearchableText', title=_(u'Search in text'))]
     for conf in configuration:
         if conf['type'] in adaptables:
             terms.append(SimpleTerm(value=conf['id'], token=conf['id'], title=conf['label']))
     return SimpleVocabulary(terms)
Esempio n. 23
0
    def get_from_request(self, name, request):
        if request.get(name) and request.get(name).filename:
            folder = self.context.getAttachmentStorage() or aq_parent(aq_inner(self.context))
            title = request.get('title_%s' % name)
            description = request.get('description_%s' % name)
            file = request.get(name)
            newId = folder.generateUniqueId(self.portal_type)
            plone_utils = getToolByName(self.context, 'plone_utils')
            if not title and file.filename in folder.objectIds():
                # WARNING: we don't get the file title, to obtain the id
                plone_utils.addPortalMessage(_('duplicate_file_error_with_link',
                                               default=u'There is already an item named ${name} in this folder.\n'
                                                       u'Loading of the new attachment has been aborted '
                                                       u'and a reference to that existing file has been created.',
                                               mapping={'name': file.filename}),
                                             type='warning')
                return {name: folder[file.filename].UID()}
            folder.invokeFactory(id=newId, type_name=self.portal_type,
                                 title=title, description=description)
            new_doc = folder[newId]
            if HAS_DEXTERITY and IDexterityContent.providedBy(new_doc):
                setattr(new_doc, self.field_name, self.field_value_class(file, filename=unicode(file.filename)))
                modified(new_doc)
                if not folder.has_key(file.filename):
                    folder.manage_renameObject(new_doc.getId(), file.filename)
            else:
                # force rename (processForm will not work with files)
                new_doc._renameAfterCreation()
                # this will trigger proper lifecycle events
                new_doc.processForm()

                try:
                    new_doc.edit(**{self.field_name: file})

                except BadRequest:
                    # Still don't get how, but sometimes this happen (at least on Plone 3)
                    plone_utils.addPortalMessage(_('duplicate_file_critical_error',
                                                   default=u'There is already an item named ${name} in this folder.\n'
                                                           u'Loading of the attachment has been aborted.',
                                                   mapping={'name': file.filename}),
                                                 type='error')
                    return None
            return {name: new_doc.UID()}
        elif request.get("existing_%s" % name):
            return {name: request.get("existing_%s" % name)}
        return {name: ''}
Esempio n. 24
0
 def validate(self, configuration, data=None):
     data = data or self.field.request.form.get(configuration['id'], '')
     if data:
         try:
             float(data)
             return
         except ValueError:
             return _('error_field_not_number', default='The value "$value" is not numeric',
                      mapping={'value': data.decode('utf-8')})
Esempio n. 25
0
 def validate(self, configuration, data=None):
     col_id = configuration['id']
     self.field.configuration = configuration
     vocabulary = self.field.vocabulary()
     data = data or self.field.request.form.get(col_id)
     if data and data not in vocabulary:
         return _('error_enforce_vocabulary',
                  default='The field "$name" does not fit any of the vocabulary values',
                  mapping={'name': configuration.get('label', col_id).decode('utf-8')})
Esempio n. 26
0
 def validate(self, configuration, data=None):
     data = data or self.field.request.form.get(configuration['id'], '')
     if data:
         try:
             float(data)
             return
         except ValueError:
             return _('error_field_not_number',
                      default='The value "$value" is not numeric',
                      mapping={'value': data.decode('utf-8')})
Esempio n. 27
0
    def __call__(self):
        context = self.context
        request = self.request
        indexes = self.request.form.get('row-index')
        b_start = request.form.get('b_start', None)
        tp_catalog = getToolByName(context, config.CATALOG_ID)

        if indexes is not None:
            member = getMultiAdapter((context, request), name=u'plone_portal_state').member()
            putils = getToolByName(context, 'plone_utils')
            _ = getToolByName(context, 'translation_service').utranslate
            sm = getSecurityManager()
            # check permissions: must be the owner user or have the "Manage table" permission
            storage = self.storage
            if isinstance(indexes, int):
                # we get an int when clicking on single row delete command
                indexes = [indexes,]
            for c, index in enumerate(indexes):
                if not sm.checkPermission(config.MANAGE_TABLE, context) \
                            and member.getId()!=storage[index-c].get('__creator__'):
                        raise Unauthorized("You can't delete that record")
                tp_catalog.uncatalog_row(context, storage[index-c].get('__uuid__'))
                del storage[index-c]

            # Now we need to reindex all rows that follow, to fill the hole
            min_index = min(indexes)
            self._reindex_following(min_index-c)

            if len(indexes)==1:
                msg = _(msgid="Row deleted",
                        domain="collective.tablepage",
                        context=context)
            else:
                msg = _(msgid='${count} rows deleted',
                        domain="collective.tablepage",
                        mapping={'count': len(indexes)},
                        context=context)
            putils.addPortalMessage(msg)
            # if the table is changed, the content is changed 
            self._addNewVersion(msg)

        request.response.redirect("%s/edit-table%s" % (context.absolute_url(),
                                                       b_start and '?b_start:int=%d' % b_start or ''))
Esempio n. 28
0
 def intWithCommas(self, x):
     if x < 0:
         return '-' + self.intWithCommas(-x)
     result = ''
     while x >= 1000:
         x, r = divmod(x, 1000)
         thousand_separator = translate(_('thousand_separator',
                                          default=","),
                                        context=self.request)
         result = "%s%03d%s" % (thousand_separator, r, result)
     return "%d%s" % (x, result)
Esempio n. 29
0
 def validate(self, configuration, data=None):
     if 'required' not in configuration.get('options', []):
         return None
     if not data and not self.field.request.form.get(configuration['id']):
         return _('error_field_required',
                  default='The field "$name" is required',
                  mapping={
                      'name':
                      configuration.get('label',
                                        configuration['id']).decode('utf-8')
                  })
Esempio n. 30
0
 def intWithCommas(self, x):
     if x < 0:
         return '-' + self.intWithCommas(-x)
     result = ''
     while x >= 1000:
         x, r = divmod(x, 1000)
         thousand_separator = translate(_('thousand_separator',
                                          default=","),
                                         context=self.request)
         result = "%s%03d%s" % (thousand_separator, r, result)
     return "%d%s" % (x, result)
Esempio n. 31
0
 def validate_pageColumns(self, value):
     """Need to check some table format"""
     ids = []
     for record in value:
         # do not validate the hidden empty row
         if record.get('orderindex_').isdigit():
             id = record.get('id', '')
             try:
                 ids.index(id)
                 return _('pagecolumn_validation_error_duplicated_id',
                          default=u'Id "${col_name}" is duplicated',
                          mapping={'col_name': id})
             except ValueError:
                     ids.append(id)
             if not re.match(r"^[a-zA-Z][a-zA-Z0-9.\-_]*$", id):
                 return _('pagecolumn_validation_error_id_format',
                          default=u'Invalid value: "${col_name}". "Column Id" must not contains special characters',
                          mapping={'col_name': id})
             if id in config.RESERVED_IDS:
                 return _('pagecolumn_validation_error_id_invalid',
                          default=u'A reserved value has been used for "id"')
Esempio n. 32
0
 def validate(self, configuration, data=None):
     col_id = configuration['id']
     self.field.configuration = configuration
     vocabulary = self.field.vocabulary()
     data = data or self.field.request.form.get(col_id)
     if data and data.decode("utf-8") not in vocabulary:
         return _(
             'error_enforce_vocabulary',
             default=
             'The field "$name" does not fit any of the vocabulary values',
             mapping={
                 'name': configuration.get('label', col_id).decode('utf-8')
             })
Esempio n. 33
0
 def validate(self, configuration, data=None):
     if 'unique' not in configuration.get('options', []):
         return None
     col_id = configuration['id']
     data = data or self.field.request.form.get(col_id)
     if data:
         context = self.field.context
         storage = IDataStorage(context)
         for i, row in enumerate(storage):
             if i!=self.field.request.form.get('row-index') and row.get(col_id)==data:
                 return _('error_field_unique', default='The value "$value" is already present in the column \"$name\"',
                          mapping={'name': configuration.get('label', col_id).decode('utf-8'),
                                   'value': data.decode('utf-8')})
Esempio n. 34
0
 def __init__(self, context, request):
     BaseField.__init__(self, context, request)
     self.rows = 5
     self.widget = RichWidget(description='',
                              label=_('Html'),
                              filter_buttons=(
                                  'tablecontrols',
                                  'code',
                                  'fullscreen',
                                  'attribs',
                              ),
                              rows=25,
                              allow_file_upload=False)
Esempio n. 35
0
 def validate(self, configuration, data=None):
     data = data or self.field.request.form.get(configuration['id'], '')
     if data:
         ptool = getToolByName(self.field.context, 'plone_utils')
         if ptool.validateEmailAddresses(data):
             return
         return _('error_field_not_email',
                  default='The field "$name" is not a valid e-mail address',
                  mapping={
                      'name':
                      configuration.get('label',
                                        configuration['id']).decode('utf-8')
                  })
Esempio n. 36
0
 def get_from_request(self, name, request):
     if request.get(name) and request.get(name).filename:
         folder = self.context.getAttachmentStorage() or aq_parent(aq_inner(self.context))
         title = request.get('title_%s' % name)
         description = request.get('description_%s' % name)
         file = request.get(name)
         newId = folder.generateUniqueId(TYPE_TO_CREATE)
         if not title and file.filename in folder.objectIds():
             # WARNING: we don't get the file title, to obtain the id
             plone_utils = getToolByName(self.context, 'plone_utils')
             plone_utils.addPortalMessage(_('duplicate_file_error_with_link',
                                            default=u'There is already an item named ${name} in this folder.\n'
                                                    u'Loading of the new attachment has been aborted '
                                                    u'and a reference to that existing file has been created.',
                                            mapping={'name': file.filename}),
                                          type='warning')
             return {name: folder[file.filename].UID()}
         folder.invokeFactory(id=newId, type_name=TYPE_TO_CREATE,
                              title=title, description=description)
         new_doc = folder[newId]
         # force rename (processForm will not work with files)
         new_doc._renameAfterCreation()
         # this will trigger proper lifecycle events
         new_doc.processForm()
         try:
             new_doc.edit(file=file)
         except BadRequest:
             # Still don't get how, but sometimes this happen (at least on Plone 3)
             plone_utils.addPortalMessage(_('duplicate_file_critical_error',
                                            default=u'There is already an item named ${name} in this folder.\n'
                                                    u'Loading of the attachment has been aborted.',
                                            mapping={'name': file.filename}),
                                          type='error')
             return None
         return {name: new_doc.UID()}
     elif request.get("existing_%s" % name):
         return {name: request.get("existing_%s" % name)}
     return {name: ''}
Esempio n. 37
0
 def validate_searchConfig(self, value):
     """Need to check table page ids"""
     ids = []
     for record in value:
         # do not validate the hidden empty row
         if record.get('orderindex_').isdigit():
             id = record.get('id', '')
             try:
                 ids.index(id)
                 return _('searchconfig_validation_error_duplicated_id',
                          default=u'The column "${col_name}" has already been used',
                          mapping={'col_name': id})
             except ValueError:
                     ids.append(id)
Esempio n. 38
0
 def validate(self, configuration, data=None):
     if 'required' not in configuration.get('options', []):
         return None
     form = self.field.request.form
     field_id = configuration['id']
     if not form.get("external_%s" % field_id) and not form.get(
             'internal_%s' % field_id) and not data:
         return _('error_field_required',
                  default='The field "$name" is required',
                  mapping={
                      'name':
                      configuration.get('label',
                                        configuration['id']).decode('utf-8')
                  })
Esempio n. 39
0
 def __call__(self, context):
     configuration = context.getPageColumns()
     adaptables = [x[0] for x in getUtilitiesFor(ISearchableColumn)]
     terms = [
         SimpleTerm(value='SearchableText',
                    token='SearchableText',
                    title=_(u'Search in text'))
     ]
     for conf in configuration:
         if conf['type'] in adaptables:
             terms.append(
                 SimpleTerm(value=conf['id'],
                            token=conf['id'],
                            title=conf['label']))
     return SimpleVocabulary(terms)
Esempio n. 40
0
    def render_view(self, data, index=None, storage=None):
        """When in view, render data in the proper monetary (and localized) format"""
        self.data = data or ''
        if self.data:
            
            try:
                float(self.data)
            except ValueError:
                # Do not continue
                return self.data
            
            parts = self.data.split('.')
            try:
                i_data = int(parts[0])
            except ValueError:
                return self.view_template(data='')
            i_data = self.intWithCommas(i_data)

            if len(parts)>1:
                dec_data = parts[1]
            else:
                dec_data = '00'
            if len(dec_data)<2:
                dec_data = dec_data + "0"
            dec_data = dec_data[0:2]

            decimal_separator = translate(_('decimal_separator',
                                             default="."),
                                          context=self.request)
            monetary_sign = translate(_('monetary_sign',
                                        default="$"),
                                      context=self.request)

            self.data = "%s %s%s%s" % (monetary_sign, i_data, decimal_separator, dec_data)
            return self.view_template(data=self.data)
        return ''
Esempio n. 41
0
def checkSearchConfig(context, event):
    """Warn user if tablepage_catalog configuration is not proper"""
    sm = getSecurityManager()
    if not sm.checkPermission(MANAGE_SEARCH_PERMISSION, context):
        return
    tp_catalog = getToolByName(context, 'tablepage_catalog')
    indexes = tp_catalog.indexes()
    warn_fields = []
    for conf in context.getSearchConfig():
        if conf['id'] not in indexes:
            warn_fields.append(conf['id'])
    if warn_fields:
        putils = getToolByName(context, 'plone_utils')
        putils.addPortalMessage(_('bad_configured_search_cols',
                                  default=u"There are columns ($cols) defined as searchable but no indexes "
                                          u"with same has been found in the tablepage_catalog tool.\n"
                                          u"Search form will use them.",
                                  mapping={'cols': ', '.join(warn_fields)}),
                                type="warning")
Esempio n. 42
0
 def get_from_request(self, name, request):
     results = []
     context = self.context
     folder = context.getAttachmentStorage() or aq_parent(aq_inner(context))
     plone_utils = getToolByName(context, 'plone_utils')
     # first of all we need also to check for existings selected files
     for existing_selection in request.get("existing_%s" % name, []):
         results.append(existing_selection)
     cnt = 0
     while True:
         if request.get("%s_%s" % (name, cnt)) and request.get("%s_%s" % (name, cnt)).filename:
             title = request.get('title_%s_%s' % (name, cnt))
             description = request.get('description_%s_%s' % (name, cnt))
             file = request.get("%s_%s" % (name, cnt))
             cnt += 1
             newId = folder.generateUniqueId(self.portal_type)
             if not title and file.filename in folder.objectIds():
                 # WARNING: we don't get the file title, to obtain the id
                 plone_utils.addPortalMessage(_('duplicate_file_error',
                                                default=u'There is already an item named ${name} in this folder.\n'
                                                        u'Loading of the attachment has been aborted.',
                                                mapping={'name': file.filename}),
                                              type='warning')
                 results.append(folder[file.filename].UID())
                 continue
             folder.invokeFactory(id=newId, type_name=self.portal_type,
                                  title=title, description=description)
             new_doc = folder[newId]
             if HAS_DEXTERITY and IDexterityContent.providedBy(new_doc):
                 setattr(new_doc, self.field_name, self.field_value_class(file, filename=unicode(file.filename)))
                 modified(new_doc)
                 if not folder.has_key(file.filename):
                     folder.manage_renameObject(new_doc.getId(), file.filename)
             else:
                 # force rename (processForm will not work with files)
                 new_doc._renameAfterCreation()
                 # this will trigger proper lifecycle events
                 new_doc.processForm()
             new_doc.edit(**{self.field_name: file})
             results.append(new_doc.UID())
         else:
             break
     return {name: '\n'.join(results)}
Esempio n. 43
0
 def validate(self, configuration, data=None):
     if 'unique' not in configuration.get('options', []):
         return None
     col_id = configuration['id']
     data = data or self.field.request.form.get(col_id)
     if data:
         context = self.field.context
         storage = IDataStorage(context)
         for i, row in enumerate(storage):
             if i != self.field.request.form.get('row-index') and row.get(
                     col_id) == data:
                 return _(
                     'error_field_unique',
                     default=
                     'The value "$value" is already present in the column \"$name\"',
                     mapping={
                         'name':
                         configuration.get('label', col_id).decode('utf-8'),
                         'value':
                         data.decode('utf-8')
                     })
Esempio n. 44
0
 def __call__(self, *args, **kwargs):
     context = self.context
     request = self.request
     catalog = getToolByName(context, config.CATALOG_ID)
     storage = IDataStorage(context)
     # now we load the tabel view and rebuild all rows by using the ignore_cache parameter
     table_view = getMultiAdapter((context, request), name=u'view-table')
     table_view.rows(ignore_cache=True)
     for index, row in enumerate(storage):
         uuid = row.get('__uuid__')
         if not uuid:
             # this should not happen
             logger.warning("Row without an uuid! index %d, document at %s" % (index, context.absolute_url_path()))
             continue
         catalog.reindex_rows(context, uuid, storage)
         if index and index % 100 == 0:
             logger.info("Refreshing catalog (%d)" % index)
             transaction.savepoint()
     logger.info("Refreshing catalog and caches: done")
     getToolByName(context, 'plone_utils').addPortalMessage(_('reindex_performed_message',
                                                              u'$count rows has been updated',
                                                              mapping={'count': index+1}))
     request.response.redirect('%s/edit-table' % context.absolute_url())
Esempio n. 45
0
 def get_from_request(self, name, request):
     results = []
     context = self.context
     folder = context.getAttachmentStorage() or aq_parent(aq_inner(context))
     plone_utils = getToolByName(context, 'plone_utils')
     # first of all we need also to check for existings selected files
     for existing_selection in request.get("existing_%s" % name, []):
         results.append(existing_selection)
     cnt = 0
     while True:
         if request.get("%s_%s" % (name, cnt)) and request.get("%s_%s" % (name, cnt)).filename:
             title = request.get('title_%s_%s' % (name, cnt))
             description = request.get('description_%s_%s' % (name, cnt))
             file = request.get("%s_%s" % (name, cnt))
             cnt += 1
             newId = folder.generateUniqueId(TYPE_TO_CREATE)
             if not title and file.filename in folder.objectIds():
                 # WARNING: we don't get the file title, to obtain the id
                 plone_utils.addPortalMessage(_('duplicate_file_error',
                                                default=u'There is already an item named ${name} in this folder.\n'
                                                        u'Loading of the attachment has been aborted.',
                                                mapping={'name': file.filename}),
                                              type='warning')
                 results.append(folder[file.filename].UID())
                 continue
             folder.invokeFactory(id=newId, type_name=TYPE_TO_CREATE,
                                  title=title, description=description)
             new_doc = folder[newId]
             # force rename (processForm will not work with files)
             new_doc._renameAfterCreation()
             # this will trigger proper lifecycle events
             new_doc.processForm()
             new_doc.edit(file=file)
             results.append(new_doc.UID())
         else:
             break
     return {name: '\n'.join(results)}
Esempio n. 46
0
    def search_fields(self):
        """Return a set of search field to be rendered"""
        context = self.context
        utilities = {}
        tableConf = {}
        
        # All registered search fields
        for name, ut in getUtilitiesFor(ISearchableColumn):
            utilities[name] = ut
        # table configuration
        for conf in context.getPageColumns():
            tableConf[conf['id']] = conf

        catalog_keys = self.get_valid_catalog_indexes()

        fields = []
        for conf in context.getSearchConfig():
            if not conf:
                continue
            field_id = conf['id']
            if field_id=='SearchableText':
                field_type = 'Text'
            else:
                field_type = tableConf[field_id]['type']
            if field_type not in utilities.keys() or field_id not in catalog_keys.keys():
                continue
            field = utilities[field_type]
            field.id = field_id
            field.configuration = tableConf.get(field_id) or {}
            field.search_configuration = conf
            field.context = context
            field.request = self.request
            field.label = conf.get('label') or field.configuration.get('label') or _(u'Search in text')
            field.description = conf.get('description') or field.configuration.get('description') or ''
            fields.append(field.render(meta_type=catalog_keys[field_id].meta_type))
        return fields
Esempio n. 47
0
 def sortOrderVocabulary(self):
     return atapi.DisplayList((
         ('asc', _("Ascending")),
         ('desc', _("Descending")),
     ))
    def __call__(self):
        # PLEASE refactorgin this mess
        request = self.request
        context = self.context
        file = request.form.get('csv')
        check_duplicate = request.form.get('look_for_duplicate')
        tp_catalog = getToolByName(context, config.CATALOG_ID)
        if file and file.filename:
            try:
                dialect = csv.Sniffer().sniff(file.read(1024), delimiters=";,")
                if not dialect.delimiter:
                    # some stupid Python 2.4 CSV bug may happens
                    raise csv.Error
            except csv.Error:
                dialect = 'excel'
            file.seek(0)
            counter = 0
            storage = IDataStorage(context)
            member = getMultiAdapter((context, request), name=u'plone_portal_state').member()
            reader = csv.reader(file, dialect)

            configuration = self.context.getPageColumns()
            valid_headers = [c['id'] for c in configuration]
            valid_retrievers = [self._getRetrieverAdapter(c['type']) for c in configuration]
            validators = [self._getRetrieveValidators(c['type']) for c in configuration]
 
            headers = []
            first = True
            putils = getToolByName(context, 'plone_utils')
            for line, row in enumerate(reader):
                logger.info("Importing line %04d" % line)
                if first:
                    headers = [h.strip() for h in row if h.strip()]
                    if configuration:
                        # CSV row is accessed by index
                        headers = [(h, headers.index(h)) for h in headers if h in valid_headers]
                    else:
                        # No configuration. Let's guess a configuration using CSV headers
                        self.context.setPageColumns([{'id' : h,
                                                      'label' : h,
                                                      'description' : '',
                                                      'type' : 'String',
                                                      'vocabulary' : '',
                                                      'options' : [],
                                                      } for h in headers])
                        headers = [(h, headers.index(h)) for h in headers]
                        configuration = self.context.getPageColumns()
                        valid_retrievers = [self._getRetrieverAdapter(c['type']) for c in configuration]
                        validators = [self._getRetrieveValidators(c['type']) for c in configuration]
                    first = False
                    continue

                tobe_saved = {}
                skip_row = False

                if len(row)<len(headers):
                    putils.addPortalMessage(_('error_row_count_dont_match',
                                              default=u"Skipping line $line. Found $lrow columns instead of $lheaders",
                                              mapping={'line': line+1,
                                                       'lrow': len(row),
                                                       'lheaders': len(headers)}),
                                            type="error")
                    continue


                for header, hindex in headers:
                    skip_cell = False
                    if request.form.get('validate'):
                        required_field_validation_failed = False
                        for vname, v in validators[hindex]:
                            msg = v.validate(configuration[hindex], data=row[hindex])
                            if msg:
                                if vname==u'required':
                                    putils.addPortalMessage(_('warn_invalid_row',
                                                              default=u"Line $line can't be imported due to missing "
                                                                      u"required data",
                                                              mapping={'line': line+1}),
                                                            type="warning")
                                    required_field_validation_failed = True
                                    break
                                putils.addPortalMessage(_('warn_invalid_cell',
                                                          default=u"Line $line, cell $cell: can't import data "
                                                                  u"due to failed validator check",
                                                          mapping={'line': line+1, 'cell': hindex}),
                                                        type="warning")
                                skip_cell = True
                                break

                        if required_field_validation_failed:
                            skip_row = True
                            break

                    # do not spend time to save data if this will be discarded
                    if not skip_row and not skip_cell:
                        try:
                            tobe_saved[header] = valid_retrievers[hindex].data_to_storage(row[hindex])
                        except NotImplementedError:
                            # column is not implementing CSV data load
                            continue

                if not skip_row and tobe_saved:
                    if check_duplicate and self._checkDuplicateRow(tobe_saved, storage):
                        putils.addPortalMessage(_('warn_duplicate',
                                                  default=u"Line ${line_number} not added because duplicated "
                                                          u"data has been found",
                                                  mapping={'line_number': line+1}),
                                                type="warning")
                        continue
                    tobe_saved['__creator__'] = member.getId()
                    tobe_saved['__uuid__'] = str(uuid.uuid4())
                    counter += 1
                    storage.add(tobe_saved)
                    tp_catalog.catalog_row(context, tobe_saved)
            msg = _('count_rows_added',
                    default=u'${count} rows added',
                    mapping={'count': counter})
            putils.addPortalMessage(msg)
            self._addNewVersion(msg)
            #return request.response.redirect('%s/edit-table' % context.absolute_url())
        return self.index()
Esempio n. 49
0
 def insertTypeVocabulary(self):
     return atapi.DisplayList(
         (('append', _("At the end")),
         ('prepend', _("At the beginning"))),
     )
Esempio n. 50
0
 def showHeadersVocabulary(self):
     return atapi.DisplayList(
         (('view_only', _("... only on page view")),
         ('edit_only', _("... only when editing table")),
         ('always', _("... always display"))),
     )
Esempio n. 51
0
    from archetypes.referencebrowserwidget import ReferenceBrowserWidget
except ImportError:
    from Products.ATReferenceBrowserWidget.ATReferenceBrowserWidget import ReferenceBrowserWidget


TablePageSchema = ATDocumentSchema.copy() + atapi.Schema(
    (
        atapi.TextField(
            "textBefore",
            required=False,
            searchable=True,
            storage=atapi.AnnotationStorage(migrate=True),
            validators=("isTidyHtmlWithCleanup",),
            default_output_type="text/x-html-safe",
            widget=atapi.RichWidget(
                label=_(u"label_text_before", default=u"Text before the table"),
                visible={"view": "invisible", "edit": "visible"},
                rows=25,
                allow_file_upload=zconf.ATDocument.allow_document_upload,
            ),
        ),
        DataGridField(
            "pageColumns",
            required=True,
            storage=atapi.AnnotationStorage(),
            columns=("id", "label", "description", "type", "vocabulary", "options"),
            widget=DataGridWidget(
                label=_(u"Columns"),
                description=_("help_pageColumns", default=u"Definition of rows inside the table"),
                visible={"view": "invisible", "edit": "visible"},
                helper_js=("datagridwidget.js", "datagridwidget_patches.js", "datagridmultiselect.js"),
Esempio n. 52
0
    def _save(self):
        """Save a new row or update one"""
        form = self.request.form
        context = self.context
        storage = self.storage
        configuration = self.configuration
        row_index = form.get('row-index', -1)
        tp_catalog = getToolByName(context, config.CATALOG_ID)
        if context.getInsertType()=='prepend':
            # this is the first row in the section, so you want to add something above it
            row_index = self._first_index_in_section(row_index)
        else:
            # this is the last row in the section, so you want to add something below it (default)
            row_index = self._last_index_in_section(row_index)
        # Run validations
        for conf in configuration:
            id = conf['id']
            col_type = conf['type']
            field = getMultiAdapter((context, self.request),
                                    IColumnField, name=col_type)

            validators = getAdapters((field, ),
                                     IFieldValidator)
            for name, validator in validators:
                msg = validator.validate(conf)
                if msg:
                    self.errors[id] = msg
                    break

        to_be_saved = {}
        if not self.errors:
            # As some IColumnDataRetriever adapter do some stuff, we read data only if no error has been get
            for conf in configuration:
                id = conf['id']
                col_type = conf['type']
                try:
                    retriever = getAdapter(context,
                                           IColumnDataRetriever,
                                           name=col_type)
                except ComponentLookupError:
                    retriever = IColumnDataRetriever(context)
                try:
                    data = retriever.get_from_request(id, form)
                except NotImplementedError:
                    data = None
                if data:
                    to_be_saved.update(**data)
        else:
            putils = getToolByName(context, 'plone_utils')
            putils.addPortalMessage(pmf(u'Please correct the indicated errors.'), type="error")
            return
        
        if to_be_saved:
            member = getMultiAdapter((context, self.request), name=u'plone_portal_state').member()
            _ = getToolByName(context, 'translation_service').utranslate
            if form.get('row-index') is not None and not form.get('addRow'):
                # Edit row
                row_index = form.get('row-index')
                if not self.check_manager_or_mine_record(row_index):
                    raise Unauthorized("You can't modify that record")
                to_be_saved['__uuid__'] = storage[row_index]['__uuid__']
                storage.update(row_index, to_be_saved)
                self._addNewVersion(_(msgid="Row changed",
                                      domain="collective.tablepage",
                                      context=context))
            else:
                # Add row
                to_be_saved['__creator__'] = member.getId()
                to_be_saved['__uuid__'] = str(uuid.uuid4())
                storage.add(to_be_saved, row_index)
                self._addNewVersion(_(msgid="Row added",
                                      domain="collective.tablepage",
                                      context=context))
            self._populateCache(storage, row_index)
            tp_catalog.catalog_row(context, storage[row_index])
Esempio n. 53
0
try:
    from archetypes.referencebrowserwidget import ReferenceBrowserWidget
except ImportError:
    from Products.ATReferenceBrowserWidget.ATReferenceBrowserWidget import ReferenceBrowserWidget


TablePageSchema = ATDocumentSchema.copy() + atapi.Schema((

    atapi.TextField('textBefore',
              required=False,
              searchable=True,
              storage=atapi.AnnotationStorage(migrate=True),
              validators=('isTidyHtmlWithCleanup',),
              default_output_type='text/x-html-safe',
              widget=atapi.RichWidget(
                        label=_(u'label_text_before', default=u'Text before the table'),
                        visible={'view': 'invisible', 'edit': 'visible'},
                        rows=25,
                        allow_file_upload=zconf.ATDocument.allow_document_upload),
    ),

    DataGridField('pageColumns',
                  required=True,
                  storage=atapi.AnnotationStorage(),
                  columns=("id", "label", "description", "type", "vocabulary", "options"),
                  widget=DataGridWidget(
                      label=_(u"Columns"),
                      description=_('help_pageColumns',
                                    default=u"Definition of rows inside the table"),
                      visible={'view': 'invisible', 'edit': 'visible'},
                      helper_js=('datagridwidget.js', 'datagridwidget_patches.js', 'datagridmultiselect.js'),
Esempio n. 54
0
 def insertTypeVocabulary(self):
     return atapi.DisplayList((("append", _("At the end")), ("prepend", _("At the beginning"))))
Esempio n. 55
0
    def _save(self):
        """Save a new row or update one"""
        form = self.request.form
        context = self.context
        storage = self.storage
        configuration = self.configuration
        row_index = form.get('row-index', -1)
        tp_catalog = getToolByName(context, config.CATALOG_ID)
        if context.getInsertType() == 'prepend':
            # this is the first row in the section, so you want to add something above it
            row_index = self._first_index_in_section(row_index)
        else:
            # this is the last row in the section, so you want to add something below it (default)
            row_index = self._last_index_in_section(row_index)
        # Run validations
        for conf in configuration:
            id = conf['id']
            col_type = conf['type']
            field = getMultiAdapter((context, self.request),
                                    IColumnField,
                                    name=col_type)

            validators = getAdapters((field, ), IFieldValidator)
            for name, validator in validators:
                msg = validator.validate(conf)
                if msg:
                    self.errors[id] = msg
                    break

        to_be_saved = {}
        if not self.errors:
            # As some IColumnDataRetriever adapter do some stuff, we read data only if no error has been get
            for conf in configuration:
                id = conf['id']
                col_type = conf['type']
                try:
                    retriever = getAdapter(context,
                                           IColumnDataRetriever,
                                           name=col_type)
                except ComponentLookupError:
                    retriever = IColumnDataRetriever(context)
                try:
                    data = retriever.get_from_request(id, form)
                except NotImplementedError:
                    data = None
                if data:
                    to_be_saved.update(**data)
        else:
            putils = getToolByName(context, 'plone_utils')
            putils.addPortalMessage(
                pmf(u'Please correct the indicated errors.'), type="error")
            return
        if to_be_saved:
            member = getMultiAdapter((context, self.request),
                                     name=u'plone_portal_state').member()
            _ = getToolByName(context, 'translation_service').utranslate
            to_be_saved['__tablerowstyle__'] = form.get('__tablerowstyle__')
            if form.get('row-index') is not None and not form.get('addRow'):
                # Edit row
                row_index = form.get('row-index')
                if not self.check_manager_or_mine_record(row_index):
                    raise Unauthorized("You can't modify that record")
                to_be_saved['__uuid__'] = storage[row_index]['__uuid__']
                storage.update(row_index, to_be_saved)
                self._addNewVersion(
                    _(msgid="Row changed",
                      domain="collective.tablepage",
                      context=context))
            else:
                # Add row
                to_be_saved['__creator__'] = member.getId()
                to_be_saved['__uuid__'] = str(uuid.uuid4())
                storage.add(to_be_saved, row_index)
                self._addNewVersion(
                    _(msgid="Row added",
                      domain="collective.tablepage",
                      context=context))
            self._populateCache(storage, row_index)
            tp_catalog.catalog_row(context, storage[row_index])