Example #1
0
 def testChangeSetFolderReordered(self):
     self.setupTestFolders()
     if safe_hasattr(aq_base(self.folder.copy_of_folder1),
                     'moveObjectsToTop'):
         self.folder.copy_of_folder1.moveObjectsToTop(['doc3'])
     elif safe_hasattr(aq_base(self.folder.copy_of_folder1),
                       'moveObjectsByDelta'):
         self.folder.copy_of_folder1.moveObjectsByDelta(['doc3'], -3)
     else:
         # We don't have an orderable folder give up
         return
     self.cs.computeDiff(self.folder.folder1, self.folder.copy_of_folder1)
     diffs = self.cs.getDiffs()
     self.assertEqual(len(diffs), 14)
     self.assertTrue(diffs[0].same)
     self.assertFalse(self.cs._added)
     self.assertFalse(self.cs._removed)
     sub_cs = self.cs.getSubDiffs()
     self.assertEqual(len(sub_cs), 3)
     # The sub diffs should show no changes
     for i in range(len(sub_cs)):
         self.assertTrue(isinstance(sub_cs[i], BaseChangeSet))
         sub_diffs = sub_cs[i].getDiffs()
         self.assertEqual(len(sub_diffs), 15)
         self.assertTrue(sub_diffs[0].same)
Example #2
0
    def suggestions(self):
        """Get suggestions from spellcheck component.
        """
        if not safe_hasattr(self, 'solr_response'):
            return []

        suggested_terms = []
        search_terms = [
            term.lower()
            for term in self.request.form.get('SearchableText', '').split()
        ]
        query_params = self.request.form.copy()

        if safe_hasattr(self.solr_response, 'spellcheck'):
            suggestions = self.solr_response.spellcheck.get('suggestions', [])
            for term in search_terms:
                if term in suggestions:
                    suggestion = suggestions[term]['suggestion']
                    query_params['SearchableText'] = suggestion[0]['word']
                    query_string = ''
                    for k, v in query_params.items():
                        if isinstance(v, list):
                            query_string += '&' + '&'.join(
                                ['%s=%s' % (k, vv) for vv in v])
                        else:
                            query_string += '&%s=%s' % (k, v)
                    suggested_terms.append(
                        (suggestion[0]['word'], query_string))
        return suggested_terms
Example #3
0
 def __init__(self,
              obj1,
              obj2,
              field,
              id1=None,
              id2=None,
              field_name=None,
              field_label=None,
              schemata=None):
     self.field = field
     self.oldValue = _getValue(obj1, field, field_name)
     self.newValue = _getValue(obj2, field, field_name)
     self.same = (self.oldValue == self.newValue)
     if not id1 and safe_hasattr(obj1, 'getId'):
         id1 = obj1.getId()
     if not id2 and safe_hasattr(obj2, 'getId'):
         id2 = obj2.getId()
     self.id1 = id1
     self.id2 = id2
     self.label = field_label or field
     self.schemata = schemata or 'default'
     fld1 = _getValue(obj1, field, field_name, convert_to_str=False)
     fld2 = _getValue(obj2, field, field_name, convert_to_str=False)
     if safe_hasattr(fld1, 'getFilename'):
         self.oldFilename = fld1.getFilename()
     else:
         self.oldFilename = None
     if safe_hasattr(fld2, 'getFilename'):
         self.newFilename = fld2.getFilename()
     else:
         self.newFilename = None
     if self.oldFilename is not None and self.newFilename is not None \
        and self.same:
         self.same = (self.oldFilename == self.newFilename)
def importVarious(context):
    """
    Final PFGDataGrid import steps.
    """

    # Only run step if a flag file is present (e.g. not an extension profile)
    if context.readDataFile('pfgdatagrid-various.txt') is None:
        return

    site = context.getSite()

    #######################
    # Both PloneFormGen and the target field provider are going to have
    # to be installed first. So, let's check.

    portal_skins = getToolByName(site, 'portal_skins')
    assert safe_hasattr(portal_skins, 'DataGridWidget'), "DataGridField must be installed prior to installing this product."
    assert safe_hasattr(portal_skins, 'PloneFormGen'), "PloneFormGen must be installed prior to installing this product."

    #######################
    # Here's the code specific to making this visible to PloneFormGen

    classes = listTypes(config.PROJECTNAME)
    myTypes = [item['name'] for item in classes]
    portal_types = getToolByName(site, 'portal_types')
    for typeName in ('FormFolder', 'FieldsetFolder'):
        ptType = portal_types.getTypeInfo(typeName)
        ffact = list(ptType.allowed_content_types)
        ffact += myTypes
        ptType.manage_changeProperties(allowed_content_types=ffact)
Example #5
0
def _getValue(ob, field, field_name, convert_to_str=True):
    # Check for the attribute without acquisition.  If it's there,
    # grab it *with* acquisition, so things like ComputedAttribute
    # will work
    if IDexterityContent.providedBy(ob) and field:
        value = getattr(ob, field, None)
    elif field and safe_hasattr(aq_base(ob), field):
        value = getattr(ob, field)
    elif safe_hasattr(aq_base(ob), 'getField'):
        # Archetypes with an adapter extended schema needs special handling
        field = ob.getField(field_name)
        if field is None:
            raise AttributeError(field)
        value = field.getAccessor(ob)
    else:
        raise AttributeError(field)

    # Handle case where the field is a method
    try:
        value = value()
    except (AttributeError, TypeError):
        pass

    if convert_to_str:
        # If this is some object, convert it to a string
        try:
            if isinstance(value, Acquisition.Implicit):
                value = str(value)
        except TypeError:
            pass

    return value
