def showfilters(req, db, user): path = req.getParameter("path", "/") repo_name = req.getParameter("repository", None) if not repo_name: user = req.getParameter("user", req.user) if not user: raise page.utils.DisplayMessage( "The URL must contain either a repository or a user parameter or both." ) repo_name = dbutils.User.fromName(db, user).getPreference( db, "defaultRepository") repository = gitutils.Repository.fromParameter(db, repo_name) path = path.rstrip("/") if repository.getHead(db).isDirectory(path): show_path = path + "/" path += "/dummy.txt" else: show_path = path file_id = dbutils.find_file(db, path=path) filters = review_filters.Filters() filters.setFiles(db, [file_id]) filters.load(db, repository=repository, recursive=True) reviewers = [] watchers = [] for user_id, (filter_type, _delegate) in filters.listUsers(file_id).items(): if filter_type == 'reviewer': reviewers.append(user_id) else: watchers.append(user_id) result = "Path: %s\n" % show_path reviewers_found = False watchers_found = False for reviewer_id in sorted(reviewers): if not reviewers_found: result += "\nReviewers:\n" reviewers_found = True reviewer = dbutils.User.fromId(db, reviewer_id) result += " %s <%s>\n" % (reviewer.fullname, reviewer.email) for watcher_id in sorted(watchers): if not watchers_found: result += "\nWatchers:\n" watchers_found = True watcher = dbutils.User.fromId(db, watcher_id) result += " %s <%s>\n" % (watcher.fullname, watcher.email) if not reviewers_found and not watchers_found: result += "\nNo matching filters found.\n" return result
def parseReviewFilters(db, data): reviewfilters = [] for filter_data in data: filter_username = filter_data["username"] filter_type = filter_data["type"] filter_path = filter_data["path"] filter_user = dbutils.User.fromName(db, filter_username) if not filter_user: raise OperationError("no such user: '******'" % filter_username) filter_directory_id = 0 filter_file_id = 0 if filter_path != "/": if filter_path[-1] == "/" or dbutils.is_directory(filter_path): filter_directory_id = dbutils.find_directory(db, path=filter_path) else: filter_file_id = dbutils.find_file(db, path=filter_path) reviewfilters.append((filter_directory_id, filter_file_id, filter_type, None, filter_user.id)) return reviewfilters
def process(self, db, creator, review_id, filters): review = dbutils.Review.fromId(db, review_id) by_user = {} for filter in filters: if "user_ids" in filter: user_ids = set(filter["user_ids"]) else: user_ids = set([]) if "user_names" in filter: for user_name in filter["user_names"]: user_ids.add(dbutils.User.fromName(db, user_name).id) if "directory_ids" in filter: directory_ids = set(filter["directory_ids"]) else: directory_ids = set([]) if "file_ids" in filter: file_ids = set(filter["file_ids"]) else: file_ids = set([]) if "paths" in filter: for path in filter["paths"]: if not path or path == "/": directory_ids.add(0) elif path.endswith("/") or dbutils.is_directory( db, path) or ("." not in path.split("/")[-1] and not dbutils.is_file(db, path)): directory_ids.add(dbutils.find_directory(db, path)) else: file_ids.add(dbutils.find_file(db, path)) for user_id in user_ids: reviewer_directory_ids, reviewer_file_ids, watcher_directory_ids, watcher_file_ids = by_user.setdefault( user_id, (set(), set(), set(), set())) if filter["type"] == "reviewer": reviewer_directory_ids |= directory_ids reviewer_file_ids |= file_ids else: watcher_directory_ids |= directory_ids watcher_file_ids |= file_ids pending_mails = [] for user_id, args in by_user.items(): user = dbutils.User.fromId(db, user_id) pending_mails.extend( review_utils.addReviewFilters(db, creator, user, review, *args)) db.commit() mailutils.sendPendingMails(pending_mails) return OperationResult()
def showfilters(req, db, user): user = dbutils.User.fromName(db, req.getParameter("user", req.user)) path = req.getParameter("path") repository = gitutils.Repository.fromParameter(db, req.getParameter("repository", user.getPreference(db, "defaultRepository"))) path = path.rstrip("/") if dbutils.is_directory(db, path): directory_id = dbutils.find_directory(db, path=path) show_path = path + "/" cursor = db.cursor() cursor.execute("SELECT name FROM files WHERE directory=%s ORDER BY id ASC LIMIT 1", (directory_id,)) row = cursor.fetchone() if row: path += "/" + row[0] else: path += "/dummy.txt" else: show_path = path file_id = dbutils.find_file(db, path=path) filters = review_filters.Filters() filters.load(db, repository=repository, recursive=True) reviewers = [] watchers = [] for user_id, (filter_type, _delegate) in filters.listUsers(db, file_id).items(): if filter_type == 'reviewer': reviewers.append(user_id) else: watchers.append(user_id) result = "Path: %s\n" % show_path reviewers_found = False watchers_found = False for reviewer_id in sorted(reviewers): if not reviewers_found: result += "\nReviewers:\n" reviewers_found = True reviewer = dbutils.User.fromId(db, reviewer_id) result += " %s <%s>\n" % (reviewer.fullname, reviewer.email) for watcher_id in sorted(watchers): if not watchers_found: result += "\nWatchers:\n" watchers_found = True watcher = dbutils.User.fromId(db, watcher_id) result += " %s <%s>\n" % (watcher.fullname, watcher.email) if not reviewers_found and not watchers_found: result += "\nNo matching filters found.\n" return result
def showfilters(req, db, user): path = req.getParameter("path", "/") repo_name = req.getParameter("repository", None) if not repo_name: user = req.getParameter("user", req.user) if not user: raise page.utils.DisplayMessage("The URL must contain either a repository or a user parameter or both.") repo_name = dbutils.User.fromName(db, user).getPreference(db, "defaultRepository") repository = gitutils.Repository.fromParameter(db, repo_name) path = path.rstrip("/") if repository.getHead(db).isDirectory(path): show_path = path + "/" path += "/dummy.txt" else: show_path = path file_id = dbutils.find_file(db, path=path) filters = review_filters.Filters() filters.setFiles(db, [file_id]) filters.load(db, repository=repository, recursive=True) reviewers = [] watchers = [] for user_id, (filter_type, _delegate) in filters.listUsers(file_id).items(): if filter_type == 'reviewer': reviewers.append(user_id) else: watchers.append(user_id) result = "Path: %s\n" % show_path reviewers_found = False watchers_found = False for reviewer_id in sorted(reviewers): if not reviewers_found: result += "\nReviewers:\n" reviewers_found = True reviewer = dbutils.User.fromId(db, reviewer_id) result += " %s <%s>\n" % (reviewer.fullname, reviewer.email) for watcher_id in sorted(watchers): if not watchers_found: result += "\nWatchers:\n" watchers_found = True watcher = dbutils.User.fromId(db, watcher_id) result += " %s <%s>\n" % (watcher.fullname, watcher.email) if not reviewers_found and not watchers_found: result += "\nNo matching filters found.\n" return result
def process(self, db, creator, review_id, filters): review = dbutils.Review.fromId(db, review_id) by_user = {} for filter in filters: if "user_ids" in filter: user_ids = set(filter["user_ids"]) else: user_ids = set([]) if "user_names" in filter: for user_name in filter["user_names"]: user_ids.add(dbutils.User.fromName(db, user_name).id) if "directory_ids" in filter: directory_ids = set(filter["directory_ids"]) else: directory_ids = set([]) if "file_ids" in filter: file_ids = set(filter["file_ids"]) else: file_ids = set([]) if "paths" in filter: for path in filter["paths"]: if not path or path == "/": directory_ids.add(0) elif path.endswith("/") or dbutils.is_directory(db, path) or ("." not in path.split("/")[-1] and not dbutils.is_file(db, path)): directory_ids.add(dbutils.find_directory(db, path)) else: file_ids.add(dbutils.find_file(db, path)) for user_id in user_ids: reviewer_directory_ids, reviewer_file_ids, watcher_directory_ids, watcher_file_ids = by_user.setdefault(user_id, (set(), set(), set(), set())) if filter["type"] == "reviewer": reviewer_directory_ids |= directory_ids reviewer_file_ids |= file_ids else: watcher_directory_ids |= directory_ids watcher_file_ids |= file_ids pending_mails = [] for user_id, args in by_user.items(): user = dbutils.User.fromId(db, user_id) pending_mails.extend(review_utils.addReviewFilters(db, creator, user, review, *args)) db.commit() mailutils.sendPendingMails(pending_mails) return OperationResult()
def renderShowFilters(req, db, user): path = req.getParameter("path", "/") repo_name = req.getParameter("repository", user.getPreference(db, "defaultRepository")) repository = gitutils.Repository.fromParameter(db, repo_name) show_path = path if path.endswith("/") or repository.getHead(db).isDirectory(path): path = path.rstrip("/") + "/dummy.txt" file_id = dbutils.find_file(db, path=path) filters = reviewing.filters.Filters() filters.setFiles(db, [file_id]) filters.load(db, repository=repository, recursive=True) reviewers = [] watchers = [] for user_id, (filter_type, _delegate) in filters.listUsers(file_id).items(): if filter_type == 'reviewer': reviewers.append(user_id) else: watchers.append(user_id) result = "Path: %s\n" % show_path reviewers_found = False watchers_found = False for reviewer_id in sorted(reviewers): if not reviewers_found: result += "\nReviewers:\n" reviewers_found = True reviewer = dbutils.User.fromId(db, reviewer_id) result += " %s <%s>\n" % (reviewer.fullname, reviewer.email) for watcher_id in sorted(watchers): if not watchers_found: result += "\nWatchers:\n" watchers_found = True watcher = dbutils.User.fromId(db, watcher_id) result += " %s <%s>\n" % (watcher.fullname, watcher.email) if not reviewers_found and not watchers_found: result += "\nNo matching filters found.\n" return page.utils.ResponseBody(result, content_type="text/plain")
def renderShowFilters(req, db, user): path = req.getParameter("path", "/") repo_name = req.getParameter( "repository", user.getPreference(db, "defaultRepository")) repository = gitutils.Repository.fromParameter(db, repo_name) show_path = path if path.endswith("/") or repository.getHead(db).isDirectory(path): path = path.rstrip("/") + "/dummy.txt" file_id = dbutils.find_file(db, path=path) filters = reviewing.filters.Filters() filters.setFiles(db, [file_id]) filters.load(db, repository=repository, recursive=True) reviewers = [] watchers = [] for user_id, (filter_type, _delegate) in filters.listUsers(file_id).items(): if filter_type == 'reviewer': reviewers.append(user_id) else: watchers.append(user_id) result = "Path: %s\n" % show_path reviewers_found = False watchers_found = False for reviewer_id in sorted(reviewers): if not reviewers_found: result += "\nReviewers:\n" reviewers_found = True reviewer = dbutils.User.fromId(db, reviewer_id) result += " %s <%s>\n" % (reviewer.fullname, reviewer.email) for watcher_id in sorted(watchers): if not watchers_found: result += "\nWatchers:\n" watchers_found = True watcher = dbutils.User.fromId(db, watcher_id) result += " %s <%s>\n" % (watcher.fullname, watcher.email) if not reviewers_found and not watchers_found: result += "\nNo matching filters found.\n" return page.utils.ResponseBody(result, content_type="text/plain")
def renderSearch(req, db, user): summary_value = req.getParameter("summary", None) summary_mode_value = req.getParameter("summarymode", None) branch_value = req.getParameter("branch", None) owner_value = req.getParameter("owner", None) path_value = req.getParameter("path", None) document = htmlutils.Document(req) document.setTitle("Search") html = document.html() head = html.head() body = html.body() page.utils.generateHeader(body, db, user, current_page="search") document.addExternalStylesheet("resource/search.css") document.addExternalScript("resource/search.js") document.addInternalScript(user.getJS()) cursor = db.cursor() cursor.execute( "SELECT DISTINCT name, fullname FROM users JOIN reviewusers ON (reviewusers.uid=users.id) WHERE reviewusers.owner" ) users = [("{ label: %s, value: %s }" % (htmlutils.jsify("%s (%s)" % (fullname, name)), htmlutils.jsify(name))) for name, fullname in cursor] document.addInternalScript("var users = [ %s ];" % ", ".join(users)) search = page.utils.PaleYellowTable(body, "Search") def renderSummary(target): target.input(name="summary", value=summary_value or "") summary_mode = target.select(name="summary_mode") summary_mode.option(value="all", selected="selected" if summary_mode_value == "all" else None).text("All words") summary_mode.option(value="any", selected="selected" if summary_mode_value == "any" else None).text("Any word") def renderBranch(target): target.input(name="branch", value=branch_value or "") def renderOwner(target): target.input(name="owner", value=owner_value or "") def renderPath(target): target.input(name="path", value=path_value or "") def renderButton(target): target.button(onclick="search();").text("Search") search.addItem("Summary", renderSummary, "Words occurring in the review's summary.") search.addItem("Branch", renderBranch, "Name of review branch.") search.addItem("Owner", renderOwner, "Owner of the review.") search.addItem( "Path", renderPath, "Path (file or directory) that the review contains changes in.") search.addCentered(renderButton) if summary_value is not None: summary_value = summary_value.strip() if branch_value is not None: branch_value = branch_value.strip() if owner_value is not None: owner_value = owner_value.strip() if path_value is not None: path_value = path_value.strip() if summary_value or branch_value or owner_value or path_value: query = """SELECT DISTINCT reviews.id, reviews.summary, branches.name FROM %s WHERE %s""" tables = ["reviews", "branches ON (branches.id=reviews.branch)"] conditions = [] arguments = [] if summary_value: words = summary_value.split() operator = " AND " if summary_mode_value == "all" else " OR " conditions.append( "(%s)" % operator.join(["reviews.summary ~* %s"] * len(words))) arguments.extend([".*\\m" + word + "\\M.*" for word in words]) if branch_value: def globToSQLPattern(glob): pattern = glob.replace("\\", "\\\\").replace("%", "\\%").replace( "?", "_").replace("*", "%") if pattern[0] != "%": pattern = "%" + pattern if pattern[-1] != "%": pattern = pattern + "%" return pattern conditions.append("branches.name LIKE %s") arguments.append(globToSQLPattern(branch_value)) if owner_value: owner = dbutils.User.fromName(db, owner_value) tables.append("reviewusers ON (reviewusers.review=reviews.id)") conditions.append("reviewusers.uid=%s") conditions.append("reviewusers.owner") arguments.append(owner.id) if path_value: file_ids = dbutils.contained_files( db, dbutils.find_directory(db, path_value)) if path_value[-1] != '/': file_ids.append(dbutils.find_file(db, path_value)) tables.append("reviewfiles ON (reviewfiles.review=reviews.id)") conditions.append("reviewfiles.file=ANY (%s)") arguments.append(file_ids) query = """SELECT DISTINCT reviews.id, reviews.summary, branches.name FROM %s WHERE %s ORDER BY reviews.id""" % (" JOIN ".join(tables), " AND ".join(conditions)) cursor.execute(query, arguments) table = body.div("main").table("paleyellow reviews", align="center") table.col(width="20%") table.col(width="80%") header = table.tr().td("h1", colspan=4).h1() header.text("Reviews") for review_id, summary, branch_name in cursor: row = table.tr("review") row.td("name").text(branch_name) row.td("title").a(href="r/%d" % review_id).text(summary) return document
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&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)
teams_per_modules.setdefault(frozenset(modules), set()).update(team) for modules, team in teams_per_modules.items(): row = shared.tr("reviewers") cell = row.td("reviewers") members = sorted([dbutils.User.fromId(db, user_id).fullname for user_id in team]) for member in members: cell.text(member).br() row.td("willreview").innerHTML("<span class='also'>also</span> review changes in") cell = row.td("files") for path in diff.File.eliminateCommonPrefixes(sorted(modules)): cell.span("file").innerHTML(path).br() directory_ids = "[ %s ]" % ", ".join([str(dbutils.find_directory(db, path=path[:-1])) for path in modules if path.endswith("/")]) file_ids = "[ %s ]" % ", ".join([str(dbutils.find_file(db, path=path)) for path in modules if not path.endswith("/")]) user_ids = "[ %s ]" % ", ".join(map(str, team)) cell = row.td("buttons") cell.button("accept", critic_directory_ids=directory_ids, critic_file_ids=file_ids, critic_user_ids=user_ids).text("I will review this!") cell.button("deny", critic_directory_ids=directory_ids, critic_file_ids=file_ids, critic_user_ids=user_ids).text("They will review this!") yield flush(target) profiler.check("shared assignments") cursor.execute("SELECT batches.id, users.fullname, batches.comment, batches.time FROM batches JOIN users ON (users.id=batches.uid) WHERE batches.review=%s ORDER BY batches.id DESC", [review.id]) rows = cursor.fetchall() if rows: notes = dict([(chain.id, chain) for chain in open_notes])
def render(db, target, user, repository, review, changesets, commits, listed_commits=None, context_lines=3, is_merge=False, conflicts=False, moves=False, compact=False, wrap=True, tabify=False, profiler=None, rebases=None): cursor = db.cursor() main = target.div("main", style="border-bottom: 3px solid black; margin-bottom: 20px; padding-bottom: 10px") options = {} if not user.getPreference(db, "ui.keyboardShortcuts"): options['show'] = True if user.getPreference(db, "commit.expandAllFiles"): options['show'] = True options['expand'] = True if compact: options['compact'] = True if tabify: options['tabify'] = True options['commit'] = changesets[0].child if len(changesets) == 1: if commits and len(commits) > 1: def linkToCommit(commit): if review: return "%s/%s?review=%d" % (repository.name, commit.sha1, review.id) else: return "%s/%s" % (repository.name, commit.sha1) columns = [(10, log_html.WhenColumn()), (5, log_html.TypeColumn()), (65, log_html.SummaryColumn(linkToCommit)), (20, log_html.AuthorColumn())] log_html.render(db, main, "Squashed History", commits=commits, listed_commits=listed_commits, rebases=rebases, review=review, columns=columns, collapsable=True) elif changesets[0].parent is None or (changesets[0].parent.sha1 in changesets[0].child.parents) or conflicts: renderCommitInfo(db, main, user, repository, review, changesets[0].child, conflicts) else: main.setAttribute("style", "margin-bottom: 20px; padding-bottom: 10px") if moves: def renderMoveHeaderLeft(db, target, file): target.text(file.move_source_file.path) def renderMoveHeaderRight(db, target, file): target.text(file.move_target_file.path) options['show'] = True options['expand'] = True options['support_expand'] = False options['header_left'] = renderMoveHeaderLeft options['header_right'] = renderMoveHeaderRight context_lines = 0 else: renderCommitFiles(db, target, user, review, changeset=changesets[0]) yield target for stop in changeset_html.render(db, target, user, changesets[0], review, context_lines=context_lines, options=options, wrap=wrap): yield stop else: commit = changesets[0].child renderCommitInfo(db, main, user, repository, review, commit) if profiler: profiler.check("render commit info") nparents = len(changesets) target.addInternalScript("var parentsCount = %d;" % nparents) files = {} for index, changeset in enumerate(changesets): for file in changeset.files: files.setdefault(file.id, [file.id, file.path, [None] * nparents])[2][index] = file renderCommitFiles(db, target, user, review, changesets=changesets, file_id="p%df%%d" % index, approve_file_id="p%da%%d" % index, nparents=nparents, conflicts=changesets[-1].conflicts, files=diff.File.sorted(files.values(), key=lambda x: x[1])) if profiler: profiler.check("render commit files") mergebase = repository.mergebase(commit, db=db) if profiler: profiler.check("merge base") yield target relevant_commits = [] cursor.execute("SELECT parent, file, sha1 FROM relevantcommits JOIN commits ON (relevant=id) WHERE commit=%s", (commit.getId(db),)) rows = cursor.fetchall() if rows: for index in range(len(changesets)): relevant_commits.append({}) commits_by_sha1 = {} for parent_index, file_id, sha1 in rows: if sha1 not in commits_by_sha1: commits_by_sha1[sha1] = gitutils.Commit.fromSHA1(db, repository, sha1) relevant_commits[parent_index].setdefault(file_id, []).append(commits_by_sha1[sha1]) else: values = [] commits_by_sha1 = {} for index, changeset in enumerate(changesets): relevant_files = set([file.path for file in changeset.files]) files = {} if not changeset.conflicts: commit_range = "%s..%s" % (mergebase, changeset.parent.sha1) relevant_lines = repository.run("log", "--name-only", "--full-history", "--format=sha1:%H", commit_range, "--", *relevant_files).splitlines() for line in relevant_lines: if line.startswith("sha1:"): sha1 = line[5:] elif line in relevant_files: if sha1 not in commits_by_sha1: commits_by_sha1[sha1] = gitutils.Commit.fromSHA1(db, repository, sha1) relevant_commit = commits_by_sha1[sha1] file_id = dbutils.find_file(db, path=line) values.append((commit.getId(db), index, file_id, relevant_commit.getId(db))) files.setdefault(file_id, []).append(relevant_commit) relevant_commits.append(files) cursor.executemany("INSERT INTO relevantcommits (commit, parent, file, relevant) VALUES (%s, %s, %s, %s)", values) if profiler: profiler.check("collecting relevant commits") target.script(type="text/javascript").text("calculateTabWidth();") for index, changeset in enumerate(changesets): parent = target.div("parent", id="p%d" % index) options['support_expand'] = bool(changeset.conflicts) options['file_id'] = lambda base: "p%d%s" % (index, base) options['line_id'] = lambda base: "p%d%s" % (index, base) options['line_cell_id'] = lambda base: base is not None and "p%d%s" % (index, base) or None options['file_id_format'] = "p%df%%d" % index relevant_commits_per_file = {} for file in changeset.files: relevant_commits_per_file[file.id] = [] for index1, changeset1 in enumerate(changesets): if index1 != index: relevant_commits_per_file[file.id].extend(relevant_commits[index1].get(file.id, [])) if changeset.conflicts: text = "Merge conflict resolutions" else: text = "Changes relative to %s parent" % ("first", "second", "third", "fourth", "fifth", "seventh", "eight", "ninth")[index] parent.h1().text(text) def renderRelevantCommits(db, target, file): commits = relevant_commits_per_file.get(file.id) if commits: def linkToCommit(commit): return "%s/%s?file=%d" % (commit.repository.name, commit.sha1, file.id) columns = [(70, log_html.SummaryColumn(linkToCommit=linkToCommit)), (30, log_html.AuthorColumn())] log_html.renderList(db, target, "Relevant Commits", commits, columns=columns, hide_merges=True, className="log relevant") options['content_after'] = renderRelevantCommits options['parent_index'] = index options['merge'] = True for stop in changeset_html.render(db, parent, user, changeset, review, context_lines=context_lines, options=options, wrap=wrap, parent_index=index): yield stop if profiler: profiler.check("render diff") if user.getPreference(db, "ui.keyboardShortcuts"): page.utils.renderShortcuts(target, "showcommit", merge_parents=len(changesets), squashed_diff=commits and len(commits) > 1)
def renderSearch(req, db, user): summary_value = req.getParameter("summary", None) summary_mode_value = req.getParameter("summarymode", None) branch_value = req.getParameter("branch", None) owner_value = req.getParameter("owner", None) path_value = req.getParameter("path", None) document = htmlutils.Document(req) document.setTitle("Search") html = document.html() head = html.head() body = html.body() page.utils.generateHeader(body, db, user, current_page="search") document.addExternalStylesheet("resource/search.css") document.addExternalScript("resource/search.js") document.addInternalScript(user.getJS()) cursor = db.cursor() cursor.execute("SELECT DISTINCT name, fullname FROM users JOIN reviewusers ON (reviewusers.uid=users.id) WHERE reviewusers.owner") users = [("{ label: %s, value: %s }" % (htmlutils.jsify("%s (%s)" % (fullname, name)), htmlutils.jsify(name))) for name, fullname in cursor] document.addInternalScript("var users = [ %s ];" % ", ".join(users)) search = page.utils.PaleYellowTable(body, "Search") def renderSummary(target): target.input(name="summary", value=summary_value or "") summary_mode = target.select(name="summary_mode") summary_mode.option(value="all", selected="selected" if summary_mode_value == "all" else None).text("All words") summary_mode.option(value="any", selected="selected" if summary_mode_value == "any" else None).text("Any word") def renderBranch(target): target.input(name="branch", value=branch_value or "") def renderOwner(target): target.input(name="owner", value=owner_value or "") def renderPath(target): target.input(name="path", value=path_value or "") def renderButton(target): target.button(onclick="search();").text("Search") search.addItem("Summary", renderSummary, "Words occurring in the review's summary.") search.addItem("Branch", renderBranch, "Name of review branch.") search.addItem("Owner", renderOwner, "Owner of the review.") search.addItem("Path", renderPath, "Path (file or directory) that the review contains changes in.") search.addCentered(renderButton) if summary_value is not None: summary_value = summary_value.strip() if branch_value is not None: branch_value = branch_value.strip() if owner_value is not None: owner_value = owner_value.strip() if path_value is not None: path_value = path_value.strip() if summary_value or branch_value or owner_value or path_value: query = """SELECT DISTINCT reviews.id, reviews.summary, branches.name FROM %s WHERE %s""" tables = ["reviews", "branches ON (branches.id=reviews.branch)"] conditions = [] arguments = [] if summary_value: words = summary_value.split() operator = " AND " if summary_mode_value == "all" else " OR " conditions.append("(%s)" % operator.join(["reviews.summary ~* %s"] * len(words))) arguments.extend([".*\\m" + word + "\\M.*" for word in words]) if branch_value: def globToSQLPattern(glob): pattern = glob.replace("\\", "\\\\").replace("%", "\\%").replace("?", "_").replace("*", "%") if pattern[0] != "%": pattern = "%" + pattern if pattern[-1] != "%": pattern = pattern + "%" return pattern conditions.append("branches.name LIKE %s") arguments.append(globToSQLPattern(branch_value)) if owner_value: owner = dbutils.User.fromName(db, owner_value) tables.append("reviewusers ON (reviewusers.review=reviews.id)") conditions.append("reviewusers.uid=%s") conditions.append("reviewusers.owner") arguments.append(owner.id) if path_value: file_ids = dbutils.contained_files(db, dbutils.find_directory(db, path_value)) if path_value[-1] != '/': file_ids.append(dbutils.find_file(db, path_value)) tables.append("reviewfiles ON (reviewfiles.review=reviews.id)") conditions.append("reviewfiles.file=ANY (%s)") arguments.append(file_ids) query = """SELECT DISTINCT reviews.id, reviews.summary, branches.name FROM %s WHERE %s ORDER BY reviews.id""" % (" JOIN ".join(tables), " AND ".join(conditions)) cursor.execute(query, arguments) table = body.div("main").table("paleyellow reviews", align="center") table.col(width="20%") table.col(width="80%") header = table.tr().td("h1", colspan=4).h1() header.text("Reviews") for review_id, summary, branch_name in cursor: row = table.tr("review") row.td("name").text(branch_name) row.td("title").a(href="r/%d" % review_id).text(summary) return document
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&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)