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))
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)
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