Example #6
0
def handle_enable_user_folders(obj, event):
    """Additional configuration when the ``enable_user_folders``
    setting is updated in the ``Security```control panel.

    If the setting is enabled, a new user action is added with a link to
    the personal folder. If the setting is disabled, the action is hidden.
    """
    if event.record.fieldName != 'enable_user_folders':
        return

    portal = getSite()
    value = event.newValue

    membership = getToolByName(portal, 'portal_membership')
    membership.memberareaCreationFlag = value

    # support the 'my folder' user action #8417
    portal_actions = getToolByName(portal, 'portal_actions', None)
    if portal_actions is not None:
        object_category = getattr(portal_actions, 'user', None)
        if value and not safe_hasattr(object_category, 'mystuff'):
            # add action
            _add_mystuff_action(object_category)
        elif safe_hasattr(object_category, 'mystuff'):
            a = getattr(object_category, 'mystuff')
            a.visible = bool(value)    # show/hide action
 def testChangeSetFolderReordered(self):
     self.setupTestFolders()
     if safe_hasattr(aq_base(self.folder.copy_of_folder1),
                     'moveObjectsToTop'):
         self.folder.copy_of_folder1.moveObjectsToTop(['doc3'])
     elif safe_hasattr(aq_base(self.folder.copy_of_folder1),
                       'moveObjectsByDelta'):
         self.folder.copy_of_folder1.moveObjectsByDelta(['doc3'], -3)
     else:
         # We don't have an orderable folder give up
         return
     self.cs.computeDiff(self.folder.folder1, self.folder.copy_of_folder1)
     diffs = self.cs.getDiffs()
     self.assertEqual(len(diffs), 14)
     self.assertTrue(diffs[0].same)
     self.assertFalse(self.cs._added)
     self.assertFalse(self.cs._removed)
     sub_cs = self.cs.getSubDiffs()
     self.assertEqual(len(sub_cs), 3)
     # The sub diffs should show no changes
     for i in range(len(sub_cs)):
         self.assertTrue(isinstance(sub_cs[i], BaseChangeSet))
         sub_diffs = sub_cs[i].getDiffs()
         self.assertEqual(len(sub_diffs), self.len_diff)
         self.assertTrue(sub_diffs[0].same)
Example #8
0
def get_acquired_base_object(minisite_obj, path):
    obj = minisite_obj
    path = path.split('@@')[0]
    contents = path.strip('/').split('/')
    parent_id = contents[0]
    child_id = len(contents) > 1 and contents[1] or None
    while not IPloneSiteRoot.providedBy(obj):
        parent = aq_parent(aq_inner(obj))
        if parent is None:
            break
        obj = parent
        if safe_hasattr(aq_base(obj), parent_id):
            if not child_id:
                return obj
            container = getattr(aq_base(obj), parent_id)
            if safe_hasattr(container, child_id):
                try:
                    obj_url = '/'.join(obj.getPhysicalPath())
                    full_url = "{0}{1}".format(
                        obj_url,
                        path,
                    )
                    api.content.get(full_url)
                except Unauthorized:
                    continue
                except NotFound:
                    continue
                else:
                    return obj
Example #9
0
    def populate_with_object(self, obj):
        """Tile can be populated with images and links; in this case we're not
        going to take care of any modification of the original object; we just
        copy the data to the tile and deal with it.
        """
        if obj.portal_type not in self.accepted_ct():
            return

        super(BannerTile, self).populate_with_object(obj)  # check permissions
        obj = aq_base(obj)  # avoid acquisition
        title = obj.Title()
        rights = obj.Rights() if safe_hasattr(obj, "Rights") else None

        # if image, store a copy of its data
        if obj.portal_type == "Image":
            if safe_hasattr(obj, "getImage"):
                data = obj.getImage().data
            else:
                data = obj.image.data
            image = NamedBlobImage(data)
        else:
            image = None
        image_description = obj.Description() or obj.Title()
        remote_url = obj.getRemoteUrl() if obj.portal_type == "Link" else None

        data_mgr = ITileDataManager(self)
        data_mgr.set(
            {
                "title": title,
                "image": image,
                "image_description": image_description,
                "remote_url": remote_url,
                "rights": rights,
            }
        )
Example #10
0
 def __init__(self, obj1, obj2, field, id1=None, id2=None,
              field_name=None, field_label=None, schemata=None):
     self.field = field
     self.oldValue = _getValue(obj1, field, field_name)
     self.newValue = _getValue(obj2, field, field_name)
     self.same = (self.oldValue == self.newValue)
     if not id1 and safe_hasattr(obj1, 'getId'):
         id1 = obj1.getId()
     if not id2 and safe_hasattr(obj2, 'getId'):
         id2 = obj2.getId()
     self.id1 = id1
     self.id2 = id2
     self.label = field_label or field
     self.schemata = schemata or 'default'
     fld1 = _getValue(obj1, field, field_name, convert_to_str=False)
     fld2 = _getValue(obj2, field, field_name, convert_to_str=False)
     if safe_hasattr(fld1, 'getFilename'):
         self.oldFilename = fld1.getFilename()
     else:
         self.oldFilename = None
     if safe_hasattr(fld2, 'getFilename'):
         self.newFilename = fld2.getFilename()
     else:
         self.newFilename = None
     if self.oldFilename is not None and self.newFilename is not None \
        and self.same:
         self.same = (self.oldFilename == self.newFilename)
Example #11
0
    def suggestions(self):
        """Get suggestions from spellcheck component.
        """
        if not safe_hasattr(self, 'solr_response'):
            return []

        suggested_terms = []
        search_terms = [term.lower() for term in self.request.form.get(
            'SearchableText', '').split()]
        query_params = self.request.form.copy()

        if safe_hasattr(self.solr_response, 'spellcheck'):
            suggestions = self.solr_response.spellcheck.get('suggestions', [])
            for term in search_terms:

                if not isinstance(term, unicode):
                    term = term.decode('utf-8')

                if term in suggestions:
                    suggestion = suggestions[term]['suggestion']
                    query_params['SearchableText'] = suggestion[0]['word']
                    query_string = ''
                    for k, v in query_params.items():
                        if isinstance(v, list):
                            query_string += '&' + '&'.join(
                                ['%s=%s' % (k, vv) for vv in v])
                        else:
                            query_string += '&%s=%s' % (k, v)
                    suggested_terms.append((suggestion[0]['word'],
                                            query_string))
        return suggested_terms
    def fgPrimeDefaults(self, request, contextObject=None):
        """ primes request with default """
        value = None

        # try and look up the current state
        formFolder = self.formFolderObject()
        if formFolder.getAllowEditPrevious():
            value = formFolder.getExistingValue(self.aq_base)

        # the field macros will try to get the field value
        # via Field.getEditAccessor. Unfortunately, it looks for it
        # as an attribute of the object, not the field.
        # so, communicate via the request, but don't overwrite
        # what's already there.
        if value is None:
            if safe_hasattr(self, 'getFgTDefault') and self.getRawFgTDefault():
                if contextObject:
                    # see note in fgvalidate
                    value = self.getFgTDefault(expression_context=getExprContext(self, contextObject))
                else:
                    value = self.getFgTDefault()

        if (value is None) and safe_hasattr(self, 'getFgDefault'):
            value = self.getFgDefault()
        if value:
            request.form.setdefault(self.fgField.__name__, value)
