Beispiel #1
0
    def update_issue_status(self, request, comment_resource, *args, **kwargs):
        """Updates the issue status for a comment.

        Handles all of the logic for updating an issue status.
        """
        try:
            review_request = \
                resources.review_request.get_object(request, *args, **kwargs)
            comment = comment_resource.get_object(request, *args, **kwargs)
        except ObjectDoesNotExist:
            return DOES_NOT_EXIST

        # Check permissions to change the issue status
        if not comment.can_change_issue_status(request.user):
            return self._no_access_error(request.user)

        # We can only update the status of an issue if an issue has been
        # opened
        if not comment.issue_opened:
            raise PermissionDenied

        # We can only update the status of the issue
        issue_status = \
            BaseComment.issue_string_to_status(kwargs.get('issue_status'))
        comment.issue_status = issue_status
        comment.save()

        last_activity_time, updated_object = review_request.get_last_activity()
        comment.timestamp = localize(comment.timestamp)

        return 200, {
            comment_resource.item_result_key: comment,
            'last_activity_time': last_activity_time.isoformat(),
        }
Beispiel #2
0
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':
                escape(comment.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.id,
                'issue_opened':
                comment.issue_opened,
                'issue_status':
                BaseComment.issue_status_to_string(comment.issue_status),
            })

    return simplejson.dumps(comments)
Beispiel #3
0
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 update_issue_status(self, request, comment_resource, *args, **kwargs):
        """Updates the issue status for a comment.

        Handles all of the logic for updating an issue status.
        """
        try:
            review_request = \
                resources.review_request.get_object(request, *args, **kwargs)
            comment = comment_resource.get_object(request, *args, **kwargs)
        except ObjectDoesNotExist:
            return DOES_NOT_EXIST

        # Check permissions to change the issue status
        if not comment.can_change_issue_status(request.user):
            return self.get_no_access_error(request)

        # We can only update the status of an issue if an issue has been
        # opened
        if not comment.issue_opened:
            raise PermissionDenied

        comment._review_request = review_request

        # We can only update the status of the issue
        issue_status = \
            BaseComment.issue_string_to_status(kwargs.get('issue_status'))
        comment.issue_status = issue_status
        comment.save(update_fields=['issue_status'])

        last_activity_time, updated_object = review_request.get_last_activity()

        return 200, {
            comment_resource.item_result_key: comment,
            'last_activity_time': last_activity_time.isoformat(),
        }
Beispiel #5
0
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': escape(comment.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.id,
                'issue_opened': comment.issue_opened,
                'issue_status': BaseComment.issue_status_to_string(
                    comment.issue_status),
            })

    return json.dumps(comments)
