Exemple #1
0
    def renderHeaderItems(target):
        has_draft_items = review_utils.renderDraftItems(db, user, review, target)

        target = target.div("buttons")

        if not has_draft_items:
            if review.state == "open":
                if review.accepted(db):
                    target.button(id="closeReview", onclick="closeReview();").text("Close Review")
                else:
                    if user in review.owners or user.getPreference(db, "review.pingAnyReview"):
                        target.button(id="pingReview", onclick="pingReview();").text("Ping Review")
                    if user in review.owners or user.getPreference(db, "review.dropAnyReview"):
                        target.button(id="dropReview", onclick="dropReview();").text("Drop Review")

                if user in review.owners and not review.description:
                    target.button(id="writeDescription", onclick="editDescription();").text("Write Description")
            else:
                target.button(id="reopenReview", onclick="reopenReview();").text("Reopen Review")

        target.span("buttonscope buttonscope-global")
Exemple #2
0
 def generateRight(target):
     review_utils.renderDraftItems(db, user, review, target)
Exemple #3
0
def renderShowComments(req, db, user):
    context_lines = req.getParameter("context", user.getPreference(db, "comment.diff.contextLines"), filter=int)

    default_compact = "yes" if user.getPreference(db, "commit.diff.compactMode") else "no"
    compact = req.getParameter("compact", default_compact) == "yes"

    default_tabify = "yes" if user.getPreference(db, "commit.diff.visualTabs") else "no"
    tabify = req.getParameter("tabify", default_tabify) == "yes"

    original = req.getParameter("original", "no") == "yes"

    review_id = req.getParameter("review", filter=int)
    batch_id = req.getParameter("batch", None, filter=int)
    filter = req.getParameter("filter", "all")
    blame = req.getParameter("blame", None)

    profiler = profiling.Profiler()

    review = dbutils.Review.fromId(db, review_id)
    review.repository.enableBlobCache()

    cursor = db.cursor()

    profiler.check("create review")

    if blame is not None:
        blame_user = dbutils.User.fromName(db, blame)

        cursor.execute("""SELECT commentchains.id
                            FROM commentchains
                            JOIN commentchainlines ON (commentchainlines.chain=commentchains.id)
                            JOIN fileversions ON (fileversions.new_sha1=commentchainlines.sha1)
                            JOIN changesets ON (changesets.id=fileversions.changeset)
                            JOIN commits ON (commits.id=changesets.child)
                            JOIN gitusers ON (gitusers.id=commits.author_gituser)
                            JOIN usergitemails USING (email)
                            JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id AND reviewchangesets.review=commentchains.review)
                           WHERE commentchains.review=%s
                             AND usergitemails.uid=%s
                             AND commentchains.state!='empty'
                             AND (commentchains.state!='draft' OR commentchains.uid=%s)
                        ORDER BY commentchains.file, commentchainlines.first_line""",
                       (review.id, blame_user.id, user.id))

        include_chain_ids = set([chain_id for (chain_id,) in cursor])

        profiler.check("initial blame filtering")
    else:
        include_chain_ids = None

    if filter == "toread":
        query = """SELECT commentchains.id
                     FROM commentchains
                     JOIN comments ON (comments.chain=commentchains.id)
                     JOIN commentstoread ON (commentstoread.comment=comments.id)
          LEFT OUTER JOIN commentchainlines ON (commentchainlines.chain=commentchains.id)
                    WHERE review=%s
                      AND commentstoread.uid=%s
                 ORDER BY file, first_line"""

        cursor.execute(query, (review.id, user.id))
    else:
        query = """SELECT commentchains.id
                     FROM commentchains
          LEFT OUTER JOIN commentchainlines ON (chain=id)
                    WHERE review=%s
                      AND commentchains.state!='empty'"""

        arguments = [review.id]

        if filter == "issues":
            query += " AND type='issue' AND (commentchains.state!='draft' OR commentchains.uid=%s)"
            arguments.append(user.id)
        elif filter == "draft-issues":
            query += " AND type='issue' AND commentchains.state='draft' AND commentchains.uid=%s"
            arguments.append(user.id)
        elif filter == "open-issues":
            query += " AND type='issue' AND commentchains.state='open'"
        elif filter == "addressed-issues":
            query += " AND type='issue' AND commentchains.state='addressed'"
        elif filter == "closed-issues":
            query += " AND type='issue' AND commentchains.state='closed'"
        elif filter == "notes":
            query += " AND type='note' AND (commentchains.state!='draft' OR commentchains.uid=%s)"
            arguments.append(user.id)
        elif filter == "draft-notes":
            query += " AND type='note' AND commentchains.state='draft' AND commentchains.uid=%s"
            arguments.append(user.id)
        elif filter == "open-notes":
            query += " AND type='note' AND commentchains.state='open'"
        else:
            query += " AND (commentchains.state!='draft' OR commentchains.uid=%s)"
            arguments.append(user.id)

        if batch_id is not None:
            query += " AND batch=%s"
            arguments.append(batch_id)

        # This ordering is inaccurate if comments apply to the same file but
        # different commits, but then, in that case there isn't really a
        # well-defined natural order either.  Two comments that apply to the
        # same file and commit will at least be order by line number, and that's
        # better than nothing.
        query += " ORDER BY file, first_line"

        cursor.execute(query, arguments)

    profiler.check("main query")

    if include_chain_ids is None:
        chain_ids = [chain_id for (chain_id,) in cursor]
    else:
        chain_ids = [chain_id for (chain_id,) in cursor if chain_id in include_chain_ids]

    profiler.check("query result")

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    document.addInternalScript(user.getJS(db))
    document.addInternalScript(review.getJS())

    page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review")])

    profiler.check("page header")

    target = body.div("main")

    if chain_ids and not user.isAnonymous() and user.name == req.user:
        document.addInternalScript("$(function () { markChainsAsRead([%s]); });" % ", ".join(map(str, chain_ids)))

    if chain_ids:
        processed = set()

        chains = []
        file_ids = set()
        changesets_files = {}
        changesets = {}

        if blame is not None:
            annotators = {}
            review.branch.loadCommits(db)
            commits = log.commitset.CommitSet(review.branch.commits)

        for chain_id in chain_ids:
            if chain_id in processed:
                continue
            else:
                processed.add(chain_id)

                chain = review_comment.CommentChain.fromId(db, chain_id, user, review=review)
                chains.append(chain)

                if chain.file_id is not None:
                    file_ids.add(chain.file_id)
                    parent, child = review_html.getCodeCommentChainChangeset(db, chain, original)
                    if parent and child:
                        changesets_files.setdefault((parent, child), set()).add(chain.file_id)

        profiler.check("load chains")

        changeset_cache = {}

        for (from_commit, to_commit), filtered_file_ids in changesets_files.items():
            changesets[(from_commit, to_commit)] = changeset_utils.createChangeset(db, user, review.repository, from_commit=from_commit, to_commit=to_commit, filtered_file_ids=filtered_file_ids)[0]
            profiler.check("create changesets")

            if blame is not None:
                annotators[(from_commit, to_commit)] = operation.blame.LineAnnotator(db, from_commit, to_commit, file_ids=file_ids, commits=commits, changeset_cache=changeset_cache)
                profiler.check("create annotators")

        for chain in chains:
            if blame is not None and chain.file_id is not None:
                try:
                    changeset = changesets[(chain.first_commit, chain.last_commit)]
                    annotator = annotators[(chain.first_commit, chain.last_commit)]
                except KeyError:
                    # Most likely a comment created via /showfile.  Such a
                    # comment could be in code that 'blame_user' modified in the
                    # review, but for now, let's skip the comment.
                    continue
                else:
                    file_in_changeset = changeset.getFile(chain.file_id)

                    if not file_in_changeset:
                        continue

                    try:
                        offset, count = chain.lines_by_sha1[file_in_changeset.new_sha1]
                    except KeyError:
                        # Probably a chain raised against the "old" side of the diff.
                        continue
                    else:
                        if not annotator.annotate(chain.file_id, offset, offset + count - 1, check_user=blame_user):
                            continue

            profiler.check("detailed blame filtering")

            if chain.file_id is not None:
                from_commit, to_commit = review_html.getCodeCommentChainChangeset(db, chain, original)
                changeset = changesets.get((from_commit, to_commit))
            else:
                changeset = None

            review_html.renderCommentChain(db, target, user, review, chain,
                                           context_lines=context_lines,
                                           compact=compact,
                                           tabify=tabify,
                                           original=original,
                                           changeset=changeset,
                                           linkify=linkify.Context(db=db, request=req, review=review))

            profiler.check("rendering")

            yield document.render(stop=target, pretty=not compact) + "<script>console.log((new Date).toString());</script>"

            profiler.check("transfer")

        page.utils.renderShortcuts(target, "showcomments")
    else:
        target.h1(align="center").text("No comments.")

    profiler.output(db, user, document)

    yield document.render(pretty=not compact)