Example #13
0
def _getValue(ob, field, field_name, convert_to_str=True):
    # Check for the attribute without acquisition.  If it's there,
    # grab it *with* acquisition, so things like ComputedAttribute
    # will work
    if IDexterityContent.providedBy(ob) and field:
        value = getattr(ob, field, None)
    elif field and safe_hasattr(aq_base(ob), field):
        value = getattr(ob, field)
    elif safe_hasattr(aq_base(ob), 'getField'):
        # Archetypes with an adapter extended schema needs special handling
        field = ob.getField(field_name)
        if field is None:
            raise AttributeError(field)
        value = field.getAccessor(ob)
    else:
        raise AttributeError(field)

    # Handle case where the field is a method
    try:
        value = value()
    except (AttributeError, TypeError):
        pass

    if convert_to_str:
        # If this is some object, convert it to a string
        try:
            if isinstance(value, Acquisition.Implicit):
                value = str(value)
        except TypeError:
            pass

    return value
Example #14
0
def handle_enable_user_folders(obj, event):
    """Additional configuration when the ``enable_user_folders``
    setting is updated in the ``Security```control panel.

    If the setting is enabled, a new user action is added with a link to
    the personal folder. If the setting is disabled, the action is hidden.
    """
    if event.record.fieldName != 'enable_user_folders':
        return

    portal = getSite()
    value = event.newValue

    membership = getToolByName(portal, 'portal_membership')
    membership.memberareaCreationFlag = value

    # support the 'my folder' user action #8417
    portal_actions = getToolByName(portal, 'portal_actions', None)
    if portal_actions is not None:
        object_category = getattr(portal_actions, 'user', None)
        if value and not safe_hasattr(object_category, 'mystuff'):
            # add action
            _add_mystuff_action(object_category)
        elif safe_hasattr(object_category, 'mystuff'):
            a = getattr(object_category, 'mystuff')
            a.visible = bool(value)  # show/hide action
def importVarious(context):
    """
    Final PFGDataGrid import steps.
    """

    # Only run step if a flag file is present (e.g. not an extension profile)
    if context.readDataFile('pfgdatagrid-various.txt') is None:
        return

    site = context.getSite()

    #######################
    # Both PloneFormGen and the target field provider are going to have
    # to be installed first. So, let's check.

    portal_skins = getToolByName(site, 'portal_skins')
    assert safe_hasattr(
        portal_skins, 'DataGridWidget'
    ), "DataGridField must be installed prior to installing this product."
    assert safe_hasattr(
        portal_skins, 'PloneFormGen'
    ), "PloneFormGen must be installed prior to installing this product."

    #######################
    # Here's the code specific to making this visible to PloneFormGen

    classes = listTypes(config.PROJECTNAME)
    myTypes = [item['name'] for item in classes]
    portal_types = getToolByName(site, 'portal_types')
    for typeName in ('FormFolder', 'FieldsetFolder'):
        ptType = portal_types.getTypeInfo(typeName)
        ffact = list(ptType.allowed_content_types)
        ffact += myTypes
        ptType.manage_changeProperties(allowed_content_types=ffact)
Example #16
0
def cleanup_stored_refs(obj):
    """Cleanup new dx item."""
    if safe_hasattr(obj, '_relatedItems'):
        del obj._relatedItems
    if safe_hasattr(obj, '_backrefs'):
        del obj._backrefs
    if safe_hasattr(obj, '_relatedItemsOrder'):
        del obj._relatedItemsOrder
Example #17
0
 def set_smtp_userid(self, value):
     if safe_hasattr(self.mail_settings, 'smtp_userid'):
         self.mail_settings.smtp_userid = value
         # SecureMailhost 1.x also uses this:
         if safe_hasattr(self.mail_settings, '_smtp_userid'):
             self.mail_settings._smtp_userid = value
     elif safe_hasattr(self.mail_settings, 'smtp_userid'):
         self.mail_settings.smtp_uid = value
Example #18
0
def cleanup_stored_refs(obj):
    """Cleanup new dx item."""
    if safe_hasattr(obj, '_relatedItems'):
        del obj._relatedItems
    if safe_hasattr(obj, '_backrefs'):
        del obj._backrefs
    if safe_hasattr(obj, '_relatedItemsOrder'):
        del obj._relatedItemsOrder
Example #19
0
 def set_smtp_userid(self, value):
     if safe_hasattr(self.context, 'smtp_userid'):
         self.context.smtp_userid = value
         #SecureMailhost 1.x also uses this:
         if safe_hasattr(self.context, '_smtp_userid'):
             self.context._smtp_userid = value
     elif safe_hasattr(self.context, 'smtp_uid'):
         self.context.smtp_uid = value
Example #20
0
 def set_smtp_userid(self, value):
     if safe_hasattr(self.mail_settings, 'smtp_userid'):
         self.mail_settings.smtp_userid = value
         # SecureMailhost 1.x also uses this:
         if safe_hasattr(self.mail_settings, '_smtp_userid'):
             self.mail_settings._smtp_userid = value
     elif safe_hasattr(self.mail_settings, 'smtp_userid'):
         self.mail_settings.smtp_uid = value
Example #21
0
 def set_smtp_userid(self, value):
     if safe_hasattr(self.context, 'smtp_userid'):
         self.context.smtp_userid = value
         #SecureMailhost 1.x also uses this:
         if safe_hasattr(self.context, '_smtp_userid'):
             self.context._smtp_userid = value
     elif safe_hasattr(self.context, 'smtp_uid'):
         self.context.smtp_uid = value
Example #22
0
 def convert_to_object(self, obj):
     """
     Converts object if object has getObject() attribute.
     """
     if safe_hasattr(obj, 'getObject'):
         obj = obj.getObject()
     elif safe_hasattr(obj, 'object'):
         obj = obj.object
     return obj
Example #23
0
 def convert_to_object(self, obj):
     """
     Converts object if object has getObject() attribute.
     """
     if safe_hasattr(obj, 'getObject'):
         obj = obj.getObject()
     elif safe_hasattr(obj, 'object'):
         obj = obj.object
     return obj
Example #24
0
 def set_smtp_pass(self, value):
     # Don't update the value, if we don't get a new one
     if value is not None:
         if safe_hasattr(self.mail_settings, 'smtp_pass'):
             self.mail_settings.smtp_pass = value
             # SecureMailhost 1.x also uses this:
             if safe_hasattr(self.mail_settings, '_smtp_pass'):
                 self.mail_settings._smtp_pass = value
         elif safe_hasattr(self.mail_settings, 'smtp_pwd'):
             self.mail_settings.smtp_pwd = value
Example #25
0
 def set_smtp_pass(self, value):
     # Don't update the value, if we don't get a new one
     if value is not None:
         if safe_hasattr(self.mail_settings, 'smtp_pass'):
             self.mail_settings.smtp_pass = value
             # SecureMailhost 1.x also uses this:
             if safe_hasattr(self.mail_settings, '_smtp_pass'):
                 self.mail_settings._smtp_pass = value
         elif safe_hasattr(self.mail_settings, 'smtp_pwd'):
             self.mail_settings.smtp_pwd = value
