Example #1
0
    def _check_key_consistency(self, current_attr, key, obj_patch_old,
                               patch_current):
        """
        check if a key provided in patch is consistent with current state

        """

        if key not in self._attrs.keys():
            raise main_errors.PatchUnprocessable(
                message="unknown key in patch")
        old_val = getattr(obj_patch_old, key)
        cur_val = getattr(self, key)
        msg = "Patch current_state not consistent with db, step {} key {}"

        if isinstance(current_attr, types.ListType):
            if not isinstance(cur_val, types.ListType):
                raise main_errors.PatchConflict(messag=msg.format(0, key))

        if key not in patch_current.keys():
            # means patch wants to add the key.
            # Value in db should be null or empty
            if cur_val not in (None, [], {}):
                raise main_errors.PatchConflict(message=msg.format(0.5, key))
        else:
            if isinstance(current_attr, types.ListType):
                if old_val == [] and cur_val != []:
                    raise main_errors.PatchConflict(message=msg.format(1, key))
                if cur_val == [] and old_val != []:
                    raise main_errors.PatchConflict(message=msg.format(2, key))
                for old in old_val:
                    for elem in cur_val:
                        if issubclass(current_attr[0], CaliopenObject):
                            if elem.__dict__ == old.__dict__:
                                break
                        else:
                            if elem == old:
                                break
                    else:
                        raise main_errors.PatchConflict(
                            message=msg.format(3, key))
            elif issubclass(self._attrs[key], types.DictType):
                if cmp(old_val, cur_val) != 0:
                    raise main_errors.PatchConflict(message=msg.format(4, key))
            else:
                # XXX ugly patch but else compare 2 distinct objects
                # and not their representation
                if hasattr(old_val, 'marshall_dict') and \
                        hasattr(cur_val, 'marshall_dict'):
                    old_val = old_val.marshall_dict()
                    cur_val = cur_val.marshall_dict()
                if old_val != cur_val:
                    raise main_errors.PatchConflict(message=msg.format(5, key))
Example #2
0
    def validate_consistency(self, user, is_new):
        """
        Function used by create_draft and patch_draft
        to unsure provided params are consistent with draft's context

        :param user : the user object
        :param is_new : if true indicates that we want to validate a new draft,
                                    otherwise it is an update of existing one

        If needed, draft is modified to conform.
        """
        try:
            self.validate()

        except Exception as exc:
            log.warn("draft validation failed with error {}".format(exc))
            raise exc
        # copy body to body_plain TODO : manage plain or html depending on user pref.
        if hasattr(self, "body") and self.body is not None:
            self.body_plain = self.body
        else:
            self.body = self.body_plain = ""
        # check discussion consistency and get last message from discussion
        last_message = self._check_discussion_consistency(user)
        if last_message is not None:
            # check subject consistency
            # (https://www.wikiwand.com/en/List_of_email_subject_abbreviations)
            # for now, we use standard prefix «Re: » (RFC5322#section-3.6.5)
            p = re.compile(
                '([\[\(] *)?(RE?S?|FYI|RIF|I|FS|VB|RV|ENC|ODP|PD|YNT|ILT|SV|VS|VL|AW|WG|ΑΠ|ΣΧΕΤ|ΠΡΘ|תגובה|הועבר|主题|转发|FWD?) *([-:;)\]][ :;\])-]*|$)|\]+ *$',
                re.IGNORECASE)
            if hasattr(self, 'subject') and self.subject is not None:
                if p.sub('', self.subject).strip() != p.sub(
                        '', last_message.subject).strip():
                    raise err.PatchConflict(message="subject has been changed")
            else:
                # no subject property provided :
                # add subject from context with only one "Re: " prefix
                self.subject = "Re: " + p.sub('', last_message.subject).strip()

                # TODO: prevent modification of protected attributes
                # below attributes should not be editable by patch:
                # - tags
        else:
            # fill <from> field consistently
            # based on current user's selected identity
            self._add_from_participant(user)
Example #3
0
    def _check_discussion_consistency(self, user):
        from caliopen_main.message.objects.message import Message
        new_discussion = False
        if not hasattr(self, 'discussion_id') or self.discussion_id == "" \
                or self.discussion_id is None:
            # no discussion_id provided. Try to find one with draft's parent_id
            # or create new discussion
            if hasattr(self, 'parent_id') \
                    and self.parent_id is not None \
                    and self.parent_id != "":
                parent_msg = Message(user, message_id=self.parent_id)
                try:
                    parent_msg.get_db()
                    parent_msg.unmarshall_db()
                except NotFound:
                    raise err.PatchError(message="parent message not found")
                self.discussion_id = parent_msg.discussion_id
            else:
                discussion = Discussion.create_from_message(user, self)
                self.discussion_id = discussion.discussion_id
                new_discussion = True
        if not new_discussion:
            dim = DIM(user)
            d_id = self.discussion_id
            last_message = dim.get_last_message(d_id, -10, 10, True)
            if last_message == {}:
                raise err.PatchError(
                    message='No such discussion {}'.format(d_id))
            is_a_reply = (str(last_message.message_id) != str(self.message_id))
            if is_a_reply:
                # check participants consistency
                if hasattr(self,
                           "participants") and len(self.participants) > 0:
                    participants = [p['address'] for p in self.participants]
                    last_msg_participants = [
                        p['address'] for p in last_message.participants
                    ]
                    if len(participants) != len(last_msg_participants):
                        raise err.PatchError(
                            message="list of participants "
                            "is not consistent for this discussion")
                    participants.sort()
                    last_msg_participants.sort()

                    for i, participant in enumerate(participants):
                        if participant != last_msg_participants[i]:
                            raise err.PatchConflict(
                                message="list of participants "
                                "is not consistent for this discussion")
                else:
                    self.build_participants_for_reply(user)

                # check parent_id consistency
                if 'parent_id' in self and self.parent_id != "" \
                        and self.parent_id is not None:
                    if not dim.message_belongs_to(
                            discussion_id=self.discussion_id,
                            message_id=self.parent_id):
                        raise err.PatchConflict(message="provided message "
                                                "parent_id does not belong "
                                                "to this discussion")
                else:
                    self.parent_id = last_message.parent_id

                self.update_external_references(user)

            else:
                last_message = None
        else:
            last_message = None

        return last_message