Exemple #4
0
def renderShowBatch(req, db, user):
    batch_id = page.utils.getParameter(req, "batch", None, filter=int)
    review_id = page.utils.getParameter(req, "review", None, filter=int)

    cursor = db.cursor()

    if batch_id is None and review_id is None:
        return page.utils.displayMessage(db, req, user, "Missing argument: 'batch'")

    if batch_id:
        cursor.execute("SELECT review, uid, comment FROM batches WHERE id=%s", (batch_id,))

        row = cursor.fetchone()
        if not row:
            raise page.utils.DisplayMessage("Invalid batch ID: %d" % batch_id)

        review_id, author_id, chain_id = row
        author = dbutils.User.fromId(db, author_id)
    else:
        chain_id = None
        author = user

    review = dbutils.Review.fromId(db, review_id)

    if chain_id:
        batch_chain = review_comment.CommentChain.fromId(db, chain_id, user, review=review)
        batch_chain.loadComments(db, user)
    else:
        batch_chain = None

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review")])

    document.addExternalStylesheet("resource/showreview.css")
    document.addExternalStylesheet("resource/showbatch.css")
    document.addExternalStylesheet("resource/review.css")
    document.addExternalStylesheet("resource/comment.css")
    document.addExternalScript("resource/review.js")
    document.addExternalScript("resource/comment.js")
    document.addInternalScript(user.getJS())
    document.addInternalScript(review.getJS())

    if batch_chain:
        document.addInternalScript("commentChainById[%d] = %s;" % (batch_chain.id, batch_chain.getJSConstructor()))

    target = body.div("main")

    table = target.table('paleyellow basic comments', align='center')
    table.col(width='10%')
    table.col(width='80%')
    table.col(width='10%')
    table.tr().td('h1', colspan=3).h1().text("Review by %s" % htmlify(author.fullname))

    if batch_chain:
        batch_chain.loadComments(db, user)

        row = table.tr("line")
        row.td("heading").text("Comment:")
        row.td("value").preformatted().div("text").text(htmlify(batch_chain.comments[0].comment))
        row.td("status").text()

    def renderFiles(title, cursor):
        files = []

        for file_id, delete_count, insert_count in cursor.fetchall():
            files.append((dbutils.describe_file(db, file_id), delete_count, insert_count))

        paths = []
        deleted = []
        inserted = []

        for path, delete_count, insert_count in sorted(files):
            paths.append(path)
            deleted.append(delete_count)
            inserted.append(insert_count)

        if paths:
            diff.File.eliminateCommonPrefixes(paths)

            row = table.tr("line")
            row.td("heading").text(title)

            files_table = row.td().table("files callout")
            headers = files_table.thead().tr()
            headers.th("path").text("Changed Files")
            headers.th("lines", colspan=2).text("Lines")

            files = files_table.tbody()
            for path, delete_count, insert_count in zip(paths, deleted, inserted):
                file = files.tr()
                file.td("path").preformatted().innerHTML(path)
                file.td("lines").preformatted().text("-%d" % delete_count if delete_count else None)
                file.td("lines").preformatted().text("+%d" % insert_count if insert_count else None)

            row.td("status").text()

    def condition(table_name):
        if batch_id:
            return "%s.batch=%d" % (table_name, batch_id)
        else:
            return "review=%d AND %s.batch IS NULL AND %s.uid=%d" % (review.id, table_name, table_name, author.id)

    cursor.execute("""SELECT reviewfiles.file, SUM(deleted), SUM(inserted)
                        FROM reviewfiles
                        JOIN reviewfilechanges ON (reviewfilechanges.file=reviewfiles.id)
                       WHERE %s
                         AND reviewfilechanges.to_state='reviewed'
                    GROUP BY reviewfiles.file""" % condition("reviewfilechanges"))
    renderFiles("Reviewed:", cursor)

    cursor.execute("""SELECT reviewfiles.file, SUM(deleted), SUM(inserted)
                        FROM reviewfiles
                        JOIN reviewfilechanges ON (reviewfilechanges.file=reviewfiles.id)
                       WHERE %s
                         AND reviewfilechanges.to_state='pending'
                    GROUP BY reviewfiles.file""" % condition("reviewfilechanges"))
    renderFiles("Unreviewed:", cursor)

    def renderChains(title, cursor, replies):
        all_chains = [review_comment.CommentChain.fromId(db, chain_id, user, review=review)
                      for (chain_id,) in cursor]

        if not all_chains:
            return

        for chain in all_chains:
            chain.loadComments(db, user)

        issue_chains = filter(lambda chain: chain.type == "issue", all_chains)
        draft_issues = filter(lambda chain: chain.state == "draft", issue_chains)
        open_issues = filter(lambda chain: chain.state == "open", issue_chains)
        addressed_issues = filter(lambda chain: chain.state == "addressed", issue_chains)
        closed_issues = filter(lambda chain: chain.state == "closed", issue_chains)
        note_chains = filter(lambda chain: chain.type == "note", all_chains)
        draft_notes = filter(lambda chain: chain.state == "draft" and chain != batch_chain, note_chains)
        open_notes = filter(lambda chain: chain.state == "open" and chain != batch_chain, note_chains)

        def renderChains(target, chains):
            for chain in chains:
                row = target.tr("comment")
                row.td("author").text(chain.user.fullname)
                row.td("title").a(href="showcomment?chain=%d" % chain.id).innerHTML(chain.leader())
                row.td("when").text(chain.when())

        def showcomments(filter_param):
            params = { "review": review.id, "filter": filter_param }
            if batch_id:
                params["batch"] = batch_id
            return htmlutils.URL("/showcomments", **params)

        if draft_issues or open_issues or addressed_issues or closed_issues:
            h2 = table.tr().td("h2", colspan=3).h2().text(title)
            if len(draft_issues) + len(open_issues) + len(addressed_issues) + len(closed_issues) > 1:
                h2.a(href=showcomments("issues")).text("[display all]")

            if draft_issues:
                h3 = table.tr(id="draft-issues").td("h3", colspan=3).h3().text("Draft issues")
                if len(draft_issues) > 1:
                    h3.a(href=showcomments("draft-issues")).text("[display all]")
                renderChains(table, draft_issues)

            if batch_id is not None or replies:
                if open_issues:
                    h3 = table.tr(id="open-issues").td("h3", colspan=3).h3().text("Still open issues")
                    if batch_id and len(open_issues) > 1:
                        h3.a(href=showcomments("open-issues")).text("[display all]")
                    renderChains(table, open_issues)

                if addressed_issues:
                    h3 = table.tr(id="addressed-issues").td("h3", colspan=3).h3().text("Now addressed issues")
                    if batch_id and len(addressed_issues) > 1:
                        h3.a(href=showcomments("addressed-issues")).text("[display all]")
                    renderChains(table, addressed_issues)

                if closed_issues:
                    h3 = table.tr(id="closed-issues").td("h3", colspan=3).h3().text("Now closed issues")
                    if batch_id and len(closed_issues) > 1:
                        h3.a(href=showcomments("closed-issues")).text("[display all]")
                    renderChains(table, closed_issues)

        if draft_notes or open_notes:
            h2 = table.tr().td("h2", colspan=3).h2().text(title)
            if len(draft_notes) + len(open_notes) > 1:
                h2.a(href=showcomments("notes")).text("[display all]")

            if draft_notes:
                h3 = table.tr(id="draft-notes").td("h3", colspan=3).h3().text("Draft notes")
                if len(draft_notes) > 1:
                    h3.a(href=showcomments("draft-notes")).text("[display all]")
                renderChains(table, draft_notes)

            if open_notes:
                h3 = table.tr(id="notes").td("h3", colspan=3).h3().text("Notes")
                if batch_id and len(open_notes) > 1:
                    h3.a(href=showcomments("open-notes")).text("[display all]")
                renderChains(table, open_notes)

    cursor.execute("SELECT id FROM commentchains WHERE %s AND type='issue'" % condition("commentchains"))

    renderChains("Raised issues", cursor, False)

    cursor.execute("""SELECT commentchains.id
                        FROM commentchains
                        JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id)
                       WHERE %s
                         AND to_state='closed'""" % condition("commentchainchanges"))

    renderChains("Resolved issues", cursor, False)

    cursor.execute("""SELECT commentchains.id
                        FROM commentchains
                        JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id)
                       WHERE %s
                         AND to_state='open'""" % condition("commentchainchanges"))

    renderChains("Reopened issues", cursor, False)

    cursor.execute("""SELECT commentchains.id
                        FROM commentchains
                        JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id)
                       WHERE %s
                         AND to_type='issue'""" % condition("commentchainchanges"))

    renderChains("Converted into issues", cursor, False)

    cursor.execute("""SELECT commentchains.id
                        FROM commentchains
                        JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id)
                       WHERE %s
                         AND to_type='note'""" % condition("commentchainchanges"))

    renderChains("Converted into notes", cursor, False)

    cursor.execute("SELECT id FROM commentchains WHERE %s AND type='note'" % condition("commentchains"))

    renderChains("Written notes", cursor, False)

    cursor.execute("""SELECT commentchains.id
                        FROM commentchains
                        JOIN comments ON (comments.chain=commentchains.id)
                       WHERE %s
                         AND comments.id!=commentchains.first_comment""" % condition("comments"))

    renderChains("Replied to", cursor, True)

    return document