Example #26
0
    def _has_image_field(self, obj):
        """Return True if the object has an image field.

        :param obj: [required]
        :type obj: content object
        """
        if safe_hasattr(obj, 'image'):  # Dexterity
            return True
        elif safe_hasattr(obj, 'Schema'):  # Archetypes
            return 'image' in obj.Schema().keys()
        else:
            return False
Example #27
0
    def _has_image_field(self, obj):
        """Return True if the object has an image field.

        :param obj: [required]
        :type obj: content object
        """
        if safe_hasattr(obj, 'image'):  # Dexterity
            return True
        elif safe_hasattr(obj, 'Schema'):  # Archetypes
            return 'image' in obj.Schema().keys()
        else:
            return False
Example #28
0
 def set_enable_user_folders(self, value):
     self.pmembership.memberareaCreationFlag = value
     # support the 'my folder' user action #8417
     portal_actions = getToolByName(self.portal, 'portal_actions', None)
     if portal_actions is not None:
         object_category = getattr(portal_actions, 'user', None)
         if value and not safe_hasattr(object_category, 'mystuff'):
             # add action
             self.add_mystuff_action(object_category)
         elif safe_hasattr(object_category, 'mystuff'):
             a = getattr(object_category, 'mystuff')
             a.visible = bool(value)    # show/hide action
 def set_enable_user_folders(self, value):
     self.pmembership.memberareaCreationFlag = value
     # support the 'my folder' user action #8417
     portal_actions = getToolByName(self.portal, 'portal_actions', None)
     if portal_actions is not None:
         object_category = getattr(portal_actions, 'user', None)
         if value and not safe_hasattr(object_category, 'mystuff'):
             # add action
             self.add_mystuff_action(object_category)
         elif safe_hasattr(object_category, 'mystuff'):
             a = getattr(object_category, 'mystuff')
             a.visible = bool(value)  # show/hide action
Example #30
0
 def snippets(self):
     """Return snippets with highlighted search terms in a dict with the
        item's UID as key and the snippet text as value.
     """
     if not safe_hasattr(self, 'solr_response'):
         return {}
     if safe_hasattr(self.solr_response, 'highlighting'):
         joined_snippets = {}
         for uid, snippets in self.solr_response.highlighting.items():
             joined_snippets[uid] = ' '.join([' '.join(snippet) for snippet
                                              in snippets.values()]).strip()
         return joined_snippets
     return {}
Example #31
0
def migrate_users(context, mapping, mode='move', replace=False):
    """Migrate Plone users."""

    # Statistics
    moved = []
    copied = []
    deleted = []

    uf = getToolByName(context, 'acl_users')

    for old_userid, new_userid in mapping.items():
        users = uf.searchUsers(id=old_userid)
        if len(users) > 0:
            for user in users:
                plugin = uf.get(user['pluginid'])
                # Only ZODB User Manager is supported
                if (safe_hasattr(plugin, '_user_passwords') and
                        safe_hasattr(plugin, '_login_to_userid') and
                        safe_hasattr(plugin, '_userid_to_login')):
                    pw = plugin._user_passwords[old_userid]
                    login = plugin._userid_to_login[old_userid]

                    # Do nothing if a user with new_userid already exists and
                    # replace is False.
                    if new_userid in plugin._user_passwords and not replace:
                        continue

                    if mode in ['move', 'delete']:
                        del plugin._user_passwords[old_userid]
                        del plugin._userid_to_login[old_userid]
                        del plugin._login_to_userid[login]

                    if mode in ['copy', 'move']:
                        # If userid and login are the same or if in copy mode,
                        # set login to new userid.
                        if login == old_userid or mode == 'copy':
                            login = new_userid

                        plugin._user_passwords[new_userid] = pw
                        plugin._login_to_userid[login] = new_userid
                        plugin._userid_to_login[new_userid] = login

                    if mode == 'move':
                        moved.append(('acl_users', old_userid, new_userid))
                    if mode == 'copy':
                        copied.append(('acl_users', old_userid, new_userid))
                    if mode == 'delete':
                        deleted.append(('acl_users', old_userid, None))

    return(dict(moved=moved, copied=copied, deleted=deleted))
Example #32
0
 def snippets(self):
     """Return snippets with highlighted search terms in a dict with the
        item's UID as key and the snippet text as value.
     """
     if not safe_hasattr(self, 'solr_response'):
         return {}
     if safe_hasattr(self.solr_response, 'highlighting'):
         joined_snippets = {}
         for uid, snippets in self.solr_response.highlighting.items():
             joined_snippets[uid] = ' '.join([
                 ' '.join(snippet) for snippet in snippets.values()
             ]).strip()
         return joined_snippets
     return {}
Example #33
0
    def htmlValue(self, REQUEST):
        """ return from REQUEST, this field's value, rendered as XHTML.
            In this case, a definition list.
        """

        value = REQUEST.form.get(self.__name__, "No Input")
        if not (safe_hasattr(value, "get") and safe_hasattr(value, "len") and len(value)):
            return fieldsBase.BaseFormField.htmlValue(self, REQUEST)

        res = "<dl>\n"
        for i in range(len(value)):
            label = self.fgField.questionSet[i]
            response = value.get(str(i + 1), "")
            res = "%s<dt>%s</dt><dd>%s</dd>\n" % (res, cgi.escape(label), cgi.escape(response))
        return "%s</dl>\n" % res
Example #34
0
    def stringify(self, value):
        """Convert value to string

        This method is used to generate a simple JSON representation of the
        object (without dereferencing objects etc.)
        """
        # SuperModel -> UID
        if ISuperModel.providedBy(value):
            return str(value)
        # DateTime -> ISO8601 format
        elif isinstance(value, (DateTime)):
            return value.ISO8601()
        # Image/Files -> filename
        elif safe_hasattr(value, "filename"):
            return value.filename
        # Dict -> convert_value_to_string
        elif isinstance(value, dict):
            return {k: self.stringify(v) for k, v in value.iteritems()}
        # List -> convert_value_to_string
        if isinstance(value, (list, tuple, LazyMap)):
            return map(self.stringify, value)
        # Callables
        elif safe_callable(value):
            return self.stringify(value())
        elif isinstance(value, unicode):
            value = value.encode("utf8")
        try:
            return str(value)
        except (AttributeError, TypeError, ValueError):
            logger.warn("Could not convert {} to string".format(repr(value)))
            return None