Beispiel #6
0
def screenshotcommentcounts(context, screenshot):
    """
    Returns a JSON array of current comments for a screenshot.

    Each entry in the array has a dictionary containing the following keys:

      ================== ====================================================
      Key                Description
      ================== ====================================================
      text               The text of the comment
      localdraft         True if this is the current user's draft comment
      x                  The X location of the comment's region
      y                  The Y location of the comment's region
      w                  The width of the comment's region
      h                  The height of the comment's region
      review_id          The ID of the review this comment is associated with
      ================== ====================================================
    """
    comments = {}
    user = context.get('user', None)

    for comment in screenshot.get_comments():
        review = comment.get_review()

        if review and (review.public or review.user == user):
            position = '%dx%d+%d+%d' % (comment.w, comment.h, comment.x,
                                        comment.y)

            comments.setdefault(position, []).append({
                'comment_id':
                comment.id,
                'text':
                escape(comment.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,
                'x':
                comment.x,
                'y':
                comment.y,
                'w':
                comment.w,
                'h':
                comment.h,
                'review_id':
                review.id,
                'issue_opened':
                comment.issue_opened,
                'issue_status':
                BaseComment.issue_status_to_string(comment.issue_status),
            })

    return simplejson.dumps(comments)
Beispiel #7
0
def screenshotcommentcounts(context, screenshot):
    """
    Returns a JSON array of current comments for a screenshot.

    Each entry in the array has a dictionary containing the following keys:

      ================== ====================================================
      Key                Description
      ================== ====================================================
      text               The text of the comment
      localdraft         True if this is the current user's draft comment
      x                  The X location of the comment's region
      y                  The Y location of the comment's region
      w                  The width of the comment's region
      h                  The height of the comment's region
      review_id          The ID of the review this comment is associated with
      review_request_id  The ID of the review request this comment is
                         associated with
      ================== ====================================================
    """
    comments = {}
    user = context.get("user", None)

    for comment in screenshot.comments.all():
        review = get_object_or_none(comment.review)

        if review and (review.public or review.user == user):
            position = "%dx%d+%d+%d" % (comment.w, comment.h, comment.x, comment.y)

            comments.setdefault(position, []).append(
                {
                    "comment_id": comment.id,
                    "text": comment.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,
                    "x": comment.x,
                    "y": comment.y,
                    "w": comment.w,
                    "h": comment.h,
                    "review_id": review.id,
                    "review_request_id": review.review_request.id,
                    "issue_opened": comment.issue_opened,
                    "issue_status": BaseComment.issue_status_to_string(comment.issue_status),
                }
            )

    return simplejson.dumps(comments)
Beispiel #8
0
def screenshotcommentcounts(context, screenshot):
    """
    Returns a JSON array of current comments for a screenshot.

    Each entry in the array has a dictionary containing the following keys:

      ================== ====================================================
      Key                Description
      ================== ====================================================
      text               The text of the comment
      localdraft         True if this is the current user's draft comment
      x                  The X location of the comment's region
      y                  The Y location of the comment's region
      w                  The width of the comment's region
      h                  The height of the comment's region
      review_id          The ID of the review this comment is associated with
      ================== ====================================================
    """
    comments = {}
    user = context.get('user', None)

    for comment in screenshot.get_comments():
        review = comment.get_review()

        if review and (review.public or review.user == user):
            position = '%dx%d+%d+%d' % (comment.w, comment.h,
                                        comment.x, comment.y)

            comments.setdefault(position, []).append({
                'comment_id': comment.id,
                'text': escape(comment.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,
                'x': comment.x,
                'y': comment.y,
                'w': comment.w,
                'h': comment.h,
                'review_id': review.id,
                'issue_opened': comment.issue_opened,
                'issue_status': BaseComment.issue_status_to_string(
                    comment.issue_status),
            })

    return simplejson.dumps(comments)
Beispiel #9
0
def comment_issue(context, review_request, comment, comment_type):
    """
    Renders the code responsible for handling comment issue statuses.
    """

    issue_status = BaseComment.issue_status_to_string(comment.issue_status)
    user = context.get('user', None)

    return {
        'comment': comment,
        'comment_type': comment_type,
        'issue_status': issue_status,
        'review': comment.get_review(),
        'interactive': comment.can_change_issue_status(user),
    }
Beispiel #10
0
def comment_issue(context, review_request, comment, comment_type):
    """
    Renders the code responsible for handling comment issue statuses.
    """

    issue_status = BaseComment.issue_status_to_string(comment.issue_status)
    user = context.get('user', None)

    return {
        'comment': comment,
        'comment_type': comment_type,
        'issue_status': issue_status,
        'review': comment.get_review(),
        'interactive': comment.can_change_issue_status(user),
    }
Beispiel #11
0
def comment_issue(context, review_request, comment, comment_type):
    """
    Renders the code responsible for handling comment issue statuses.
    """

    issue_status = BaseComment.issue_status_to_string(comment.issue_status)
    user = context.get("user", None)

    return {
        "comment": comment,
        "comment_type": comment_type,
        "issue_status": issue_status,
        "review": comment.get_review(),
        "interactive": comment.can_change_issue_status(user),
    }
Beispiel #12
0
    def should_update_issue_status(self, comment, issue_status=None,
                                   issue_opened=None, **kwargs):
        """Returns True if the comment should have its issue status updated.

        Determines if a comment should have its issue status updated based
        on the current state of the comment, the review, and the arguments
        passed in the request.
        """
        if not issue_status:
            return False

        issue_status = BaseComment.issue_string_to_status(issue_status)

        return (comment.review.get().public and
                (comment.issue_opened or issue_opened) and
                issue_status != comment.issue_status)
Beispiel #13
0
    def should_update_issue_status(self, comment, issue_status=None,
                                   issue_opened=None, **kwargs):
        """Returns True if the comment should have its issue status updated.

        Determines if a comment should have its issue status updated based
        on the current state of the comment, the review, and the arguments
        passed in the request.
        """
        if not issue_status:
            return False

        issue_status = BaseComment.issue_string_to_status(issue_status)

        return (comment.review.get().public and
                (comment.issue_opened or issue_opened) and
                issue_status != comment.issue_status)
Beispiel #14
0
    def update_issue_status(self, request, comment_resource, *args, **kwargs):
        """Updates the issue status for a comment.

        Handles all of the logic for updating an issue status.
        """
        try:
            review_request = \
                resources.review_request.get_object(request, *args, **kwargs)
            comment = comment_resource.get_object(request, *args, **kwargs)
        except ObjectDoesNotExist:
            return DOES_NOT_EXIST

        # Check permissions to change the issue status
        if not comment.can_change_issue_status(request.user):
            return self.get_no_access_error(request)

        # We can only update the status of an issue if an issue has been
        # opened
        if not comment.issue_opened:
            raise PermissionDenied

        comment._review_request = review_request
        issue_status = \
            BaseComment.issue_string_to_status(kwargs.get('issue_status'))

        # If the issue requires verification, ensure that only people who are
        # authorized can close it.
        if (comment.require_verification
                and issue_status in (BaseComment.RESOLVED, BaseComment.DROPPED)
                and comment.issue_status
                in (BaseComment.OPEN, BaseComment.VERIFYING_RESOLVED,
                    BaseComment.VERIFYING_DROPPED)
                and not comment.can_verify_issue_status(request.user)):
            return self.get_no_access_error(request)

        # We can only update the status of the issue
        comment.issue_status = issue_status
        comment.save(update_fields=['issue_status'])

        last_activity_time = \
            review_request.get_last_activity_info()['timestamp']

        return 200, {
            comment_resource.item_result_key: comment,
            'last_activity_time': last_activity_time.isoformat(),
        }
Beispiel #15
0
    def update_issue_status(self, request, comment_resource, *args, **kwargs):
        """Updates the issue status for a comment.

        Handles all of the logic for updating an issue status.
        """
        try:
            review_request = \
                resources.review_request.get_object(request, *args, **kwargs)
            comment = comment_resource.get_object(request, *args, **kwargs)
        except ObjectDoesNotExist:
            return DOES_NOT_EXIST

        # Check permissions to change the issue status
        if not comment.can_change_issue_status(request.user):
            return self.get_no_access_error(request)

        # We can only update the status of an issue if an issue has been
        # opened
        if not comment.issue_opened:
            raise PermissionDenied

        comment._review_request = review_request
        issue_status = \
            BaseComment.issue_string_to_status(kwargs.get('issue_status'))

        # If the issue requires verification, ensure that only people who are
        # authorized can close it.
        if (comment.require_verification and
            issue_status in (BaseComment.RESOLVED, BaseComment.DROPPED) and
            comment.issue_status in (BaseComment.OPEN,
                                     BaseComment.VERIFYING_RESOLVED,
                                     BaseComment.VERIFYING_DROPPED) and
            not comment.can_verify_issue_status(request.user)):
            return self.get_no_access_error(request)

        # We can only update the status of the issue
        comment.issue_status = issue_status
        comment.save(update_fields=['issue_status'])

        last_activity_time, updated_object = review_request.get_last_activity()

        return 200, {
            comment_resource.item_result_key: comment,
            'last_activity_time': last_activity_time.isoformat(),
        }
Beispiel #16
0
def comment_issue(context, review_request, comment, comment_type):
    """
    Renders the code responsible for handling comment issue statuses.
    """

    issue_status = BaseComment.issue_status_to_string(comment.issue_status)
    user = context.get("user", None)
    interactive = "false"

    if user and user.is_authenticated() and user == review_request.submitter:
        interactive = "true"

    return {
        "comment": comment,
        "comment_type": comment_type,
        "issue_status": issue_status,
        "review": comment.review.get(),
        "interactive": interactive,
    }
Beispiel #17
0
def comment_issue(context, review_request, comment, comment_type):
    """
    Renders the code responsible for handling comment issue statuses.
    """

    issue_status = BaseComment.issue_status_to_string(comment.issue_status)
    user = context.get('user', None)
    interactive = 'false'

    if user and user.is_authenticated() and \
        user == review_request.submitter:
        interactive = 'true'

    return {
        'comment': comment,
        'comment_type': comment_type,
        'issue_status': issue_status,
        'review': comment.get_review(),
        'interactive': interactive,
    }
Beispiel #18
0
def comment_issue(context, review_request, comment, comment_type):
    """
    Renders the code responsible for handling comment issue statuses.
    """

    issue_status = BaseComment.issue_status_to_string(comment.issue_status)
    user = context.get('user', None)
    interactive = 'false'

    if user and user.is_authenticated() and \
        user == review_request.submitter:
        interactive = 'true'

    return {
        'comment': comment,
        'comment_type': comment_type,
        'issue_status': issue_status,
        'review': comment.get_review(),
        'interactive': interactive,
    }
Beispiel #19
0
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
Beispiel #20
0
def pretty_print_issue_status(status):
    """Turns an issue status code into a human-readable status string."""
    return BaseComment.issue_status_to_string(status)
Beispiel #21
0
def review_detail(request,
                  review_request_id,
                  local_site_name=None,
                  template_name="reviews/review_detail.html"):
    """
    Main view for review requests. This covers the review request information
    and all the reviews on it.
    """
    # If there's a local_site passed in the URL, we want to look up the review
    # request based on the local_id instead of the pk. This allows each
    # local_site configured to have its own review request ID namespace
    # starting from 1.
    review_request, response = \
        _find_review_request(request, review_request_id, local_site_name)

    if not review_request:
        return response

    reviews = review_request.get_public_reviews()
    review = review_request.get_pending_review(request.user)
    review_timestamp = 0
    last_visited = 0
    starred = False

    if request.user.is_authenticated():
        # If the review request is public and pending review and if the user
        # is logged in, mark that they've visited this review request.
        if review_request.public and review_request.status == "P":
            visited, visited_is_new = ReviewRequestVisit.objects.get_or_create(
                user=request.user, review_request=review_request)
            last_visited = visited.timestamp
            visited.timestamp = datetime.now()
            visited.save()

        profile, profile_is_new = Profile.objects.get_or_create(
            user=request.user)
        starred = review_request in profile.starred_review_requests.all()

        # Unlike review above, this covers replies as well.
        try:
            last_draft_review = Review.objects.filter(
                review_request=review_request, user=request.user,
                public=False).latest()
            review_timestamp = last_draft_review.timestamp
        except Review.DoesNotExist:
            pass

    draft = review_request.get_draft(request.user)

    # Find out if we can bail early. Generate an ETag for this.
    last_activity_time, updated_object = review_request.get_last_activity()

    if draft:
        draft_timestamp = draft.last_updated
    else:
        draft_timestamp = ""

    etag = "%s:%s:%s:%s:%s:%s" % (request.user, last_activity_time,
                                  draft_timestamp, review_timestamp,
                                  int(starred), settings.AJAX_SERIAL)

    if etag_if_none_match(request, etag):
        return HttpResponseNotModified()

    changedescs = review_request.changedescs.filter(public=True)
    latest_changedesc = None

    try:
        latest_changedesc = changedescs.latest()
        latest_timestamp = latest_changedesc.timestamp
    except ChangeDescription.DoesNotExist:
        latest_timestamp = None

    entries = []

    for temp_review in reviews:
        temp_review.ordered_comments = \
            temp_review.comments.order_by('filediff', 'first_line')

        state = ''

        # Mark as collapsed if the review is older than the latest change
        if latest_timestamp and temp_review.timestamp < latest_timestamp:
            state = 'collapsed'

        try:
            latest_reply = temp_review.public_replies().latest(
                'timestamp').timestamp
        except Review.DoesNotExist:
            latest_reply = None

        # Mark as expanded if there is a reply newer than last_visited
        if latest_reply and last_visited and last_visited < latest_reply:
            state = ''

        entries.append({
            'review': temp_review,
            'timestamp': temp_review.timestamp,
            'class': state,
        })

    for changedesc in changedescs:
        fields_changed = []

        for name, info in changedesc.fields_changed.items():
            multiline = False

            if 'added' in info or 'removed' in info:
                change_type = 'add_remove'

                # We don't hard-code URLs in the bug info, since the
                # tracker may move, but we can do it here.
                if (name == "bugs_closed"
                        and review_request.repository.bug_tracker):
                    bug_url = review_request.repository.bug_tracker
                    for field in info:
                        for i, buginfo in enumerate(info[field]):
                            try:
                                full_bug_url = bug_url % buginfo[0]
                                info[field][i] = (buginfo[0], full_bug_url)
                            except TypeError:
                                logging.warning(
                                    "Invalid bugtracker url format")

            elif 'old' in info or 'new' in info:
                change_type = 'changed'
                multiline = (name == "description" or name == "testing_done")

                # Branch text is allowed to have entities, so mark it safe.
                if name == "branch":
                    if 'old' in info:
                        info['old'][0] = mark_safe(info['old'][0])

                    if 'new' in info:
                        info['new'][0] = mark_safe(info['new'][0])

                # Make status human readable.
                if name == 'status':
                    if 'old' in info:
                        info['old'][0] = status_to_string(info['old'][0])

                    if 'new' in info:
                        info['new'][0] = status_to_string(info['new'][0])

            elif name == "screenshot_captions":
                change_type = 'screenshot_captions'
            elif name == "file_captions":
                change_type = 'file_captions'
            else:
                # No clue what this is. Bail.
                continue

            fields_changed.append({
                'title':
                fields_changed_name_map.get(name, name),
                'multiline':
                multiline,
                'info':
                info,
                'type':
                change_type,
            })

        # Expand the latest review change
        state = ''

        # Mark as collapsed if the change is older than a newer change
        if latest_timestamp and changedesc.timestamp < latest_timestamp:
            state = 'collapsed'

        entries.append({
            'changeinfo': fields_changed,
            'changedesc': changedesc,
            'timestamp': changedesc.timestamp,
            'class': state,
        })

    entries.sort(key=lambda item: item['timestamp'])

    close_description = ''

    if latest_changedesc and 'status' in latest_changedesc.fields_changed:
        status = latest_changedesc.fields_changed['status']['new'][0]

        if status in (ReviewRequest.DISCARDED, ReviewRequest.SUBMITTED):
            close_description = latest_changedesc.text

    issues = {'total': 0, 'open': 0, 'resolved': 0, 'dropped': 0}

    for entry in entries:
        if 'review' in entry:
            for comment in entry['review'].get_all_comments(issue_opened=True):
                issues['total'] += 1
                issues[BaseComment.issue_status_to_string(
                    comment.issue_status)] += 1

    response = render_to_response(
        template_name,
        RequestContext(
            request,
            _make_review_request_context(
                review_request, {
                    'draft': draft,
                    'detail_hooks': ReviewRequestDetailHook.hooks,
                    'review_request_details': draft or review_request,
                    'entries': entries,
                    'last_activity_time': last_activity_time,
                    'review': review,
                    'request': request,
                    'latest_changedesc': latest_changedesc,
                    'close_description': close_description,
                    'PRE_CREATION': PRE_CREATION,
                    'issues': issues,
                })))
    set_etag(response, etag)

    return response
Beispiel #22
0
def review_detail(request,
                  review_request_id,
                  local_site_name=None,
                  template_name="reviews/review_detail.html"):
    """
    Main view for review requests. This covers the review request information
    and all the reviews on it.
    """
    # If there's a local_site passed in the URL, we want to look up the review
    # request based on the local_id instead of the pk. This allows each
    # local_site configured to have its own review request ID namespace
    # starting from 1.
    review_request, response = \
        _find_review_request(request, review_request_id, local_site_name)

    if not review_request:
        return response

    reviews = review_request.get_public_reviews()
    review = review_request.get_pending_review(request.user)
    review_timestamp = 0
    last_visited = 0
    starred = False

    if request.user.is_authenticated():
        # If the review request is public and pending review and if the user
        # is logged in, mark that they've visited this review request.
        if review_request.public and review_request.status == "P":
            visited, visited_is_new = ReviewRequestVisit.objects.get_or_create(
                user=request.user, review_request=review_request)
            last_visited = visited.timestamp
            visited.timestamp = datetime.now()
            visited.save()

        profile, profile_is_new = Profile.objects.get_or_create(user=request.user)
        starred = review_request in profile.starred_review_requests.all()

        # Unlike review above, this covers replies as well.
        try:
            last_draft_review = Review.objects.filter(
                review_request=review_request,
                user=request.user,
                public=False).latest()
            review_timestamp = last_draft_review.timestamp
        except Review.DoesNotExist:
            pass


    draft = review_request.get_draft(request.user)

    # Find out if we can bail early. Generate an ETag for this.
    last_activity_time, updated_object = review_request.get_last_activity()

    if draft:
        draft_timestamp = draft.last_updated
    else:
        draft_timestamp = ""

    etag = "%s:%s:%s:%s:%s:%s" % (request.user, last_activity_time,
                                  draft_timestamp, review_timestamp,
                                  int(starred),
                                  settings.AJAX_SERIAL)

    if etag_if_none_match(request, etag):
        return HttpResponseNotModified()

    changedescs = review_request.changedescs.filter(public=True)
    latest_changedesc = None

    try:
        latest_changedesc = changedescs.latest()
        latest_timestamp = latest_changedesc.timestamp
    except ChangeDescription.DoesNotExist:
        latest_timestamp = None

    entries = []

    for temp_review in reviews:
        temp_review.ordered_comments = \
            temp_review.comments.order_by('filediff', 'first_line')

        state = ''

        # Mark as collapsed if the review is older than the latest change
        if latest_timestamp and temp_review.timestamp < latest_timestamp:
            state = 'collapsed'

        try:
            latest_reply = temp_review.public_replies().latest('timestamp').timestamp
        except Review.DoesNotExist:
            latest_reply = None

        # Mark as expanded if there is a reply newer than last_visited
        if latest_reply and last_visited and last_visited < latest_reply:
          state = ''

        entries.append({
            'review': temp_review,
            'timestamp': temp_review.timestamp,
            'class': state,
        })

    for changedesc in changedescs:
        fields_changed = []

        for name, info in changedesc.fields_changed.items():
            multiline = False

            if 'added' in info or 'removed' in info:
                change_type = 'add_remove'

                # We don't hard-code URLs in the bug info, since the
                # tracker may move, but we can do it here.
                if (name == "bugs_closed" and
                    review_request.repository and
                    review_request.repository.bug_tracker):
                    bug_url = review_request.repository.bug_tracker
                    for field in info:
                        for i, buginfo in enumerate(info[field]):
                            try:
                                full_bug_url = bug_url % buginfo[0]
                                info[field][i] = (buginfo[0], full_bug_url)
                            except TypeError:
                                logging.warning("Invalid bugtracker url format")

            elif 'old' in info or 'new' in info:
                change_type = 'changed'
                multiline = (name == "description" or name == "testing_done")

                # Branch text is allowed to have entities, so mark it safe.
                if name == "branch":
                    if 'old' in info:
                        info['old'][0] = mark_safe(info['old'][0])

                    if 'new' in info:
                        info['new'][0] = mark_safe(info['new'][0])

                # Make status human readable.
                if name == 'status':
                    if 'old' in info:
                        info['old'][0] = status_to_string(info['old'][0])

                    if 'new' in info:
                        info['new'][0] = status_to_string(info['new'][0])

            elif name == "screenshot_captions":
                change_type = 'screenshot_captions'
            elif name == "file_captions":
                change_type = 'file_captions'
            else:
                # No clue what this is. Bail.
                continue

            fields_changed.append({
                'title': fields_changed_name_map.get(name, name),
                'multiline': multiline,
                'info': info,
                'type': change_type,
            })

        # Expand the latest review change
        state = ''

        # Mark as collapsed if the change is older than a newer change
        if latest_timestamp and changedesc.timestamp < latest_timestamp:
            state = 'collapsed'

        entries.append({
            'changeinfo': fields_changed,
            'changedesc': changedesc,
            'timestamp': changedesc.timestamp,
            'class': state,
        })

    entries.sort(key=lambda item: item['timestamp'])

    close_description = ''

    if latest_changedesc and 'status' in latest_changedesc.fields_changed:
        status = latest_changedesc.fields_changed['status']['new'][0]

        if status in (ReviewRequest.DISCARDED, ReviewRequest.SUBMITTED):
            close_description = latest_changedesc.text

    issues = {
        'total': 0,
        'open': 0,
        'resolved': 0,
        'dropped': 0
    }

    for entry in entries:
        if 'review' in entry:
            for comment in entry['review'].get_all_comments(issue_opened=True):
                issues['total'] += 1
                issues[BaseComment.issue_status_to_string(
                        comment.issue_status)] += 1

    response = render_to_response(
        template_name,
        RequestContext(request, _make_review_request_context(review_request, {
            'draft': draft,
            'detail_hooks': ReviewRequestDetailHook.hooks,
            'review_request_details': draft or review_request,
            'entries': entries,
            'last_activity_time': last_activity_time,
            'review': review,
            'request': request,
            'latest_changedesc': latest_changedesc,
            'close_description': close_description,
            'PRE_CREATION': PRE_CREATION,
            'issues': issues,
        })))
    set_etag(response, etag)

    return response
Beispiel #23
0
 def serialize_issue_status_field(self, obj, **kwargs):
     return BaseComment.issue_status_to_string(obj.issue_status)
Beispiel #24
0
def commentcounts(context, filediff, interfilediff=None):
    """
    Returns a JSON 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 text of 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 = {}
    user = context.get("user", None)

    if interfilediff:
        query = Comment.objects.filter(filediff=filediff, interfilediff=interfilediff)
    else:
        query = Comment.objects.filter(filediff=filediff, interfilediff__isnull=True)

    for comment in query:
        review = get_object_or_none(comment.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": comment.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,
                    },
                    #'timestamp': comment.timestamp,
                    "url": comment.get_review_url(),
                    "localdraft": review.user == user and not review.public,
                    "review_id": review.id,
                    "review_request_id": review.review_request.id,
                    "issue_opened": comment.issue_opened,
                    "issue_status": BaseComment.issue_status_to_string(comment.issue_status),
                }
            )

    comments_array = []

    for key, value in comment_dict.iteritems():
        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 simplejson.dumps(comments_array)
Beispiel #25
0
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
Beispiel #26
0
def commentcounts(context, filediff, interfilediff=None):
    """
    Returns a JSON 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 text of 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 = {}
    user = context.get('user', None)
    all_comments = context.get('comments', {})

    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':
                escape(comment.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),
            })

    comments_array = []

    for key, value in comment_dict.iteritems():
        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 simplejson.dumps(comments_array)
Beispiel #27
0
 def serialize_issue_status_field(self, obj, **kwargs):
     return BaseComment.issue_status_to_string(obj.issue_status)
Beispiel #28
0
def pretty_print_issue_status(status):
    """Turns an issue status code into a human-readable status string."""
    return BaseComment.issue_status_to_string(status)