Exemple #5
0
def renderShowFile(req, db, user):
    cursor = db.cursor()

    sha1 = req.getParameter("sha1")
    path = req.getParameter("path")
    line = req.getParameter("line", None)
    review_id = req.getParameter("review", None, filter=int)

    default_tabify = "yes" if user.getPreference(db, "commit.diff.visualTabs") else "no"
    tabify = req.getParameter("tabify", default_tabify) == "yes"

    if line is None:
        first, last = None, None
    else:
        if "-" in line:
            first, last = map(int, line.split("-"))
        else:
            first = last = int(line)

        context = req.getParameter("context", user.getPreference(db, "commit.diff.contextLines"), int)

        first_with_context = max(1, first - context)
        last_with_context = last + context

    if user.getPreference(db, "commit.diff.compactMode"): default_compact = "yes"
    else: default_compact = "no"

    compact = req.getParameter("compact", default_compact) == "yes"

    if len(path) == 0 or path[-1:] == "/":
        raise page.utils.DisplayMessage(
            title="Invalid path parameter",
            body="<p>The path must be non-empty and must not end with a <code>/</code>.</p>",
            html=True)
    if path[0] == '/':
        full_path = path
        if path != "/": path = path[1:]
    else:
        full_path = "/" + path
        if not path: path = "/"

    if review_id is None:
        review = None
        repository_arg = req.getParameter("repository", "")
        if repository_arg:
            repository = gitutils.Repository.fromParameter(db, repository_arg)
        else:
            repository = gitutils.Repository.fromSHA1(db, sha1)
    else:
        review = dbutils.Review.fromId(db, review_id)
        repository = review.repository

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    if review:
        page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review")])
    else:
        page.utils.generateHeader(body, db, user)

    document.addExternalStylesheet("resource/showfile.css")
    document.addInternalStylesheet(htmlutils.stripStylesheet(user.getResource(db, "syntax.css")[1], compact))

    commit = gitutils.Commit.fromSHA1(db, repository, sha1)
    file_sha1 = commit.getFileSHA1(full_path)
    file_id = dbutils.find_file(db, path=path)

    if file_sha1 is None:
        raise page.utils.DisplayMessage(
            title="File does not exist",
            body=("<p>There is no file named <code>%s</code> in the commit "
                  "<a href='/showcommit?repository=%s&amp;sha1=%s'>"
                  "<code>%s</code></a>.</p>"
                  % (htmlutils.htmlify(textutils.escape(full_path)),
                     htmlutils.htmlify(repository.name),
                     htmlutils.htmlify(sha1), htmlutils.htmlify(sha1[:8]))),
            html=True)

    file = diff.File(file_id, path, None, file_sha1, repository)

    # A new file ID might have been added to the database, so need to commit.
    db.commit()

    if file.canHighlight():
        requestHighlights(repository, { file.new_sha1: (file.path, file.getLanguage()) })

    file.loadNewLines(True, request_highlight=True)

    if review:
        document.addInternalScript(user.getJS())
        document.addInternalScript(review.getJS())
        document.addInternalScript("var changeset = { parent: { id: %(id)d, sha1: %(sha1)r }, child: { id: %(id)d, sha1: %(sha1)r } };" % { 'id': commit.getId(db), 'sha1': commit.sha1 })
        document.addInternalScript("var files = { %(id)d: { new_sha1: %(sha1)r }, %(sha1)r: { id: %(id)d, side: 'n' } };" % { 'id': file_id, 'sha1': file_sha1 })
        document.addExternalStylesheet("resource/review.css")
        document.addExternalScript("resource/review.js")

        cursor.execute("""SELECT DISTINCT commentchains.id
                            FROM commentchains
                            JOIN commentchainlines ON (commentchainlines.chain=commentchains.id)
                           WHERE commentchains.review=%s
                             AND commentchains.file=%s
                             AND commentchainlines.sha1=%s
                             AND ((commentchains.state!='draft' OR commentchains.uid=%s)
                              AND commentchains.state!='empty')
                        GROUP BY commentchains.id""",
                       (review.id, file_id, file_sha1, user.id))

        comment_chain_script = ""

        for (chain_id,) in cursor.fetchall():
            chain = review_comment.CommentChain.fromId(db, chain_id, user, review=review)
            chain.loadComments(db, user)

            comment_chain_script += "commentChains.push(%s);\n" % chain.getJSConstructor(file_sha1)

        if comment_chain_script:
            document.addInternalScript(comment_chain_script)

    document.addExternalStylesheet("resource/comment.css")
    document.addExternalScript("resource/comment.js")
    document.addExternalScript("resource/showfile.js")

    if tabify:
        document.addExternalStylesheet("resource/tabify.css")
        document.addExternalScript("resource/tabify.js")
        tabwidth = file.getTabWidth()
        indenttabsmode = file.getIndentTabsMode()

    if user.getPreference(db, "commit.diff.highlightIllegalWhitespace"):
        document.addInternalStylesheet(user.getResource(db, "whitespace.css")[1], compact)

    if first is not None:
        document.addInternalScript("var firstSelectedLine = %d, lastSelectedLine = %d;" % (first, last))

    target = body.div("main")

    if tabify:
        target.script(type="text/javascript").text("calculateTabWidth();")

    table = target.table('file show expanded paleyellow', align='center', cellspacing=0)

    columns = table.colgroup()
    columns.col('edge')
    columns.col('linenr')
    columns.col('line')
    columns.col('middle')
    columns.col('middle')
    columns.col('line')
    columns.col('linenr')
    columns.col('edge')

    thead = table.thead()
    cell = thead.tr().td('h1', colspan=8)
    h1 = cell.h1()

    def make_url(url_path, path):
        params = { "sha1": sha1,
                   "path": path }
        if review is None:
            params["repository"] = str(repository.id)
        else:
            params["review"] = str(review.id)
        return "%s?%s" % (url_path, urllib.urlencode(params))

    h1.a("root", href=make_url("showtree", "/")).text("root")
    h1.span().text('/')

    components = path.split("/")
    for index, component in enumerate(components[:-1]):
        h1.a(href=make_url("showtree", "/".join(components[:index + 1]))).text(component, escape=True)
        h1.span().text('/')

    if first is not None:
        h1.a(href=make_url("showfile", "/".join(components))).text(components[-1], escape=True)
    else:
        h1.text(components[-1], escape=True)

    h1.span("right").a(href=("/download/%s?repository=%s&sha1=%s"
                             % (urllib.quote(path), repository.name, file_sha1)),
                       download=urllib.quote(path)).text("[download]")
    h1.span("right").a(href=("/download/%s?repository=%s&sha1=%s"
                             % (urllib.quote(path), repository.name, file_sha1))).text("[view]")

    table.tbody('spacer top').tr('spacer top').td(colspan=8).text()

    tbody = table.tbody("lines")

    yield document.render(stop=tbody, pretty=not compact)

    for linenr, line in enumerate(file.newLines(True)):
        linenr = linenr + 1
        highlight_class = ""

        if first is not None:
            if not (first_with_context <= linenr <= last_with_context): continue
            if linenr == first:
                highlight_class += " first-selected"
            if linenr == last:
                highlight_class += " last-selected"

        if tabify:
            line = htmlutils.tabify(line, tabwidth, indenttabsmode)

        line = line.replace("\r", "<i class='cr'></i>")

        row = tbody.tr("line context single", id="f%do%dn%d" % (file.id, linenr, linenr))
        row.td("edge").text()
        row.td("linenr old").text(linenr)
        row.td("line single whole%s" % highlight_class, id="f%dn%d" % (file.id, linenr), colspan=4).innerHTML(line)
        row.td("linenr new").text(linenr)
        row.td("edge").text()

        if linenr % 500:
            yield document.render(stop=tbody, pretty=not compact)

    table.tbody('spacer bottom').tr('spacer bottom').td(colspan=8).text()

    yield document.render(pretty=not compact)