def scale(instance, data, w, h, default_format='PNG'):
    """ scale image"""
    size = int(w), int(h)
    original_file = StringIO(data)
    image = PIL.Image.open(original_file)
    format = image.format
    #does not work for sizes='inscanteMethod' since we don't have an instance here
    availableSizes = instance.getAvailableSizes(None)

    if safe_hasattr(instance, 'crop_scales'):
        #if our field defines crop_scales let's see if the current sizes shall be cropped
        if size in [availableSizes[name] for name in instance.crop_scales]:
            image = crop(image, size)

    original_mode = image.mode
    if original_mode == '1':
        image = image.convert('L')
    elif original_mode == 'P':
        image = image.convert('RGBA')
    image.thumbnail(size, instance.pil_resize_algo)
    format = format or default_format
    if original_mode == 'P' and format == 'GIF':
        image = image.convert('P')
    thumbnail_file = StringIO()
    image.save(thumbnail_file, format, quality=instance.pil_quality)
    thumbnail_file.seek(0)
    return thumbnail_file, format.lower()
Example #36
0
    def get_image_data(self, obj):
        """Get image data from the object used to populate the tile.

        :param obj: object used to populate the tile
        :type obj: content type instance
        :returns: image
        :rtype: NamedBlobImage instance or None
        """
        image = None
        scale = self.scale
        # if has image, store a copy of its data
        if self._has_image_field(obj) and self._field_is_visible('image'):
            scales = obj.restrictedTraverse('@@images')
            image = scales.scale('image', scale)

        if image is not None and image != '':
            if isinstance(image.data, NamedBlobImage):
                # Dexterity
                image = image.data
            else:
                # Archetypes
                data = image.data
                if safe_hasattr(data, 'data'):  # image data weirdness...
                    data = data.data
                image = NamedBlobImage(data)
        return image
    def __call__(self, value, *args, **kwargs):

        #import pdb; pdb.set_trace()

        field = kwargs.get('field', None)
        widget = getattr(field, 'widget', None)

        # get maxlength
        if kwargs.has_key('maxlength'):
            maxlength = kwargs.get('maxlength')
        elif safe_hasattr(widget, 'maxlength'):
            maxlength = int(widget.maxlength or 0)
        else:
            # set to given default value (default defaults to 0)
            maxlength = self.maxlength

        if maxlength == 0:
            return 1

        nval = len(value)

        if nval <= maxlength:
            return 1
        else:
            return ("Validation failed(%(name)s): '%(value)s' is too long. Must be no longer than %(max)s characters." %
                    { 'name' : self.name, 'value': getattr(widget, 'label', 'Entry'), 'max' : maxlength,})
Example #38
0
    def create_or_modify_content(self, tus_upload):
        metadata = tus_upload.metadata()
        filename = metadata.get("filename", "")
        content_type = metadata.get("content-type", "application/octet-stream")
        mode = metadata.get("mode", "create")
        fieldname = metadata.get("fieldname")

        if mode == "create":
            type_ = metadata.get("@type")
            if type_ is None:
                ctr = getToolByName(self.context, "content_type_registry")
                type_ = ctr.findTypeName(filename.lower(), content_type,
                                         "") or "File"

            obj = create(self.context, type_)
        else:
            obj = self.context

        if not fieldname:
            info = IPrimaryFieldInfo(obj, None)
            if info is not None:
                fieldname = info.fieldname
            elif base_hasattr(obj, "getPrimaryField"):
                field = obj.getPrimaryField()
                fieldname = field.getName()

        if not fieldname:
            return self.error("Bad Request", "Fieldname required", 400)

        # Acquisition wrap temporarily for deserialization
        temporarily_wrapped = False
        if IAcquirer.providedBy(obj) and not safe_hasattr(obj, "aq_base"):
            obj = obj.__of__(self.context)
            temporarily_wrapped = True

        # Update field with file data
        deserializer = queryMultiAdapter((obj, self.request),
                                         IDeserializeFromJson)
        if deserializer is None:
            return self.error(
                "Not Implemented",
                f"Cannot deserialize type {obj.portal_type}",
                501,
            )
        try:
            deserializer(data={fieldname: tus_upload}, create=mode == "create")
        except DeserializationError as e:
            return self.error("Deserialization Error", str(e), 400)

        if temporarily_wrapped:
            obj = aq_base(obj)

        if mode == "create":
            if not getattr(deserializer, "notifies_create", False):
                notify(ObjectCreatedEvent(obj))
            obj = add(self.context, obj)

        tus_upload.close()
        tus_upload.cleanup()
        self.request.response.setHeader("Location", obj.absolute_url())
Example #39
0
def migrate_imagefield(src_obj, dst_obj, src_fieldname, dst_fieldname):
    """
    migrate an image field.
    This field needs to be migrated with an NamedBlobImage instance.
    """
    # get old image data and filename
    old_image = src_obj.getField(src_fieldname).get(src_obj)
    if old_image == '':
        return
    filename = safe_unicode(old_image.filename)
    old_image_data = old_image.data
    if safe_hasattr(old_image_data, 'data'):
        old_image_data = old_image_data.data

    # create the new image field
    namedblobimage = NamedBlobImage(data=old_image_data, filename=filename)

    # set new field on destination object
    setattr(dst_obj, dst_fieldname, namedblobimage)

    # handle a possible image caption field
    # postulate is the old caption field name is ending by 'Caption'
    # and the new field name is ending by '_caption'
    # is this postulate correct ?
    # should this field not be handle by itself because it will appear in the
    # old field list ?
    caption_field = src_obj.getField('%sCaption' % src_fieldname, None)
    if caption_field:
        setattr(dst_obj, ('%s_caption' % dst_fieldname),
                safe_unicode(caption_field.get(src_obj)))

    logger.info("Migrating image %s" % filename)
