def _raise_publish_error(self, *args, **kwargs): raise NotModifiedError()
def publish(self, review_request=None, user=None, trivial=False, send_notification=True, validate_fields=True, timestamp=None): """Publish this draft. This is an internal method. Programmatic publishes should use :py:meth:`reviewboard.reviews.models.review_request.ReviewRequest.publish` instead. This updates and returns the draft's ChangeDescription, which contains the changed fields. This is used by the e-mail template to tell people what's new and interesting. The keys that may be saved in ``fields_changed`` in the ChangeDescription are: * ``submitter`` * ``summary`` * ``description`` * ``testing_done`` * ``bugs_closed`` * ``depends_on`` * ``branch`` * ``target_groups`` * ``target_people`` * ``screenshots`` * ``screenshot_captions`` * ``diff`` * Any custom field IDs Each field in 'fields_changed' represents a changed field. This will save fields in the standard formats as defined by the 'ChangeDescription' documentation, with the exception of the 'screenshot_captions' and 'diff' fields. For the 'screenshot_captions' field, the value will be a dictionary of screenshot ID/dict pairs with the following fields: * ``old``: The old value of the field * ``new``: The new value of the field For the ``diff`` field, there is only ever an ``added`` field, containing the ID of the new diffset. Args: review_request (reviewboard.reviews.models.review_request. ReviewRequest, optional): The review request associated with this diff. If not provided, it will be looked up. user (django.contrib.auth.models.User, optional): The user publishing the draft. If not provided, this defaults to the review request submitter. trivial (bool, optional): Whether or not this is a trivial publish. Trivial publishes do not result in e-mail notifications. send_notification (bool, optional): Whether or not this will emit the :py:data:`reviewboard.reviews.signals.review_request_published` signal. This parameter is intended for internal use **only**. validate_fields (bool, optional): Whether or not the fields should be validated. This should only be ``False`` in the case of programmatic publishes, e.g., from close as submitted hooks. timestamp (datetime.datetime, optional): The datetime that should be used for all timestamps for objects published (:py:class:`~reviewboard.diffviewer.models.diff_set.DiffSet`, :py:class:`~reviewboard.changedescs.models.ChangeDescription`) over the course of the method. Returns: reviewboard.changedescs.models.ChangeDescription: The change description that results from this publish (if any). If this is an initial publish, there will be no change description (and this function will return ``None``). """ if timestamp is None: timestamp = timezone.now() if not review_request: review_request = self.review_request if not self.changedesc and review_request.public: self.changedesc = ChangeDescription() if not user: if self.changedesc: user = self.changedesc.get_user(self) else: user = review_request.submitter self.copy_fields_to_request(review_request) # If no changes were made, raise exception and do not save if self.changedesc and not self.changedesc.has_modified_fields(): raise NotModifiedError() if validate_fields: if not (self.target_groups.exists() or self.target_people.exists()): raise PublishError( ugettext('There must be at least one reviewer before this ' 'review request can be published.')) if not review_request.summary.strip(): raise PublishError( ugettext('The draft must have a summary.')) if not review_request.description.strip(): raise PublishError( ugettext('The draft must have a description.')) if self.diffset: self.diffset.history = review_request.diffset_history self.diffset.timestamp = timestamp self.diffset.save(update_fields=('history', 'timestamp')) if self.changedesc: self.changedesc.user = user self.changedesc.timestamp = timestamp self.changedesc.public = True self.changedesc.save() review_request.changedescs.add(self.changedesc) review_request.description_rich_text = self.description_rich_text review_request.testing_done_rich_text = self.testing_done_rich_text review_request.rich_text = self.rich_text review_request.save() if send_notification: review_request_published.send(sender=type(review_request), user=user, review_request=review_request, trivial=trivial, changedesc=self.changedesc) return self.changedesc
def publish(self, review_request=None, user=None, trivial=False, send_notification=True): """Publishes this draft. This updates and returns the draft's ChangeDescription, which contains the changed fields. This is used by the e-mail template to tell people what's new and interesting. The draft's associated ReviewRequest object will be used if one isn't passed in. The keys that may be saved in ``fields_changed`` in the ChangeDescription are: * ``submitter`` * ``summary`` * ``description`` * ``testing_done`` * ``bugs_closed`` * ``depends_on`` * ``branch`` * ``target_groups`` * ``target_people`` * ``screenshots`` * ``screenshot_captions`` * ``diff`` * Any custom field IDs Each field in 'fields_changed' represents a changed field. This will save fields in the standard formats as defined by the 'ChangeDescription' documentation, with the exception of the 'screenshot_captions' and 'diff' fields. For the 'screenshot_captions' field, the value will be a dictionary of screenshot ID/dict pairs with the following fields: * ``old``: The old value of the field * ``new``: The new value of the field For the ``diff`` field, there is only ever an ``added`` field, containing the ID of the new diffset. The ``send_notification`` parameter is intended for internal use only, and is there to prevent duplicate notifications when being called by ReviewRequest.publish. """ if not review_request: review_request = self.review_request if not self.changedesc and review_request.public: self.changedesc = ChangeDescription() if not user: if self.changedesc: user = self.changedesc.get_user(self) else: user = review_request.submitter self.copy_fields_to_request(review_request) if self.diffset: self.diffset.history = review_request.diffset_history self.diffset.save(update_fields=['history']) # If no changes were made, raise exception and do not save if self.changedesc and not self.changedesc.has_modified_fields(): raise NotModifiedError() if self.changedesc: self.changedesc.user = user self.changedesc.timestamp = timezone.now() self.changedesc.public = True self.changedesc.save() review_request.changedescs.add(self.changedesc) review_request.description_rich_text = self.description_rich_text review_request.testing_done_rich_text = self.testing_done_rich_text review_request.rich_text = self.rich_text review_request.save() if send_notification: review_request_published.send(sender=review_request.__class__, user=user, review_request=review_request, trivial=trivial, changedesc=self.changedesc) return self.changedesc
def publish(self, review_request=None, user=None, send_notification=True): """Publishes this draft. This updates and returns the draft's ChangeDescription, which contains the changed fields. This is used by the e-mail template to tell people what's new and interesting. The draft's assocated ReviewRequest object will be used if one isn't passed in. The keys that may be saved in 'fields_changed' in the ChangeDescription are: * 'summary' * 'description' * 'testing_done' * 'bugs_closed' * 'depends_on' * 'branch' * 'target_groups' * 'target_people' * 'screenshots' * 'screenshot_captions' * 'diff' Each field in 'fields_changed' represents a changed field. This will save fields in the standard formats as defined by the 'ChangeDescription' documentation, with the exception of the 'screenshot_captions' and 'diff' fields. For the 'screenshot_captions' field, the value will be a dictionary of screenshot ID/dict pairs with the following fields: * 'old': The old value of the field * 'new': The new value of the field For the 'diff' field, there is only ever an 'added' field, containing the ID of the new diffset. The 'send_notification' parameter is intended for internal use only, and is there to prevent duplicate notifications when being called by ReviewRequest.publish. """ if not review_request: review_request = self.review_request if not user: user = review_request.submitter if not self.changedesc and review_request.public: self.changedesc = ChangeDescription() def update_list(a, b, name, record_changes=True, name_field=None): aset = set([x.id for x in a.all()]) bset = set([x.id for x in b.all()]) if aset.symmetric_difference(bset): if record_changes and self.changedesc: self.changedesc.record_field_change( name, a.all(), b.all(), name_field) a.clear() for item in b.all(): a.add(item) for field_cls in get_review_request_fields(): field = field_cls(review_request) if field.can_record_change_entry: old_value = field.load_value(review_request) new_value = field.load_value(self) if field.has_value_changed(old_value, new_value): field.save_value(new_value) if self.changedesc: field.record_change_entry(self.changedesc, old_value, new_value) # Screenshots are a bit special. The list of associated screenshots # can change, but so can captions within each screenshot. screenshots = list(self.screenshots.all()) caption_changes = {} for s in review_request.screenshots.all(): if s in screenshots and s.caption != s.draft_caption: caption_changes[s.id] = { 'old': (s.caption, ), 'new': (s.draft_caption, ), } s.caption = s.draft_caption s.save(update_fields=['caption']) # Now scan through again and set the caption correctly for newly-added # screenshots by copying the draft_caption over. We don't need to # include this in the changedescs here because it's a new screenshot, # and update_list will record the newly-added item. for s in screenshots: if s.caption != s.draft_caption: s.caption = s.draft_caption s.save(update_fields=['caption']) if caption_changes and self.changedesc: self.changedesc.fields_changed['screenshot_captions'] = \ caption_changes update_list(review_request.screenshots, self.screenshots, 'screenshots', name_field="caption") # There's no change notification required for this field. review_request.inactive_screenshots = self.inactive_screenshots.all() # Files are treated like screenshots. The list of files can # change, but so can captions within each file. files = list(self.file_attachments.all()) caption_changes = {} for f in review_request.file_attachments.all(): if f in files and f.caption != f.draft_caption: caption_changes[f.id] = { 'old': (f.caption, ), 'new': (f.draft_caption, ), } f.caption = f.draft_caption f.save(update_fields=['caption']) # Now scan through again and set the caption correctly for newly-added # files by copying the draft_caption over. We don't need to include # this in the changedescs here because it's a new screenshot, and # update_list will record the newly-added item. for f in files: if f.caption != f.draft_caption: f.caption = f.draft_caption f.save(update_fields=['caption']) if caption_changes and self.changedesc: self.changedesc.fields_changed['file_captions'] = caption_changes update_list(review_request.file_attachments, self.file_attachments, 'files', name_field="display_name") # There's no change notification required for this field. review_request.inactive_file_attachments = \ self.inactive_file_attachments.all() if self.diffset: self.diffset.history = review_request.diffset_history self.diffset.save(update_fields=['history']) # If no changes were made, raise exception and do not save if self.changedesc and not self.changedesc.has_modified_fields(): raise NotModifiedError() if self.changedesc: self.changedesc.timestamp = timezone.now() self.changedesc.rich_text = self.rich_text self.changedesc.public = True self.changedesc.save() review_request.changedescs.add(self.changedesc) review_request.rich_text = self.rich_text review_request.save() if send_notification: review_request_published.send(sender=review_request.__class__, user=user, review_request=review_request, changedesc=self.changedesc) return self.changedesc