Exemple #6
0
 def renderHeaderItems(target):
     review_utils.renderDraftItems(db, user, review, target)
     target.div("buttons").span("buttonscope buttonscope-global")
Exemple #7
0
def renderFilterChanges(req, db, user):
    review_id = req.getParameter("review", filter=int)
    first_sha1 = req.getParameter("first", None)
    last_sha1 = req.getParameter("last", None)

    cursor = db.cursor()

    review = dbutils.Review.fromId(db, review_id)

    root_directories = {}
    root_files = {}

    def processFile(file_id):
        components = dbutils.describe_file(db, file_id).split("/")
        directories, files = root_directories, root_files
        for directory_name in components[:-1]:
            directories, files = directories.setdefault(directory_name, ({}, {}))
        files[components[-1]] = file_id

    if first_sha1 and last_sha1:
        cursor.execute("""SELECT commits.sha1
                            FROM commits
                            JOIN changesets ON (changesets.child=commits.id)
                            JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id)
                           WHERE reviewchangesets.review=%s""",
                       (review.id,))

        first_commit = gitutils.Commit.fromSHA1(db, review.repository, first_sha1)
        last_commit = gitutils.Commit.fromSHA1(db, review.repository, last_sha1)

        if len(first_commit.parents) > 1:
            raise page.utils.DisplayMessage(
                title="Filtering failed!",
                body=("First selected commit is a merge commit.  Please go back "
                      "and select a different range of commits."),
                review=review)

        if first_commit.parents:
            from_commit = gitutils.Commit.fromSHA1(db, review.repository, first_commit.parents[0])
        else:
            from_commit = None

        to_commit = last_commit
        commits = log.commitset.CommitSet.fromRange(db, from_commit, to_commit)

        if not commits:
            raise page.utils.DisplayMessage(
                title="Filtering failed!",
                body=("The range of commits selected includes merges with "
                      "ancestors not included in the range.  Please go back "
                      "and select a different range of commits."),
                review=review)

        cursor.execute("""SELECT DISTINCT reviewfiles.file
                            FROM reviewfiles
                            JOIN changesets ON (changesets.id=reviewfiles.changeset)
                            JOIN commits ON (commits.id=changesets.child)
                           WHERE reviewfiles.review=%s
                             AND commits.sha1=ANY (%s)""",
                       (review.id, [commit.sha1 for commit in commits]))
    else:
        cursor.execute("SELECT DISTINCT file FROM reviewfiles WHERE review=%s", (review.id,))

    for (file_id,) in cursor:
        processFile(file_id)

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review")])

    document.addExternalStylesheet("resource/filterchanges.css")
    document.addExternalScript("resource/filterchanges.js")
    document.addInternalScript(user.getJS())
    document.addInternalScript(review.getJS())

    if first_sha1 and last_sha1:
        document.addInternalScript("var commitRange = { first: %s, last: %s };" % (htmlutils.jsify(first_sha1), htmlutils.jsify(last_sha1)))
    else:
        document.addInternalScript("var commitRange = null;")

    target = body.div("main")

    basic = target.table('filter paleyellow', align='center', cellspacing=0)
    basic.col(width='10%')
    basic.col(width='60%')
    basic.col(width='30%')
    row = basic.tr("header")
    row.td('h1', colspan=2).h1().text("Filter Changes")
    row.td('h1 button').button("display").text("Display Diff")

    row = basic.tr("headings")
    row.td("select").text("Include")
    row.td("path").text("Path")
    row.td().text()

    def outputDirectory(base, name, directories, files):
        if name:
            level = base.count("/")
            row = basic.tr("directory", critic_level=level)
            row.td("select").input(type="checkbox")
            if level > 1:
                row.td("path").preformatted().innerHTML((" " * (len(base) - 2)) + "&#8230;/" + name + "/")
            else:
                row.td("path").preformatted().innerHTML(base + name + "/")
            row.td().text()
        else:
            row = basic.tr("directory", critic_level=-1)
            row.td("select").input(type="checkbox")
            row.td("path").preformatted().i().text("Everything")
            row.td().text()
            level = -1

        for directory_name in sorted(directories.keys()):
            outputDirectory(base + name + "/" if name else "", directory_name, directories[directory_name][0], directories[directory_name][1])

        for file_name in sorted(files.keys()):
            row = basic.tr("file", critic_file_id=files[file_name], critic_level=level + 1)
            row.td("select").input(type="checkbox")
            if level > -1:
                row.td("path").preformatted().innerHTML((" " * (len(base + name) - 1)) + "&#8230;/" + htmlutils.htmlify(file_name))
            else:
                row.td("path").preformatted().innerHTML(htmlutils.htmlify(file_name))
            row.td().text()

    outputDirectory("", "", root_directories, root_files)

    row = basic.tr("footer")
    row.td('spacer', colspan=3)

    row = basic.tr("footer")
    row.td('button', colspan=3).button("display").text("Display Diff")

    if user.getPreference(db, "ui.keyboardShortcuts"):
        page.utils.renderShortcuts(body, "filterchanges", review=review)

    return document