Example #40
0
    def _enabled_for_dexterity_types(self):
        """ Returns True if discussion is enabled for this conversation.

        This method checks five different settings in order to figure out if
        discussion is enable on a specific content object:

        1) Check if discussion is enabled globally in the plone.app.discussion
           registry/control panel.

        2) Check if the allow_discussion boolean flag on the content object is
           set. If it is set to True or False, return the value. If it set to
           None, try further.

        3) Check if discussion is allowed for the content type.
        """
        context = aq_inner(self.context)

        # Fetch discussion registry
        registry = queryUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)

        # Check if discussion is allowed globally
        if not settings.globally_enabled:
            return False

        # Check if discussion is allowed on the content object
        if safe_hasattr(context, 'allow_discussion'):
            if context.allow_discussion is not None:
                return context.allow_discussion

        # Check if discussion is allowed on the content type
        portal_types = getToolByName(self, 'portal_types')
        document_fti = getattr(portal_types, context.portal_type)
        return document_fti.getProperty('allow_discussion')
    def __call__(self, value, *args, **kwargs):

        #import pdb; pdb.set_trace()

        field = kwargs.get('field', None)
        widget = getattr(field, 'widget', None)

        # get maxlength
        if kwargs.has_key('maxlength'):
            maxlength = kwargs.get('maxlength')
        elif safe_hasattr(widget, 'maxlength'):
            maxlength = int(widget.maxlength or 0)
        else:
            # set to given default value (default defaults to 0)
            maxlength = self.maxlength

        if maxlength == 0:
            return 1

        # The char count must match the javascript char count. To do so, 
        # we have to replace the newlines and use the unicode value of the 
        # string to get an accurate count when special chars are present.
        value = value.replace('\r\n', '\n')
        try:
            nval = len(value.decode('utf-8'))
        except UnicodeDecodeError:
            nval = len(value)

        if nval <= maxlength:
            return 1
        else:
            return ("Validation failed(%(name)s): '%(value)s' is too long. Must be no longer than %(max)s characters." %
                    { 'name' : self.name, 'value': getattr(widget, 'label', 'Entry'), 'max' : maxlength,})
Example #42
0
def migrate_blobimagefield(src_obj, dst_obj, src_fieldname, dst_fieldname):
    """
    migrate an image field.
    Actually this field needs only to copy the existing NamedBlobImage instance
    to the new dst_obj, but we do some more in detail and create new fields.
    """
    old_image = getattr(src_obj, src_fieldname)
    if old_image == '':
        return
    filename = safe_unicode(old_image.filename)
    old_image_data = old_image.data
    if safe_hasattr(old_image_data, 'data'):
        old_image_data = old_image_data.data
    namedblobimage = NamedBlobImage(data=old_image_data,
                                    filename=filename)

    # set new field on destination object
    setattr(dst_obj, dst_fieldname, namedblobimage)

    # handle a possible image caption field
    field = '{0}_caption'.format(src_fieldname)
    old_image_caption = getattr(src_obj, field, None)
    if old_image_caption:
        setattr(dst_obj,
                ('{0}_caption'.format(dst_fieldname)),
                safe_unicode(old_image_caption))

    logger.info('Migrating image {0}'.format(filename))
def scale(instance, data, w, h, default_format='PNG'):
    """ scale image"""
    size = int(w), int(h)
    original_file = StringIO(data)
    image = PIL.Image.open(original_file)
    format = image.format
    #does not work for sizes='inscanteMethod' since we don't have an instance here
    availableSizes = instance.getAvailableSizes(None)

    if safe_hasattr(instance, 'crop_scales'):
        #if our field defines crop_scales let's see if the current sizes shall be cropped
        if size in [availableSizes[name] for name in instance.crop_scales]:
            image = crop(image, size)

    original_mode = image.mode
    if original_mode == '1':
        image = image.convert('L')
    elif original_mode == 'P':
        image = image.convert('RGBA')
    image.thumbnail(size, instance.pil_resize_algo)
    format = format or default_format
    if original_mode == 'P' and format == 'GIF':
        image = image.convert('P')
    thumbnail_file = StringIO()
    image.save(thumbnail_file, format, quality=instance.pil_quality)
    thumbnail_file.seek(0)
    return thumbnail_file, format.lower()
Example #44
0
    def computeDiff(self, ob1, ob2, id1=None, id2=None):
        """Compute the differences between two objects and return the
        results as a list.  Each object in the list will implement the
        IDifference interface"""

        # Try to get the portal type from obj1 first.  If that fails, use obj2
        pt_name = ''
        try:
            pt_name = aq_base(ob1).portal_type
        except AttributeError:
            try:
                pt_name = aq_base(ob2).portal_type
            except AttributeError:
                pass

        diff_map = self._pt_diffs.get(pt_name, {})

        diffs = []
        for field, klass_name in diff_map.items():
            klass = self._difftypes[klass_name]
            f_diff = klass(ob1, ob2, field, id1=id1, id2=id2)
            # handle compound diff types
            if safe_hasattr(f_diff, '__getitem__'):
                diffs.extend(f_diff)
            else:
                diffs.append(f_diff)
        return diffs
    def __call__(self, value, *args, **kwargs):

        field = kwargs.get("field", None)
        widget = getattr(field, "widget", None)

        # get maxlength
        if "maxlength" in kwargs:
            maxlength = kwargs.get("maxlength")
        elif safe_hasattr(widget, "maxlength"):
            maxlength = int(widget.maxlength or 0)
        else:
            # set to given default value (default defaults to 0)
            maxlength = self.maxlength

        if maxlength == 0:
            return 1
        decoded = value.replace("\r\n", "\n").decode("utf8")
        nval = len(decoded)

        if nval <= maxlength:
            return 1
        else:
            return (
                "Validation failed(%(name)s): '%(value)s' is too long. Must be no longer than %(max)s characters."
                % {"name": self.name, "value": getattr(widget, "label", "Entry"), "max": maxlength}
            )
Example #46
0
def set_vcge(obj, skos):
    """ Armazena valores no atributo
        VCGE de um objeto
    """
    if safe_hasattr(aq_base(obj), 'skos'):
        obj.skos = skos
    return True
def SearchableText_AgendaDiaria(obj):
    """ Indexa os dados dos compromissos dentro desta AgendaDiaria
        para prover busca por texto integral
    """
    children = obj.objectValues()
    SearchableText = []
    for child in children:
        if not ICompromisso.providedBy(child):
            continue
        # Campos indexaveis
        SearchableText.append(child.title)
        SearchableText.append(child.description)
        SearchableText.append(child.autoridade)
        SearchableText.append(child.location)
        SearchableText.append(child.attendees)
    if not SearchableText:
        SearchableText.append(obj.autoridade)
        SearchableText.append(obj.location)
    # Alteracao da agenda
    update = obj.update
    if safe_hasattr(update, 'output'):
        update = update.output
    SearchableText.append(update)
    return ' '.join(
        [text for text in SearchableText if isinstance(text, basestring)])
