def file_attachment_comments(context, file_attachment): """Returns a JSON array of current comments for a file attachment.""" comments = [] user = context.get('user', None) for comment in file_attachment.get_comments(): review = comment.get_review() if review and (review.public or review.user == user): comments.append({ 'comment_id': comment.id, 'text': normalize_text_for_edit(user, comment.text, comment.rich_text), 'rich_text': comment.rich_text, 'user': { 'username': escape(review.user.username), 'name': escape(review.user.get_full_name() or review.user.username), }, 'url': comment.get_review_url(), 'localdraft': review.user == user and not review.public, 'review_id': review.id, 'issue_opened': comment.issue_opened, 'issue_status': escape( BaseComment.issue_status_to_string(comment.issue_status)), }) return json.dumps(comments)
def get_data_attributes(self): """Return any data attributes to include in the element. By default, this returns nothing. Returns: dict: The data attributes to include in the element. """ attrs = super(BaseTextAreaField, self).get_data_attributes() if self.enable_markdown: if self.request: user = self.request.user else: user = None attrs.update({ 'allow-markdown': True, 'raw-value': normalize_text_for_edit( user, self.value, self.should_render_as_markdown(self.value)), }) return attrs
def serialize_comment(self, comment): """Serializes a comment. This will provide information on the comment that may be useful to the JavaScript code. Subclasses that want to add additional data should generally augment the result of this function and not replace it. """ review = comment.get_review() user = self.request.user return { 'comment_id': comment.pk, 'text': normalize_text_for_edit(user, comment.text, comment.rich_text), 'rich_text': comment.rich_text, 'html': markdown_render_conditional(comment.text, comment.rich_text), 'user': { 'username': review.user.username, 'name': review.user.get_full_name() or review.user.username, }, 'url': comment.get_review_url(), 'localdraft': review.user == user and not review.public, 'review_id': review.pk, 'review_request_id': review.review_request_id, 'issue_opened': comment.issue_opened, 'issue_status': comment.issue_status_to_string( comment.issue_status), }
def _normalize_text_for_edit(context, text, rich_text, escape_js=False): text = normalize_text_for_edit(context["request"].user, text, rich_text, escape_html=not escape_js) if escape_js: text = escapejs(text) return text
def file_attachment_comments(context, file_attachment): """Returns a JSON array of current comments for a file attachment.""" comments = [] user = context.get('user', None) for comment in file_attachment.get_comments(): review = comment.get_review() if review and (review.public or review.user == user): comments.append({ 'comment_id': comment.id, 'text': normalize_text_for_edit(user, comment.text, comment.rich_text), 'rich_text': comment.rich_text, 'user': { 'username': escape(review.user.username), 'name': escape(review.user.get_full_name() or review.user.username), }, 'url': comment.get_review_url(), 'localdraft': review.user == user and not review.public, 'review_id': review.id, 'issue_opened': comment.issue_opened, 'issue_status': escape( BaseComment.issue_status_to_string(comment.issue_status)), }) return json.dumps(comments)
def file_attachment_comments(context, file_attachment): """Returns a JSON array of current comments for a file attachment.""" comments = [] user = context.get("user", None) for comment in file_attachment.get_comments(): review = comment.get_review() if review and (review.public or review.user == user): comments.append( { "comment_id": comment.id, "text": normalize_text_for_edit(user, comment.text, comment.rich_text), "rich_text": comment.rich_text, "user": { "username": escape(review.user.username), "name": escape(review.user.get_full_name() or review.user.username), }, "url": comment.get_review_url(), "localdraft": review.user == user and not review.public, "review_id": review.id, "issue_opened": comment.issue_opened, "issue_status": escape(BaseComment.issue_status_to_string(comment.issue_status)), } ) return json.dumps(comments)
def _normalize_text_for_edit(context, text, rich_text, escape_js=False): text = normalize_text_for_edit(context['request'].user, text, rich_text, escape_html=not escape_js) if escape_js: text = escapejs(text) return text
def test_normalize_text_for_edit_plain_text_no_escape(self): """Testing normalize_text_for_edit with plain text and not escaping to HTML """ user = User.objects.create_user('test', '*****@*****.**') Profile.objects.create(user=user, default_use_rich_text=False) text = normalize_text_for_edit(user, text='< "test" **foo**', rich_text=True, escape_html=False) self.assertEqual(text, '< "test" **foo**') self.assertFalse(isinstance(text, SafeText))
def test_normalize_text_for_edit_plain_text_default_plain_text(self): """Testing normalize_text_for_edit with plain text and user defaults to plain text """ user = User.objects.create_user('test', '*****@*****.**') Profile.objects.create(user=user, default_use_rich_text=False) text = normalize_text_for_edit(user, text='< "test" **foo**', rich_text=True) self.assertEqual(text, '&lt; "test" **foo**') self.assertTrue(isinstance(text, SafeText))
def test_normalize_text_for_edit_plain_text_no_escape(self): """Testing normalize_text_for_edit with plain text and not escaping to HTML """ user = User.objects.create_user('test', '*****@*****.**') Profile.objects.create(user=user, default_use_rich_text=False) text = normalize_text_for_edit(user, text='< "test" **foo**', rich_text=True, escape_html=False) self.assertEqual(text, '< "test" **foo**') self.assertFalse(isinstance(text, SafeText))
def test_normalize_text_for_edit_plain_text_default_plain_text(self): """Testing normalize_text_for_edit with plain text and user defaults to plain text """ user = User.objects.create_user('test', '*****@*****.**') Profile.objects.create(user=user, default_use_rich_text=False) text = normalize_text_for_edit(user, text='< "test" **foo**', rich_text=True) self.assertEqual(text, '&lt; "test" **foo**') self.assertTrue(isinstance(text, SafeText))
def get_data_attributes(self): attrs = super(BaseTextAreaField, self).get_data_attributes() if self.enable_markdown: if self.request: user = self.request.user else: user = None attrs.update({ 'allow-markdown': True, 'raw-value': normalize_text_for_edit( user, self.value, self.should_render_as_markdown(self.value)), }) return attrs
def get_data_attributes(self): attrs = super(BaseTextAreaField, self).get_data_attributes() if self.enable_markdown: if self.request: user = self.request.user else: user = None attrs.update({ 'allow-markdown': True, 'raw-value': normalize_text_for_edit( user, self.value, self.should_render_as_markdown(self.value)), }) return attrs
def reviewable_page_model_data(context): """Output JSON-serialized data for a RB.ReviewablePage model. The data will be used by :js:class:`RB.ReviewablePage` in order to populate the review request and editor with the necessary state. Args: context (django.template.RequestContext): The current template context. Returns: unicode: The resulting JSON-serialized data. This consists of keys that are meant to be injected into an existing dictionary. """ request = context['request'] user = request.user review_request = context['review_request'] review_request_details = context['review_request_details'] draft = context['draft'] close_description = context['close_description'] close_description_rich_text = context['close_description_rich_text'] if review_request.local_site: local_site_prefix = 's/%s/' % review_request.local_site.name else: local_site_prefix = '' # Build data for the RB.ReviewRequest if review_request.status == review_request.PENDING_REVIEW: state_data = 'PENDING' elif review_request.status == review_request.SUBMITTED: state_data = 'CLOSE_SUBMITTED' elif review_request.status == review_request.DISCARDED: state_data = 'CLOSE_DISCARDED' else: raise ValueError('Unexpected ReviewRequest.status value "%s"' % review_request.status) review_request_data = { 'id': review_request.display_id, 'localSitePrefix': local_site_prefix, 'branch': review_request_details.branch, 'bugsClosed': review_request_details.get_bug_list(), 'closeDescription': normalize_text_for_edit(user=user, text=close_description, rich_text=close_description_rich_text, escape_html=False), 'closeDescriptionRichText': close_description_rich_text, 'description': normalize_text_for_edit( user=user, text=review_request_details.description, rich_text=review_request_details.description_rich_text, escape_html=False), 'descriptionRichText': review_request_details.description_rich_text, 'hasDraft': draft is not None, 'lastUpdatedTimestamp': review_request.last_updated, 'public': review_request.public, 'reviewURL': review_request.get_absolute_url(), 'state': state_data, 'summary': review_request_details.summary, 'targetGroups': [{ 'name': group.name, 'url': group.get_absolute_url(), } for group in review_request_details.target_groups.all()], 'targetPeople': [{ 'username': target_user.username, 'url': local_site_reverse('user', args=[target_user], request=request) } for target_user in review_request_details.target_people.all()], 'testingDone': normalize_text_for_edit( user=user, text=review_request_details.testing_done, rich_text=review_request_details.testing_done_rich_text, escape_html=False), 'testingDoneRichText': review_request_details.testing_done_rich_text, } if user.is_authenticated(): review_request_visit = context['review_request_visit'] if review_request_visit.visibility == review_request_visit.VISIBLE: visibility_data = 'VISIBLE' elif review_request_visit.visibility == review_request_visit.ARCHIVED: visibility_data = 'ARCHIVED' elif review_request_visit.visibility == review_request_visit.MUTED: visibility_data = 'MUTED' else: raise ValueError( 'Unexpected ReviewRequestVisit.visibility value "%s"' % review_request_visit.visibility) review_request_data['visibility'] = visibility_data repository = review_request.repository if repository: scmtool = repository.get_scmtool() review_request_data['repository'] = { 'id': repository.pk, 'name': repository.name, 'scmtoolName': scmtool.name, 'requiresBasedir': not scmtool.diffs_use_absolute_paths, 'requiresChangeNumber': scmtool.supports_pending_changesets, 'supportsPostCommit': repository.supports_post_commit, } if repository.bug_tracker: review_request_data['bugTrackerURL'] = \ local_site_reverse( 'bug_url', args=[review_request.display_id, '--bug_id--'], request=request) if draft: review_request_data['submitter'] = { 'title': draft.submitter.username, 'url': draft.submitter.get_absolute_url(), } # Build the data for the RB.ReviewRequestEditor. editor_data = { 'closeDescriptionRenderedText': _render_markdown(close_description, close_description_rich_text), 'commits': None, 'hasDraft': draft is not None, 'mutableByUser': context['mutable_by_user'], 'showSendEmail': context['send_email'], 'statusMutableByUser': context['status_mutable_by_user'], } if review_request.created_with_history: diffset = review_request_details.get_latest_diffset() editor_data['commits'] = [ commit.serialize() for commit in diffset.commits.all() ] # Build extra data for the RB.ReviewRequest. extra_review_request_draft_data = {} if draft and draft.changedesc: extra_review_request_draft_data.update({ 'changeDescription': normalize_text_for_edit(user=user, text=draft.changedesc.text, rich_text=draft.changedesc.rich_text, escape_html=False), 'changeDescriptionRichText': draft.changedesc.rich_text, }) editor_data['changeDescriptionRenderedText'] = _render_markdown( draft.changedesc.text, draft.changedesc.rich_text) if draft.diffset: extra_review_request_draft_data['interdiffLink'] = \ local_site_reverse( 'view-interdiff', args=[ review_request.display_id, draft.diffset.revision - 1, draft.diffset.revision ], request=request) # Build the file attachments data for the editor data. file_attachments_data = [] for file_attachment in context.get('file_attachments', []): if draft: caption = file_attachment.draft_caption else: caption = file_attachment.caption file_attachment_data = { 'id': file_attachment.pk, 'loaded': True, 'caption': caption, 'downloadURL': file_attachment.get_absolute_url(), 'filename': file_attachment.filename, 'revision': file_attachment.attachment_revision, 'thumbnailHTML': file_attachment.thumbnail, } if file_attachment.attachment_history_id: file_attachment_data['attachmentHistoryID'] = \ file_attachment.attachment_history_id if _has_usable_review_ui(user, review_request, file_attachment): file_attachment_data['reviewURL'] = \ local_site_reverse( 'file-attachment', args=[review_request.display_id, file_attachment.pk], request=request) file_attachments_data.append(file_attachment_data) if file_attachments_data: editor_data['fileAttachments'] = file_attachments_data # Build the file attachment comments data for the editor data. file_attachment_comments_data = {} for file_attachment in context.get('all_file_attachments', []): review_ui = file_attachment.review_ui if not review_ui: # For the purposes of serialization, we'll create a dummy ReviewUI. review_ui = FileAttachmentReviewUI(file_attachment.review_request, file_attachment) # NOTE: We're setting this here because file attachments serialization # requires this to be set, but we don't necessarily have it set # by this time. We should rethink parts of this down the road, # but it requires dealing with some compatibility issues for # subclasses. review_ui.request = request file_attachment_comments_data[file_attachment.pk] = \ review_ui.serialize_comments(file_attachment.get_comments()) if file_attachment_comments_data: editor_data['fileAttachmentComments'] = file_attachment_comments_data # And we're done! Assemble it together and chop off the outer dictionary # so it can be injected correctly. return json_dumps_items({ 'checkForUpdates': True, 'reviewRequestData': review_request_data, 'extraReviewRequestDraftData': extra_review_request_draft_data, 'editorData': editor_data, })
def comment_counts(user, all_comments, filediff, interfilediff=None): """ Returns an array of current comments for a filediff, sorted by line number. Each entry in the array has a dictionary containing the following keys: =========== ================================================== Key Description =========== ================================================== comment_id The ID of the comment text The plain or rich text of the comment rich_text The rich text flag for the comment line The first line number num_lines The number of lines this comment spans user A dictionary containing "username" and "name" keys for the user url The URL to the comment localdraft True if this is the current user's draft comment review_id The ID of the review this comment is associated with ============================================================== """ comment_dict = {} if interfilediff: key = (filediff.pk, interfilediff.pk) else: key = (filediff.pk, None) comments = all_comments.get(key, []) for comment in comments: review = comment.get_review() if review and (review.public or review.user_id == user.pk): key = (comment.first_line, comment.num_lines) comment_dict.setdefault(key, []).append({ 'comment_id': comment.id, 'text': normalize_text_for_edit(user, comment.text, comment.rich_text), 'html': markdown_render_conditional(comment.text, comment.rich_text), 'rich_text': comment.rich_text, 'line': comment.first_line, 'num_lines': comment.num_lines, 'user': { 'username': review.user.username, 'name': (review.user.get_full_name() or review.user.username), }, 'url': comment.get_review_url(), 'localdraft': (review.user == user and not review.public), 'review_id': review.id, 'issue_opened': comment.issue_opened, 'issue_status': BaseComment.issue_status_to_string( comment.issue_status), 'reply_to_id': comment.reply_to_id, }) comments_array = [] for key, value in six.iteritems(comment_dict): comments_array.append({ 'linenum': key[0], 'num_lines': key[1], 'comments': value, }) comments_array.sort(key=cmp_to_key( lambda x, y: cmp(x['linenum'], y['linenum'] or cmp(x['num_lines'], y['num_lines'])))) return comments_array
def comment_counts(user, all_comments, filediff, interfilediff=None): """ Returns an array of current comments for a filediff, sorted by line number. Each entry in the array has a dictionary containing the following keys: =========== ================================================== Key Description =========== ================================================== comment_id The ID of the comment text The plain or rich text of the comment rich_text The rich text flag for the comment line The first line number num_lines The number of lines this comment spans user A dictionary containing "username" and "name" keys for the user url The URL to the comment localdraft True if this is the current user's draft comment review_id The ID of the review this comment is associated with ============================================================== """ comment_dict = {} if interfilediff: key = (filediff.pk, interfilediff.pk) else: key = (filediff.pk, None) comments = all_comments.get(key, []) for comment in comments: review = comment.get_review() if review and (review.public or review.user == user): key = (comment.first_line, comment.num_lines) comment_dict.setdefault(key, []).append({ 'comment_id': comment.id, 'text': normalize_text_for_edit(user, comment.text, comment.rich_text), 'html': markdown_render_conditional(comment.text, comment.rich_text), 'rich_text': comment.rich_text, 'line': comment.first_line, 'num_lines': comment.num_lines, 'user': { 'username': review.user.username, 'name': (review.user.get_full_name() or review.user.username), }, 'url': comment.get_review_url(), 'localdraft': (review.user == user and not review.public), 'review_id': review.id, 'issue_opened': comment.issue_opened, 'issue_status': BaseComment.issue_status_to_string( comment.issue_status), 'reply_to_id': comment.reply_to_id, }) comments_array = [] for key, value in six.iteritems(comment_dict): comments_array.append({ 'linenum': key[0], 'num_lines': key[1], 'comments': value, }) comments_array.sort( cmp=lambda x, y: (cmp(x['linenum'], y['linenum'] or cmp(x['num_lines'], y['num_lines'])))) return comments_array
def reviewable_page_model_data(context): """Output JSON-serialized data for a RB.ReviewablePage model. The data will be used by :js:class:`RB.ReviewablePage` in order to populate the review request and editor with the necessary state. Args: context (django.template.RequestContext): The current template context. Returns: unicode: The resulting JSON-serialized data. This consists of keys that are meant to be injected into an existing dictionary. """ request = context['request'] user = request.user review_request = context['review_request'] review_request_details = context['review_request_details'] draft = context['draft'] close_description = context['close_description'] close_description_rich_text = context['close_description_rich_text'] if review_request.local_site: local_site_prefix = 's/%s/' % review_request.local_site.name else: local_site_prefix = '' # Build data for the RB.ReviewRequest if review_request.status == review_request.PENDING_REVIEW: state_data = 'PENDING' elif review_request.status == review_request.SUBMITTED: state_data = 'CLOSE_SUBMITTED' elif review_request.status == review_request.DISCARDED: state_data = 'CLOSE_DISCARDED' else: raise ValueError('Unexpected ReviewRequest.status value "%s"' % review_request.status) review_request_data = { 'id': review_request.display_id, 'localSitePrefix': local_site_prefix, 'branch': review_request_details.branch, 'bugsClosed': review_request_details.get_bug_list(), 'closeDescription': normalize_text_for_edit( user=user, text=close_description, rich_text=close_description_rich_text, escape_html=False), 'closeDescriptionRichText': close_description_rich_text, 'description': normalize_text_for_edit( user=user, text=review_request_details.description, rich_text=review_request_details.description_rich_text, escape_html=False), 'descriptionRichText': review_request_details.description_rich_text, 'hasDraft': draft is not None, 'lastUpdatedTimestamp': review_request.last_updated, 'public': review_request.public, 'reviewURL': review_request.get_absolute_url(), 'state': state_data, 'summary': review_request_details.summary, 'targetGroups': [ { 'name': group.name, 'url': group.get_absolute_url(), } for group in review_request_details.target_groups.all() ], 'targetPeople': [ { 'username': target_user.username, 'url': local_site_reverse('user', args=[target_user], request=request) } for target_user in review_request_details.target_people.all() ], 'testingDone': normalize_text_for_edit( user=user, text=review_request_details.testing_done, rich_text=review_request_details.testing_done_rich_text, escape_html=False), 'testingDoneRichText': review_request_details.testing_done_rich_text, } if user.is_authenticated(): review_request_visit = context['review_request_visit'] if review_request_visit.visibility == review_request_visit.VISIBLE: visibility_data = 'VISIBLE' elif review_request_visit.visibility == review_request_visit.ARCHIVED: visibility_data = 'ARCHIVED' elif review_request_visit.visibility == review_request_visit.MUTED: visibility_data = 'MUTED' else: raise ValueError( 'Unexpected ReviewRequestVisit.visibility value "%s"' % review_request_visit.visibility) review_request_data['visibility'] = visibility_data repository = review_request.repository if repository: scmtool = repository.get_scmtool() review_request_data['repository'] = { 'id': repository.pk, 'name': repository.name, 'scmtoolName': scmtool.name, 'requiresBasedir': not scmtool.diffs_use_absolute_paths, 'requiresChangeNumber': scmtool.supports_pending_changesets, 'supportsPostCommit': repository.supports_post_commit, } if repository.bug_tracker: review_request_data['bugTrackerURL'] = \ local_site_reverse( 'bug_url', args=[review_request.display_id, '--bug_id--'], request=request) if draft: review_request_data['submitter'] = { 'title': draft.submitter.username, 'url': draft.submitter.get_absolute_url(), } # Build the data for the RB.ReviewRequestEditor. editor_data = { 'closeDescriptionRenderedText': _render_markdown( close_description, close_description_rich_text), 'hasDraft': draft is not None, 'mutableByUser': context['mutable_by_user'], 'showSendEmail': context['send_email'], 'statusMutableByUser': context['status_mutable_by_user'], } # Build extra data for the RB.ReviewRequest. extra_review_request_draft_data = {} if draft and draft.changedesc: extra_review_request_draft_data.update({ 'changeDescription': normalize_text_for_edit( user=user, text=draft.changedesc.text, rich_text=draft.changedesc.rich_text, escape_html=False), 'changeDescriptionRichText': draft.changedesc.rich_text, }) editor_data['changeDescriptionRenderedText'] = _render_markdown( draft.changedesc.text, draft.changedesc.rich_text) if draft.diffset: extra_review_request_draft_data['interdiffLink'] = \ local_site_reverse( 'view-interdiff', args=[ review_request.display_id, draft.diffset.revision - 1, draft.diffset.revision ], request=request) # Build the file attachments data for the editor data. file_attachments_data = [] for file_attachment in context.get('file_attachments', []): if draft: caption = file_attachment.draft_caption else: caption = file_attachment.caption file_attachment_data = { 'id': file_attachment.pk, 'loaded': True, 'caption': caption, 'downloadURL': file_attachment.get_absolute_url(), 'filename': file_attachment.filename, 'revision': file_attachment.attachment_revision, 'thumbnailHTML': file_attachment.thumbnail, } if file_attachment.attachment_history_id: file_attachment_data['attachmentHistoryID'] = \ file_attachment.attachment_history_id if _has_usable_review_ui(user, review_request, file_attachment): file_attachment_data['reviewURL'] = \ local_site_reverse( 'file-attachment', args=[review_request.display_id, file_attachment.pk], request=request) file_attachments_data.append(file_attachment_data) if file_attachments_data: editor_data['fileAttachments'] = file_attachments_data # Build the file attachment comments data for the editor data. file_attachment_comments_data = {} for file_attachment in context.get('all_file_attachments', []): review_ui = file_attachment.review_ui if not review_ui: # For the purposes of serialization, we'll create a dummy ReviewUI. review_ui = FileAttachmentReviewUI(file_attachment.review_request, file_attachment) # NOTE: We're setting this here because file attachments serialization # requires this to be set, but we don't necessarily have it set # by this time. We should rethink parts of this down the road, # but it requires dealing with some compatibility issues for # subclasses. review_ui.request = request file_attachment_comments_data[file_attachment.pk] = \ review_ui.serialize_comments(file_attachment.get_comments()) if file_attachment_comments_data: editor_data['fileAttachmentComments'] = file_attachment_comments_data # And we're done! Assemble it together and chop off the outer dictionary # so it can be injected correctly. return json_dumps_items({ 'checkForUpdates': True, 'reviewRequestData': review_request_data, 'extraReviewRequestDraftData': extra_review_request_draft_data, 'editorData': editor_data, })