Exemple #8
0
def renderManageReviewers(req, db, user):
    review_id = req.getParameter("review", filter=int)

    cursor = db.cursor()

    review = dbutils.Review.fromId(db, review_id)

    root_directories = {}
    root_files = {}

    def processFile(file_id):
        components = dbutils.describe_file(db, file_id).split("/")
        directories, files = root_directories, root_files
        for directory_name in components[:-1]:
            directories, files = directories.setdefault(directory_name, ({}, {}))
        files[components[-1]] = file_id

    cursor.execute("SELECT file FROM reviewfiles WHERE review=%s", (review.id,))

    for (file_id,) in cursor:
        processFile(file_id)

    cursor.execute("SELECT name FROM users WHERE name IS NOT NULL")
    users = [user_name for (user_name,) in cursor if user_name]

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review")])

    document.addExternalStylesheet("resource/managereviewers.css")
    document.addExternalScript("resource/managereviewers.js")
    document.addInternalScript(user.getJS());
    document.addInternalScript(review.getJS());
    document.addInternalScript("var users = [ %s ];" % ", ".join([htmlutils.jsify(user_name) for user_name in sorted(users)]))

    target = body.div("main")

    basic = target.table('manage paleyellow', align='center')
    basic.col(width='10%')
    basic.col(width='60%')
    basic.col(width='30%')
    basic.tr().td('h1', colspan=3).h1().text("Manage Reviewers")

    row = basic.tr("current")
    row.td("select").text("Current:")
    cell = row.td("value")
    for index, reviewer in enumerate(review.reviewers):
        if index != 0: cell.text(", ")
        cell.span("reviewer", critic_username=reviewer.name).innerHTML(htmlutils.htmlify(reviewer.fullname).replace(" ", "&nbsp;"))
    row.td("right").text()

    row = basic.tr("reviewer")
    row.td("select").text("Reviewer:")
    row.td("value").input("reviewer").span("message")
    row.td("right").button("save").text("Save")

    row = basic.tr("help")
    row.td("help", colspan=3).text("Enter the name of a current reviewer to edit assignments (or unassign.)  Enter the name of another user to add a new reviewer.")

    row = basic.tr("headings")
    row.td("select").text("Assigned")
    row.td("path").text("Path")
    row.td().text()

    def outputDirectory(base, name, directories, files):
        if name:
            level = base.count("/")
            row = basic.tr("directory", critic_level=level)
            row.td("select").input(type="checkbox")
            if level > 1:
                row.td("path").preformatted().innerHTML((" " * (len(base) - 2)) + "&#8230;/" + name + "/")
            else:
                row.td("path").preformatted().innerHTML(base + name + "/")
            row.td().text()
        else:
            level = 0

        for directory_name in sorted(directories.keys()):
            outputDirectory(base + name + "/" if name else "", directory_name, directories[directory_name][0], directories[directory_name][1])

        for file_name in sorted(files.keys()):
            row = basic.tr("file", critic_file_id=files[file_name], critic_level=level + 1)
            row.td("select").input(type="checkbox")
            row.td("path").preformatted().innerHTML((" " * (len(base + name) - 1)) + "&#8230;/" + htmlutils.htmlify(file_name))
            row.td().text()

    outputDirectory("", "", root_directories, root_files)

    return document