Example #48
0
    def __call__(self, value, *args, **kwargs):

        field = kwargs.get('field', None)
        widget = getattr(field, 'widget', None)

        # get maxlength
        if 'maxlength' in kwargs:
            maxlength = kwargs.get('maxlength')
        elif safe_hasattr(widget, 'maxlength'):
            maxlength = int(widget.maxlength or 0)
        else:
            # set to given default value (default defaults to 0)
            maxlength = self.maxlength

        if maxlength == 0:
            return 1
        decoded = value.replace('\r\n', '\n').decode('utf-8')
        nval = len(decoded)

        if nval <= maxlength:
            return 1
        else:
            return (
                "Validation failed(%(name)s): '%(value)s' is too long. Must be no longer than %(max)s characters."
                % {
                    'name': self.name,
                    'value': getattr(widget, 'label', 'Entry'),
                    'max': maxlength
                })
    def _enabled_for_dexterity_types(self):
        """ Returns True if discussion is enabled for this conversation.

        This method checks five different settings in order to figure out if
        discussion is enable on a specific content object:

        1) Check if discussion is enabled globally in the plone.app.discussion
           registry/control panel.

        2) Check if the allow_discussion boolean flag on the content object is
           set. If it is set to True or False, return the value. If it set to
           None, try further.

        3) Check if discussion is allowed for the content type.
        """
        context = aq_inner(self.context)

        # Fetch discussion registry
        registry = queryUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)

        # Check if discussion is allowed globally
        if not settings.globally_enabled:
            return False

        # Check if discussion is allowed on the content object
        if safe_hasattr(context, 'allow_discussion'):
            if context.allow_discussion is not None:
                return context.allow_discussion

        # Check if discussion is allowed on the content type
        portal_types = getToolByName(self, 'portal_types')
        document_fti = getattr(portal_types, context.portal_type)
        return document_fti.getProperty('allow_discussion')
Example #50
0
    def __call__(self, value, *args, **kwargs):

        #import pdb; pdb.set_trace()

        field = kwargs.get('field', None)
        widget = getattr(field, 'widget', None)

        # get maxlength
        if kwargs.has_key('maxlength'):
            maxlength = kwargs.get('maxlength')
        elif safe_hasattr(widget, 'maxlength'):
            maxlength = int(widget.maxlength or 0)
        else:
            # set to given default value (default defaults to 0)
            maxlength = self.maxlength

        if maxlength == 0:
            return 1

        nval = len(value)

        if nval <= maxlength:
            return 1
        else:
            return (
                "Validation failed(%(name)s): '%(value)s' is too long. Must be no longer than %(max)s characters."
                % {
                    'name': self.name,
                    'value': getattr(widget, 'label', 'Entry'),
                    'max': maxlength,
                })
Example #51
0
    def get_image_data(self, obj):
        """Get image data from the object used to populate the tile.

        :param obj: object used to populate the tile
        :type obj: content type instance
        :returns: image
        :rtype: NamedBlobImage instance or None
        """
        image = None
        scale = self.scale
        # if has image, store a copy of its data
        if self._has_image_field(obj) and self._field_is_visible('image'):
            scales = obj.restrictedTraverse('@@images')
            image = scales.scale('image', scale)

        if image is not None and image != '':
            if isinstance(image.data, NamedBlobImage):
                # Dexterity
                image = image.data
            else:
                # Archetypes
                data = image.data
                if safe_hasattr(data, 'data'):  # image data weirdness...
                    data = data.data
                image = NamedBlobImage(data)
        return image
Example #52
0
def mark_file(ob, event):
    if safe_hasattr(ob, 'getFile'):
        obfile = ob.getFile()
        reindex = False

        set_track_view = False
        for name, adapter in getAdapters((obfile,), ITypeRecognition):
            iface = adapter.getIface()
            if adapter.isOfType():
                if not iface.providedBy(ob):
                    alsoProvides(ob, iface)
                    reindex = True
                # if there is set an interface, I also want to switch the view
                set_track_view = True
                # ok, found the type. do not check any more.
                break
            else:
                if iface.providedBy(ob):
                    # remove the marker if the file type changed!
                    noLongerProvides(ob, iface)
                    reindex = True

        if set_track_view:
            # if at least one interface is set, switch the view.
            ob.setLayout('track_view')
        else:
            # reset the view
            ob.setLayout(ob.getDefaultLayout())
        # we need to reindex the object, because ObjectEditedEvent is fired
        # after reindexing
        if reindex:
            # TODO: just reindex object_provides?
            ob.reindexObject()
 def setMemberProperties(self, member, REQUEST=None, **properties):
     # Set the member properties.  When changing the e-mail
     # address, also update the login name.  And make the e-mail
     # address lowercase.
     pas = getToolByName(self, 'acl_users')
     if safe_hasattr(member, 'getId'):
         member_id = member.getId()
     else:
         member_id = member
     user = pas.getUserById(member_id)
     update_login_name = False
     if 'email' in properties:
         new_email = properties.get('email')
         if new_email != new_email.lower():
             new_email = new_email.lower()
             properties['email'] = new_email
             if REQUEST is not None and 'email' in REQUEST:
                 REQUEST['email'] = new_email
         old_email = user.getProperty('email')
         if new_email != old_email:
             update_login_name = True
     user.setProperties(**properties)
     if update_login_name:
         logger.info("Updating login name from %s to %s", old_email,
                     new_email)
         userfolder = pas.source_users
         try:
             userfolder.updateUser(member_id, new_email)
         except KeyError:
             raise ValueError('you are not a Plone member (you are '
                              'probably registered on the root user '
                              'folder, please notify an administrator if '
                              'this is unexpected)')
Example #54
0
    def getContextInfo(self, context=None):
        if context is None:
            context = self.context
        context = aq_inner(context)

        info = ''
        context_name = context.Title()
        context_url = context.absolute_url()
        context_type = context.Type()
        del_url = context_url

        # TODO: we should check errors in the delete process, and
        # broadcast those to the error template in JS
        info = {'name': context_name,
                'title': context_name,
                'description': context.Description(),
                'rights': context.Rights(),
                'url': context_url,
                'delete_url': del_url,
                'delete_type': 'DELETE',
                }
        if safe_hasattr(context, 'size'):
            info['size'] = context.size()
        else:
            if context_type == 'File':
                info['size'] = context.file.getSize()
            elif context_type == 'Image':
                info['size'] = context.image.getSize()
        if context_type == 'Image':
            scales = context.restrictedTraverse('@@images')
            thumb = scales.scale(fieldname='image', scale='thumb')
            info['thumbnail_url'] = thumb.url
        return info
Example #55
0
    def __new__(cls, context):
        instance = super(NameFromFileName, cls).__new__(cls)

        if safe_hasattr(context, 'title') and not context.title:

            file_field = None
            for i in iterSchemata(context):
                fields = getFieldsInOrder(i)
                for name, field in fields:
                    if isinstance(field, NamedBlobFile):
                        file_field = field
                        break
            if file_field is None:
                return None
            filename = getattr(file_field.get(context), 'filename', None)
            if not isinstance(filename, basestring) or not filename:
                return None

            title = filename
            context.title = title

        else:
            instance = super(NameFromFileName, cls).__new__(cls)
            title = context.title

        instance.title = title
        return instance