Exemple #9
0
def renderShowReviewLog(req, db, user):
    review_id = page.utils.getParameter(req, "review", filter=int)
    granularity = page.utils.getParameter(req, "granularity")
    unassigned = page.utils.getParameter(req, "unassigned", "no") == "yes"

    cursor = db.cursor()

    review = dbutils.Review.fromId(db, review_id)

    document = htmlutils.Document(req)
    html = document.html()
    head = html.head()
    body = html.body()

    head.title("Review Log: %s" % review.branch.name)

    page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review")])

    document.addExternalStylesheet("resource/showreviewlog.css")
    document.addExternalStylesheet("resource/review.css")
    document.addExternalScript("resource/review.js")
    document.addInternalScript(review.getJS())

    target = body.div("main")

    reviewed_reviewers = review_utils.getReviewedReviewers(db, review)

    def formatFiles(files):
        paths = sorted([dbutils.describe_file(db, file_id) for file_id in files])
        if granularity == "file":
            return diff.File.eliminateCommonPrefixes(paths)
        else:
            modules = set()
            files = []
            for path in paths:
                match = re_module.match(path)
                if match: modules.add(match.group(1))
                else: files.append(path)
            return sorted(modules) + diff.File.eliminateCommonPrefixes(files)

    if reviewed_reviewers and not unassigned:
        reviewed = target.table('changes', align='center')
        reviewed.col(width="30%")
        reviewed.col(width="10%")
        reviewed.col(width="60%")
        reviewed.tr().td('h1', colspan=3).h1().text("Reviewed Changes")

        teams = review_utils.collectReviewTeams(reviewed_reviewers)

        for team in teams:
            row = reviewed.tr("reviewers")

            cell = row.td("reviewers")
            users = sorted([dbutils.User.fromId(db, user_id).fullname for user_id in team])
            for user in users: cell.text(user).br()
            row.td("willreview").innerHTML("reviewed")

            cell = row.td("files")
            for file in formatFiles(teams[team]):
                cell.span("file").innerHTML(file).br()

    pending_reviewers = review_utils.getPendingReviewers(db, review)

    if pending_reviewers:
        pending = target.table('changes', align='center')
        pending.col(width="30%")
        pending.col(width="10%")
        pending.col(width="60%")
        pending.tr().td('h1', colspan=3).h1().text("Pending Changes")

        teams = review_utils.collectReviewTeams(pending_reviewers)

        if not unassigned:
            for team in teams:
                if team is not None:
                    row = pending.tr("reviewers")

                    cell = row.td("reviewers")
                    users = sorted([dbutils.User.fromId(db, user_id).fullname for user_id in team])
                    for user in users: cell.text(user).br()
                    row.td("willreview").innerHTML("should&nbsp;review")

                    cell = row.td("files")
                    for file in formatFiles(teams[team]):
                        cell.span("file").innerHTML(file).br()

        if None in teams:
            row = pending.tr("reviewers")
            row.td("no-one", colspan=2).text("No reviewers found for changes in")

            cell = row.td("files")
            for file in formatFiles(teams[None]):
                cell.span("file").innerHTML(file).br()

    return document