Example #56
0
    def computeDiff(self, ob1, ob2, id1=None, id2=None):
        """Compute the differences between two objects and return the
        results as a list.  Each object in the list will implement the
        IDifference interface"""

        # Try to get the portal type from obj1 first.  If that fails, use obj2
        pt_name = ""
        try:
            pt_name = aq_base(ob1).portal_type
        except AttributeError:
            try:
                pt_name = aq_base(ob2).portal_type
            except AttributeError:
                pass

        diff_map = self._pt_diffs.get(pt_name, {})

        diffs = []
        for field, klass_name in diff_map.items():
            klass = self._difftypes[klass_name]
            f_diff = klass(ob1, ob2, field, id1=id1, id2=id2)
            # handle compound diff types
            if safe_hasattr(f_diff, "__getitem__"):
                diffs.extend(f_diff)
            else:
                diffs.append(f_diff)
        return diffs
Example #57
0
 def brains(self):
     """ Brains of collections or folderish content
     """
     if safe_hasattr(aq_base(self.context), 'queryCatalog'):
         return self.context.queryCatalog(batch=False)[:self.maxbreadth]
     else:
         return self.context.getFolderContents()[:self.maxbreadth]
    def onSuccess(self, fields, REQUEST=None, loopstop=False):
        # """
        # saves data.
        # """

        if LP_SAVE_TO_CANONICAL and not loopstop:
            # LinguaPlone functionality:
            # check to see if we're in a translated
            # form folder, but not the canonical version.
            parent = self.aq_parent
            if safe_hasattr(parent, 'isTranslation') and \
               parent.isTranslation() and not parent.isCanonical():
                # look in the canonical version to see if there is
                # a matching (by id) save-data adapter.
                # If so, call its onSuccess method
                cf = parent.getCanonical()
                target = cf.get(self.getId())
                if target is not None and target.meta_type == 'FormSaveDataAdapter':
                    target.onSuccess(fields, REQUEST, loopstop=True)
                    return

        from ZPublisher.HTTPRequest import FileUpload

        data = []
        for f in fields:
            showFields = getattr(self, 'showFields', [])
            if showFields and f.id not in showFields:
                continue
            if f.isFileField():
                file = REQUEST.form.get('%s_file' % f.fgField.getName())
                if isinstance(file, FileUpload) and file.filename != '':
                    file.seek(0)
                    fdata = file.read()
                    filename = file.filename
                    mimetype, enc = guess_content_type(filename, fdata, None)
                    if mimetype.find('text/') >= 0:
                        # convert to native eols
                        fdata = fdata.replace('\x0d\x0a', '\n').replace('\x0a', '\n').replace('\x0d', '\n')
                        data.append('%s:%s:%s:%s' % (filename, mimetype, enc, fdata))
                    else:
                        data.append('%s:%s:%s:Binary upload discarded' %  (filename, mimetype, enc))
                else:
                    data.append('NO UPLOAD')
            elif not f.isLabel():
                val = REQUEST.form.get(f.fgField.getName(), '')
                if not type(val) in StringTypes:
                    # Zope has marshalled the field into
                    # something other than a string
                    val = str(val)
                data.append(val)

        if self.ExtraData:
            for f in self.ExtraData:
                if f == 'dt':
                    data.append(str(DateTime()))
                else:
                    data.append(getattr(REQUEST, f, ''))


        self._addDataRow(data)
def migrate_blobimagefield(src_obj, dst_obj, src_fieldname, dst_fieldname):
    """
    migrate an image field.
    Actually this field needs only to copy the existing NamedBlobImage instance
    to the new dst_obj, but we do some more in detail and create new fields.
    """
    old_image = getattr(src_obj, src_fieldname)
    if old_image == '':
        return
    filename = safe_unicode(old_image.filename)
    old_image_data = old_image.data
    if safe_hasattr(old_image_data, 'data'):
        old_image_data = old_image_data.data
    namedblobimage = NamedBlobImage(data=old_image_data, filename=filename)

    # set new field on destination object
    setattr(dst_obj, dst_fieldname, namedblobimage)

    # handle a possible image caption field
    field = '{0}_caption'.format(src_fieldname)
    old_image_caption = getattr(src_obj, field, None)
    if old_image_caption:
        setattr(dst_obj, ('{0}_caption'.format(dst_fieldname)),
                safe_unicode(old_image_caption))

    logger.info(u'Migrating image {0}'.format(filename))
    def testChangeSetFolderComplex(self):
        self.setupTestFolders()
        # Add a new sub object
        self.folder.copy_of_folder1.invokeFactory('Document',
                                                  'doc4',
                                                  title='My Doc Title')
        # Delete a sub object
        self.folder.copy_of_folder1.manage_delObjects('doc2')
        # Change one object
        self.folder.copy_of_folder1.doc3.setTitle('My New Title')
        # Change the folder itself
        self.folder.copy_of_folder1.setTitle('My New Title')
        # Move the changed object
        if safe_hasattr(aq_base(self.folder.copy_of_folder1),
                        'moveObjectsToTop'):
            self.folder.copy_of_folder1.moveObjectsToTop(['doc3'])
        elif safe_hasattr(aq_base(self.folder.copy_of_folder1),
                          'moveObjectsByDelta'):
            self.folder.copy_of_folder1.moveObjectsByDelta(['doc3'], -3)
        else:
            # We don't have an orderable folder give up
            return

        self.cs.computeDiff(self.folder['folder1'],
                            self.folder['copy_of_folder1'])
        diffs = self.cs.getDiffs()
        self.assertEqual(len(diffs), 14)
        self.assertFalse(diffs[0].same)
        self.assertEqual(diffs[0].ndiff(),
                         '- My Folder Title%s+ My New Title' % linesep)
        self.assertEqual(list(self.cs._added), ['doc4'])
        self.assertEqual(list(self.cs._removed), ['doc2'])
        sub_cs = self.cs.getSubDiffs()
        # We only have two potentially changed objects
        self.assertEqual(len(sub_cs), 2)
        # The sub diffs should show no changes
        for i in range(len(sub_cs)):
            self.assertTrue(isinstance(sub_cs[i], BaseChangeSet))
            sub_diffs = sub_cs[i].getDiffs()
            self.assertEqual(len(sub_diffs), self.len_diff)
            if sub_cs[i].getId() == 'doc3':
                self.assertFalse(sub_diffs[0].same)
                self.assertEqual(sub_diffs[0].ndiff(),
                                 '- My Title3%s+ My New Title' % linesep)
            else:
                self.assertTrue(sub_diffs[1].same)