Exemple #10
0
 def generateButtons(target):
     review_utils.renderDraftItems(db, user, review, target)
     buttons = target.div("buttons")
     if user.getPreference(db, "debug.extensions.customProcessCommits"):
         buttons.button(onclick='customProcessCommits();').text("Process Commits")
     buttons.span("buttonscope buttonscope-global")
Exemple #11
0
def renderShowReviewLog(req, db, user):
    review_id = page.utils.getParameter(req, "review", filter=int)
    granularity = page.utils.getParameter(req, "granularity")
    unassigned = page.utils.getParameter(req, "unassigned", "no") == "yes"

    cursor = db.cursor()

    review = dbutils.Review.fromId(db, review_id)

    document = htmlutils.Document(req)
    html = document.html()
    head = html.head()
    body = html.body()

    head.title("Review Log: %s" % review.branch.name)

    page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review")])

    document.addExternalStylesheet("resource/showreviewlog.css")
    document.addExternalStylesheet("resource/review.css")
    document.addExternalScript("resource/review.js")
    document.addInternalScript(review.getJS())

    target = body.div("main")

    reviewed_reviewers = review_utils.getReviewedReviewers(db, review)

    def formatFiles(files):
        paths = sorted([dbutils.describe_file(db, file_id) for file_id in files])
        if granularity == "file":
            return diff.File.eliminateCommonPrefixes(paths)
        else:
            modules = set()
            files = []
            for path in paths:
                match = re_module.match(path)
                if match: modules.add(match.group(1))
                else: files.append(path)
            return sorted(modules) + diff.File.eliminateCommonPrefixes(files)

    if reviewed_reviewers and not unassigned:
        reviewed = target.table("paleyellow")
        reviewed.col(width="30%")
        reviewed.col(width="10%")
        reviewed.col(width="60%")
        reviewed.tr().td('h1', colspan=3).h1().text("Reviewed Changes")

        teams = review_utils.collectReviewTeams(reviewed_reviewers)

        for team in teams:
            row = reviewed.tr("reviewers")

            cell = row.td("reviewers")
            users = sorted([dbutils.User.fromId(db, user_id).fullname for user_id in team])
            for user in users: cell.text(user).br()
            row.td("willreview").innerHTML("reviewed")

            cell = row.td("files")
            for file in formatFiles(teams[team]):
                cell.span("file").innerHTML(file).br()

    pending_reviewers = review_utils.getPendingReviewers(db, review)

    if pending_reviewers:
        pending = target.table("paleyellow")
        pending.col(width="30%")
        pending.col(width="10%")
        pending.col(width="60%")
        pending.tr().td('h1', colspan=3).h1().text("Pending Changes")

        teams = review_utils.collectReviewTeams(pending_reviewers)

        if not unassigned:
            for team in teams:
                if team is not None:
                    row = pending.tr("reviewers")

                    cell = row.td("reviewers")
                    users = sorted([dbutils.User.fromId(db, user_id).fullname for user_id in team])
                    for user in users: cell.text(user).br()
                    row.td("willreview").innerHTML("should&nbsp;review")

                    cell = row.td("files")
                    for file in formatFiles(teams[team]):
                        cell.span("file").innerHTML(file).br()

        if None in teams:
            row = pending.tr("reviewers")
            row.td("no-one", colspan=2).text("No reviewers found for changes in")

            cell = row.td("files")
            for file in formatFiles(teams[None]):
                cell.span("file").innerHTML(file).br()

    return document