def getURL(self, db, user=None, indent=0): import dbutils indent = " " * indent if db and user: url_prefixes = user.getCriticURLs(db) else: url_prefixes = [dbutils.getURLPrefix(db)] return "\n".join(["%s%s/r/%d" % (indent, url_prefix, self.id) for url_prefix in url_prefixes])
def getURL(self, db, user=None, indent=0, separator="\n"): import dbutils indent = " " * indent if user: url_prefixes = user.getCriticURLs(db) else: url_prefixes = [dbutils.getURLPrefix(db)] return separator.join(["%s%s/r/%d" % (indent, url_prefix, self.id) for url_prefix in url_prefixes])
def constructURL(db, user, path): path = os.path.relpath(path, configuration.paths.GIT_DIR) url_type = user.getPreference(db, "repository.urlType") if url_type == "git": url_format = "git://%s/%s" elif url_type in ("ssh", "host"): if url_type == "ssh": prefix = "ssh://%s" else: prefix = "%s:" url_format = prefix + os.path.join(configuration.paths.GIT_DIR, "%s") else: import dbutils url_prefix = dbutils.getURLPrefix(db, user) return "%s/%s" % (url_prefix, path) return url_format % (configuration.base.HOSTNAME, path)
def renderItem(target): span = target.span("name") span.b().text(extension.getName()) span.text(" by %s" % author.fullname) span = target.span("details") span.b().text("Details: ") select = span.select("details", critic_author=extension.getAuthorName(), critic_extension=extension.getName()) select.option(value='', selected="selected" if selected_version is False else None).text("Select version") versions = extension.getVersions() if versions: optgroup = select.optgroup(label="Official Versions") for version in extension.getVersions(): optgroup.option(value="version/%s" % version, selected="selected" if selected_version == version else None).text("%s" % version.upper()) optgroup = select.optgroup(label="Development") optgroup.option(value='live', selected="selected" if selected_version is None else None).text("LIVE") if manifest: is_installed = manifest.status in ("partial", "installed") or installed_version is not False if is_installed: target.span("installed").text(" [installed]") target.div("description").preformatted().text(manifest.description, linkify=True) else: is_installed = False target.div("description broken").preformatted().a(href="loadmanifest?author=%s&name=%s" % (extension.getAuthorName(), extension.getName())).text("[This extension has an invalid MANIFEST file]") if selected_version is False: return pages = [] injects = [] processcommits = [] processchanges = [] scheduled = [] if manifest: for role in manifest.roles: if isinstance(role, PageRole): pages.append(role) elif isinstance(role, InjectRole): injects.append(role) elif isinstance(role, ProcessCommitsRole): processcommits.append(role) elif isinstance(role, ProcessChangesRole): processchanges.append(role) elif isinstance(role, ScheduledRole): scheduled.append(role) role_table = target.table("roles") if pages: role_table.tr().th(colspan=2).text("Pages") for role in pages: row = role_table.tr() url = "%s/%s" % (dbutils.getURLPrefix(db), role.pattern) if is_installed and role.installed and "*" not in url: row.td("pattern").a(href=url).text(url) else: row.td("pattern").text(url) td = row.td("description") td.text(role.description) if is_installed and not role.installed: td.text(" ") td.span("inactive").text("[Not active!]") if injects: role_table.tr().th(colspan=2).text("Page Injections") for role in injects: row = role_table.tr() row.td("pattern").text("%s/%s" % (dbutils.getURLPrefix(db), role.pattern)) td = row.td("description") td.text(role.description) if is_installed and not role.installed: td.text(" ") td.span("inactive").text("[Not active!]") if processcommits: role_table.tr().th(colspan=2).text("ProcessCommits hooks") ul = role_table.tr().td(colspan=2).ul() for role in processcommits: li = ul.li() li.text(role.description) if is_installed and not role.installed: li.text(" ") li.span("inactive").text("[Not active!]") if processchanges: role_table.tr().th(colspan=2).text("ProcessChanges hooks") ul = role_table.tr().td(colspan=2).ul() for role in processchanges: li = ul.li() li.text(role.description) if is_installed and not role.installed: li.text(" ") li.span("inactive").text("[Not active!]") if scheduled: role_table.tr().th(colspan=2).text("Scheduled hooks") for role in scheduled: row = role_table.tr() row.td("pattern").text("%s @ %s" % (role.frequency, role.at)) td = row.td("description") td.text(role.description) if is_installed and not role.installed: td.text(" ") td.span("inactive").text("[Not active!]")
def createBranch(user, repository, name, head): processCommits(repository.name, head) cursor = db.cursor() def commit_id(sha1): cursor.execute("SELECT id FROM commits WHERE sha1=%s", [sha1]) return cursor.fetchone()[0] components = name.split("/") for index in range(1, len(components)): try: repository.revparse("refs/heads/%s" % "/".join(components[:index])) except: continue message = ( "Cannot create branch with name '%s' since there is already a branch named '%s' in the repository." % (name, "/".join(components[:index]))) raise IndexException, textutils.reflow(message, line_length=80 - len("remote: ")) if name.startswith("r/"): try: review_id = int(name[2:]) cursor.execute( "SELECT branches.name FROM reviews JOIN branches ON (branches.id=reviews.branch) WHERE reviews.id=%s", (review_id, )) row = cursor.fetchone() message = "Refusing to create review named as a number." if row: message += "\nDid you mean to push to the branch '%s', perhaps?" % row[ 0] raise IndexException, message except ValueError: pass if user.getPreference(db, "review.createViaPush"): the_commit = gitutils.Commit.fromSHA1(db, repository, head, commit_id(head)) all_commits = [the_commit] review = review_utils.createReview(db, user, repository, all_commits, name, the_commit.summary(), None, via_push=True) print "Submitted review: %s/r/%d" % (dbutils.getURLPrefix(db), review.id) if review.reviewers: print " Reviewers:" for reviewer in review.reviewers: print " %s <%s>" % (reviewer.fullname, reviewer.email) if review.watchers: print " Watchers:" for watcher in review.watchers: print " %s <%s>" % (watcher.fullname, watcher.email) if configuration.extensions.ENABLED: if extensions.executeProcessCommits(db, user, review, all_commits, None, the_commit, stdout): print print "Thank you!" return True else: raise IndexException, "Refusing to create review; user preference 'review.createViaPush' is not enabled." sha1 = head base = None tail = None cursor.execute( """SELECT 1 FROM reachable JOIN branches ON (branches.id=reachable.branch) JOIN repositories ON (repositories.id=branches.repository) WHERE repositories.id=%s LIMIT 1""", (repository.id, )) if cursor.fetchone(): def reachable(sha1): cursor.execute( """SELECT branches.id FROM branches JOIN reachable ON (reachable.branch=branches.id) JOIN commits ON (commits.id=reachable.commit) WHERE branches.repository=%s AND branches.type='normal' AND commits.sha1=%s ORDER BY reachable.branch ASC LIMIT 1""", (repository.id, sha1)) return cursor.fetchone() else: def reachable(sha1): return None commit_map = {} commit_list = [] row = reachable(sha1) if row: # Head of branch is reachable from an existing branch. Could be because # this branch is actually empty (just created with no "own" commits) or # it could have been merged into some other already existing branch. We # can't tell, so we just record it as empty. base = row[0] tail = sha1 else: stack = [] while True: if sha1 not in commit_map: commit = gitutils.Commit.fromSHA1(db, repository, sha1) commit_map[sha1] = commit commit_list.append(commit) for sha1 in commit.parents: if sha1 not in commit_map: row = reachable(sha1) if not row: stack.append(sha1) elif base is None: base = row[0] tail = sha1 base_chain = [base] while True: cursor.execute( "SELECT base FROM branches WHERE id=%s", (base_chain[-1], )) next = cursor.fetchone()[0] if next is None: break else: base_chain.append(next) def reachable(sha1): cursor.execute( """SELECT 1 FROM reachable JOIN commits ON (commits.id=reachable.commit) WHERE reachable.branch=ANY (%s) AND commits.sha1=%s""", (base_chain, sha1)) return cursor.fetchone() if stack: sha1 = stack.pop(0) else: break if len(commit_list) % 10000 > 1000: stdout.write("\n") stdout.flush() if not base: cursor.execute( "INSERT INTO branches (repository, name, head) VALUES (%s, %s, %s) RETURNING id", (repository.id, name, commit_id(head))) branch_id = cursor.fetchone()[0] else: cursor.execute( "INSERT INTO branches (repository, name, head, base, tail) VALUES (%s, %s, %s, %s, %s) RETURNING id", (repository.id, name, commit_id(head), base, commit_id(tail))) branch_id = cursor.fetchone()[0] cursor.execute("SELECT name FROM branches WHERE id=%s", [base]) print "Added branch based on %s containing %d commit%s:" % ( cursor.fetchone()[0], len(commit_list), "s" if len(commit_list) > 1 else "") print " %s/log?repository=%d&branch=%s" % (dbutils.getURLPrefix(db), repository.id, name) if len(commit_list) > 1: print "To create a review of all %d commits:" % len(commit_list) else: print "To create a review of the commit:" print " %s/createreview?repository=%d&branch=%s" % ( dbutils.getURLPrefix(db), repository.id, name) reachable_values = [(branch_id, commit.sha1) for commit in commit_list] cursor.executemany( "INSERT INTO reachable (branch, commit) SELECT %s, id FROM commits WHERE sha1=%s", reachable_values) if isinstance(user, str): user_name = user else: user_name = user.name if not repository.hasMainBranch( ) and user_name == configuration.base.SYSTEM_USER_NAME: cursor.execute("UPDATE repositories SET branch=%s WHERE id=%s", (branch_id, repository.id))
def commitRangeFromReview(db, user, review, filter, file_ids): edges = cursor = db.cursor() if filter == "pending": cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child FROM changesets JOIN reviewfiles ON (reviewfiles.changeset=changesets.id) JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id) WHERE reviewfiles.review=%s AND reviewuserfiles.uid=%s AND reviewfiles.state='pending'""", (review.id, user.id)) elif filter == "reviewable": cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child FROM changesets JOIN reviewfiles ON (reviewfiles.changeset=changesets.id) JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id) WHERE reviewfiles.review=%s AND reviewuserfiles.uid=%s""", (review.id, user.id)) elif filter == "relevant": filters = review_filters.Filters() filters.load(db, review=review, user=user) cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child, reviewfiles.file, reviewuserfiles.uid IS NOT NULL FROM changesets JOIN reviewfiles ON (reviewfiles.changeset=changesets.id) LEFT OUTER JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id AND reviewuserfiles.uid=%s) WHERE reviewfiles.review=%s""", (user.id, review.id)) edges = set() for parent_id, child_id, file_id, is_reviewer in cursor: if is_reviewer or filters.isRelevant(db, user, file_id): edges.add((parent_id, child_id)) elif filter == "files": assert len(file_ids) != 0 cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child FROM changesets JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id) JOIN fileversions ON (fileversions.changeset=changesets.id) WHERE reviewchangesets.review=%s AND fileversions.file=ANY (%s)""", (review.id, list(file_ids))) else: raise Exception, "invalid filter: %s" % filter listed_commits = set() with_pending = set() for parent_id, child_id in edges: listed_commits.add(child_id) with_pending.add((parent_id, child_id)) if len(listed_commits) == 1: return None, gitutils.Commit.fromId(db, review.repository, child_id).sha1, list(listed_commits), listed_commits if filter in ("reviewable", "relevant", "files"): cursor.execute("SELECT child FROM changesets JOIN reviewchangesets ON (changeset=id) WHERE review=%s", (review.id,)) all_commits = [gitutils.Commit.fromId(db, review.repository, commit_id) for (commit_id,) in cursor] commitset = CommitSet(review.branch.commits) tails = commitset.getFilteredTails(review.repository) if len(commitset) == 0: raise Exception, "empty commit-set" elif len(tails) > 1: ancestor = review.repository.getCommonAncestor(tails) paths = [] cursor.execute("SELECT DISTINCT file FROM reviewfiles WHERE review=%s", (review.id,)) files_in_review = set(file_id for (file_id,) in cursor) if filter == "files": files_in_review &= file_ids paths_in_review = set(dbutils.describe_file(db, file_id) for file_id in files_in_review) paths_in_upstreams = set() for tail in tails: paths_in_upstream = set(review.repository.run("diff", "--name-only", "%s..%s" % (ancestor, tail)).splitlines()) paths_in_upstreams |= paths_in_upstream paths.append((tail, paths_in_upstream)) overlapping_changes = paths_in_review & paths_in_upstreams if overlapping_changes: candidates = [] for index1, data in enumerate(paths): for index2, (tail, paths_in_upstream) in enumerate(paths): if index1 != index2 and paths_in_upstream & paths_in_review: break else: candidates.append(data) else: candidates = paths if not candidates: paths.sort(cmp=lambda a, b: cmp(len(a[1]), len(b[1]))) url = "/%s/%s..%s?file=%s" % (review.repository.name, paths[0][0][:8], review.branch.head.sha1[:8], ",".join(map(str, sorted(files_in_review)))) message = """\ <p>It is not possible to generate a diff of the requested set of commits that contains only changes from those commits.</p> <p>The following files would contain unrelated changes:<p> <pre style='padding-left: 2em'>%s</pre> <p>You can use the URL below if you want to view this diff anyway, including the unrelated changes.</p> <pre style='padding-left: 2em'><a href='%s'>%s%s</a></pre>""" % ("\n".join(sorted(overlapping_changes)), url, dbutils.getURLPrefix(db), url) raise page.utils.DisplayMessage(title="Impossible Diff", body=message, review=review, html=True) else: candidates.sort(cmp=lambda a, b: cmp(len(b[1]), len(a[1]))) return candidates[0][0], review.branch.head.sha1, all_commits, listed_commits elif len(tails) == 0: raise Exception, "impossible commit-set (%r)" % commitset return tails.pop(), review.branch.head.sha1, all_commits, listed_commits
yield str(document) return except: error_message = traceback.format_exc() environ["wsgi.errors"].write(error_message) db.rollback() if not user or user.hasRole(db, "developer"): title = "You broke the system again:" body = error_message body_html = "<pre>%s</pre>" % htmlify(body) else: prefix = dbutils.getURLPrefix(db) x_forwarded_host = req.getRequestHeader("X-Forwarded-Host") if x_forwarded_host: prefix = "https://" + x_forwarded_host url = "%s/%s?%s" % (prefix, req.path, req.query) mailutils.sendExceptionMessage("wsgi", ( "User: %s\nMethod: %s\nPath: %s\nQuery: %s\nURL: %s\n\n%s" % (req.user, req.method, req.path, req.query, url, error_message))) title = "Darn! It seems we have a problem..." body = "A message has been sent to the system administrator(s) with details about the problem." body_html = body
def renderFormatted(db, table, lines, toc=False): re_h1 = re.compile("^=+$") re_h2 = re.compile("^-+$") data = {"configuration.URL": dbutils.getURLPrefix(db)} blocks = [] block = [] for line in lines: if line.strip(): block.append(line % data) elif block: blocks.append(block) block = [] else: if block: blocks.append(block) text = None for block in blocks: def textToId(text): return text.lower().replace(' ', '_') if len(block) == 2: if re_h1.match(block[1]): table.setTitle(block[0]) table.tr("h1").td("h1").h1(id=textToId(block[0])).text( block[0]) text = None if toc: toc = table.tr("toc").td("toc").div().table("toc") toc.tr("heading").th().text("Table of contents") continue elif re_h2.match(block[1]): if toc: toc.tr("h2").td().a(href="#" + textToId(block[0])).text( block[0]) table.tr("h2").td("h2").div().h2(id=textToId(block[0])).text( block[0]) text = None continue if len(block) == 1 and block[0] == "[repositories]": text = None repositories = table.tr("repositories").td("repositories").table( "repositories", align="center", cellspacing=0) headings = repositories.tr("headings") headings.th("name").text("Short name") headings.th("path").text("Repository path") cursor = db.cursor() cursor.execute( "SELECT name, path FROM repositories ORDER BY id ASC") first = " first" for name, path in cursor: row = repositories.tr("repository" + first) row.td("name").text(name) row.td("path").text("%s:%s" % (configuration.base.HOSTNAME, path)) first = "" continue if not text: text = table.tr("text").td("text") def translateConfigLinks(text): def linkify(match): item = match.group(1) return "<a href='config?highlight=%s'>%s</a>" % (item, item) return re.sub("CONFIG\\(([^)]+)\\)", linkify, text) def processText(lines): for index, line in enumerate(lines): if line.startswith(" http"): lines[index] = "<a href='%s'>%s</a>" % (line.strip(), line.strip()) return translateConfigLinks("\n".join(lines)).replace( "--", "—") if len(block) > 2 and re_h2.match(block[1]): if toc: toc.tr("h3").td().a(href="#" + textToId(block[0])).text( block[0]) div = text.div() div.h3(id=textToId(block[0])).text(block[0]) block = block[2:] if block[0].startswith("|"): pre = table.tr("pre").td("pre").div().table( "pre").tr().td().preformatted() pre.text("\n".join([line[2:] for line in block])) text = None continue elif block[0].startswith("* ") or block[0].startswith("1 "): if block[0].startswith("* "): items = text.div().ul() else: items = text.div().ol() item = [] for line in block: if line[:2] != ' ': if item: items.li().text("\n".join(item), cdata=True) item = [] else: assert line[:2] == " " item.append(line[2:]) if item: items.li().text("\n".join(item), cdata=True) elif block[0].startswith("? "): items = text.div().dl() term = [] definition = None for line in block: if line[:2] == '? ': if definition: items.dt().text(" ".join(term)) items.dd().text(processText(definition)) definition = None term = [line[2:]] elif line[:2] == '= ': assert term assert definition is None definition = [line[2:]] elif definition is None: term.append(line[2:]) else: definition.append(line[2:]) items.dt().text(" ".join(term)) items.dd().text(processText(definition)) elif block[0].startswith(" "): text_data = translateConfigLinks("\n".join(block)) if block[0].startswith(" <code>"): className = "example" else: className = "hint" text_data = text_data.replace("--", "—") text.div().div(className).text(text_data, cdata=True) else: text.div().text(processText(block), cdata=True)
def addCommitsToReview(db, user, review, commits, new_review=False, commitset=None, pending_mails=None, silent_if_empty=set(), full_merges=set(), replayed_rebases={}, tracked_branch=False): cursor = db.cursor() if not new_review: import index new_commits = log_commitset.CommitSet(commits) old_commits = log_commitset.CommitSet(review.branch.commits) merges = new_commits.getMerges() for merge in merges: # We might have stripped it in a previous pass. if not merge in new_commits: continue tails = filter( lambda sha1: sha1 not in old_commits and sha1 not in merge. parents, new_commits.getTailsFrom(merge)) if tails: if tracked_branch: raise index.IndexException("""\ Merge %s adds merged-in commits. Please push the merge manually and follow the instructions.""" % merge.sha1[:8]) cursor.execute( "SELECT id, confirmed, tail FROM reviewmergeconfirmations WHERE review=%s AND uid=%s AND merge=%s", (review.id, user.id, merge.getId(db))) row = cursor.fetchone() if not row or not row[1]: if not row: cursor.execute( "INSERT INTO reviewmergeconfirmations (review, uid, merge) VALUES (%s, %s, %s) RETURNING id", (review.id, user.id, merge.getId(db))) confirmation_id = cursor.fetchone()[0] merged = set() for tail_sha1 in tails: children = new_commits.getChildren(tail_sha1) while children: child = children.pop() if child not in merged and new_commits.isAncestorOf( child, merge): merged.add(child) children.update( new_commits.getChildren(child) - merged) merged_values = [(confirmation_id, commit.getId(db)) for commit in merged] cursor.executemany( "INSERT INTO reviewmergecontributions (id, merged) VALUES (%s, %s)", merged_values) db.commit() else: confirmation_id = row[0] message = "Merge %s adds merged-in commits:" % merge.sha1[: 8] for tail_sha1 in tails: for parent_sha1 in merge.parents: if parent_sha1 in new_commits: parent = new_commits.get(parent_sha1) if tail_sha1 in new_commits.getTailsFrom( parent): message += "\n %s..%s" % (tail_sha1[:8], parent_sha1[:8]) message += """ Please confirm that this is intended by loading: %s/confirmmerge?id=%d""" % (dbutils.getURLPrefix(db, user), confirmation_id) raise index.IndexException(message) elif row[2] is not None: if row[2] == merge.getId(db): cursor.execute( "SELECT merged FROM reviewmergecontributions WHERE id=%s", (row[0], )) for (merged_id, ) in cursor: merged = gitutils.Commit.fromId( db, review.repository, merged_id) if merged.sha1 in merge.parents: new_commits = new_commits.without([merged]) break else: tail = gitutils.Commit.fromId(db, review.repository, row[2]) cut = [ gitutils.Commit.fromSHA1(db, review.repository, sha1) for sha1 in tail.parents if sha1 in new_commits ] new_commits = new_commits.without(cut) if commitset: commitset &= set(new_commits) commits = [commit for commit in commits if commit in commitset] changesets, silent_commits, silent_changesets = \ createChangesetsForCommits(db, commits, silent_if_empty, full_merges, replayed_rebases) if not new_review: print "Adding %d commit%s to the review at:\n %s" % ( len(commits), len(commits) > 1 and "s" or "", review.getURL(db)) reviewchangesets_values = [(review.id, changeset.id) for changeset in changesets] cursor.executemany( """INSERT INTO reviewchangesets (review, changeset) VALUES (%s, %s)""", reviewchangesets_values) cursor.executemany( """INSERT INTO reviewfiles (review, changeset, file, deleted, inserted) SELECT reviewchangesets.review, reviewchangesets.changeset, fileversions.file, COALESCE(SUM(chunks.deleteCount), 0), COALESCE(SUM(chunks.insertCount), 0) FROM reviewchangesets JOIN fileversions USING (changeset) LEFT OUTER JOIN chunks USING (changeset, file) WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=%s GROUP BY reviewchangesets.review, reviewchangesets.changeset, fileversions.file""", reviewchangesets_values) new_reviewers, new_watchers = assignChanges(db, user, review, changesets=changesets) cursor.execute( "SELECT include FROM reviewrecipientfilters WHERE review=%s AND uid IS NULL", (review.id, )) try: opt_out = cursor.fetchone()[0] is True except: opt_out = True if not new_review: for user_id in new_reviewers: new_reviewuser = dbutils.User.fromId(db, user_id) print "Added reviewer: %s <%s>" % (new_reviewuser.fullname, new_reviewuser.email) if opt_out: # If the user has opted out from receiving e-mails about this # review while only watching it, clear the opt-out now that the # user becomes a reviewer. cursor.execute( "DELETE FROM reviewrecipientfilters WHERE review=%s AND uid=%s AND include=FALSE", (review.id, user_id)) for user_id in new_watchers: new_reviewuser = dbutils.User.fromId(db, user_id) print "Added watcher: %s <%s>" % (new_reviewuser.fullname, new_reviewuser.email) review.incrementSerial(db) reviewing.comment.propagateCommentChains(db, user, review, new_commits, replayed_rebases) if pending_mails is None: pending_mails = [] notify_commits = filter(lambda commit: commit not in silent_commits, commits) notify_changesets = filter( lambda changeset: changeset not in silent_changesets, changesets) if not new_review and notify_changesets: recipients = review.getRecipients(db) for to_user in recipients: pending_mails.extend( mail.sendReviewAddedCommits(db, user, to_user, recipients, review, notify_commits, notify_changesets, tracked_branch=tracked_branch)) mail.sendPendingMails(pending_mails) review.reviewers.extend( [User.fromId(db, user_id) for user_id in new_reviewers]) for user_id in new_watchers: review.watchers[User.fromId(db, user_id)] = "automatic" return True
def renderItem(target): target.span("name").innerHTML(extension.getTitle(db, html=True)) if hosting_user: is_author = manifest and manifest.isAuthor(db, hosting_user) is_sole_author = is_author and len(manifest.authors) == 1 else: is_sole_author = False if extension_error is None: span = target.span("details") span.b().text("Details: ") select = span.select("details", critic_author=extension.getAuthorName(), critic_extension=extension.getName()) select.option(value='', selected="selected" if selected_version is False else None).text("Select version") versions = extension.getVersions() if versions: optgroup = select.optgroup(label="Official Versions") for version in versions: optgroup.option(value="version/%s" % version, selected="selected" if selected_version == version else None).text("%s" % version.upper()) optgroup = select.optgroup(label="Development") optgroup.option(value='live', selected="selected" if selected_version is None else None).text("LIVE") if manifest: is_installed = bool(installed_version) if is_installed: target.span("installed").text(" [installed]") else: is_installed = bool(universal_version) if is_installed: target.span("installed").text(" [installed (universal)]") target.div("description").preformatted().text(manifest.description, linkify=True) if not is_sole_author: authors = target.div("authors") authors.b().text("Author%s:" % ("s" if len(manifest.authors) > 1 else "")) authors.text(", ".join(author.name for author in manifest.getAuthors())) else: is_installed = False div = target.div("description broken").preformatted() if extension_error is None: anchor = div.a(href="loadmanifest?key=%s" % extension.getKey()) anchor.text("[This extension has an invalid MANIFEST file]") else: div.text("[This extension has been deleted or has become inaccessible]") if selected_version is False: return pages = [] injects = [] processcommits = [] filterhooks = [] scheduled = [] if manifest: for role in manifest.roles: if isinstance(role, PageRole): pages.append(role) elif isinstance(role, InjectRole): injects.append(role) elif isinstance(role, ProcessCommitsRole): processcommits.append(role) elif isinstance(role, FilterHookRole): filterhooks.append(role) elif isinstance(role, ScheduledRole): scheduled.append(role) role_table = target.table("roles") if pages: role_table.tr().th(colspan=2).text("Pages") for role in pages: row = role_table.tr() url = "%s/%s" % (dbutils.getURLPrefix(db, user), role.pattern) if is_installed and "*" not in url: row.td("pattern").a(href=url).text(url) else: row.td("pattern").text(url) td = row.td("description") td.text(role.description) if injects: role_table.tr().th(colspan=2).text("Page Injections") for role in injects: row = role_table.tr() row.td("pattern").text("%s/%s" % (dbutils.getURLPrefix(db, user), role.pattern)) td = row.td("description") td.text(role.description) if processcommits: role_table.tr().th(colspan=2).text("ProcessCommits hooks") ul = role_table.tr().td(colspan=2).ul() for role in processcommits: li = ul.li() li.text(role.description) if filterhooks: role_table.tr().th(colspan=2).text("FilterHook hooks") for role in filterhooks: row = role_table.tr() row.td("title").text(role.title) row.td("description").text(role.description) if scheduled: role_table.tr().th(colspan=2).text("Scheduled hooks") for role in scheduled: row = role_table.tr() row.td("pattern").text("%s @ %s" % (role.frequency, role.at)) td = row.td("description") td.text(role.description)
def renderFormatted(db, user, table, lines, toc=False, title_right=None): re_h1 = re.compile("^=+$") re_h2 = re.compile("^-+$") data = { "configuration.URL": dbutils.getURLPrefix(db), "configuration.base.HOSTNAME": configuration.base.HOSTNAME, "configuration.base.SYSTEM_USER_NAME": configuration.base.SYSTEM_USER_NAME, "configuration.base.SYSTEM_GROUP_NAME": configuration.base.SYSTEM_GROUP_NAME, "configuration.paths.CONFIG_DIR": configuration.paths.CONFIG_DIR, "configuration.paths.INSTALL_DIR": configuration.paths.INSTALL_DIR, "configuration.paths.GIT_DIR": configuration.paths.GIT_DIR, } blocks = [] block = [] for line in lines: if line.strip(): block.append(line % data) elif block: blocks.append(block) block = [] else: if block: blocks.append(block) text = None for block in blocks: def textToId(text): return text.lower().replace(" ", "_") if len(block) == 2: if re_h1.match(block[1]): table.setTitle(block[0]) h1 = table.tr("h1").td("h1").h1(id=textToId(block[0])) h1.text(block[0]) if title_right: span_right = h1.span("right") if callable(title_right): title_right(span_right) else: span_right.text(title_right) text = None if toc: toc = table.tr("toc").td("toc").div().table("toc") toc.tr("heading").th().text("Table of contents") continue elif re_h2.match(block[1]): if toc: toc.tr("h2").td().a(href="#" + textToId(block[0])).text(block[0]) table.tr("h2").td("h2").div().h2(id=textToId(block[0])).text(block[0]) text = None continue if len(block) == 1 and block[0] == "[repositories]": text = None repositories = ( table.tr("repositories").td("repositories").table("repositories", align="center", cellspacing=0) ) headings = repositories.tr("headings") headings.th("name").text("Short name") headings.th("path").text("Repository path") cursor = db.cursor() cursor.execute("SELECT name, path FROM repositories ORDER BY id ASC") first = " first" for name, path in cursor: row = repositories.tr("repository" + first) row.td("name").text(name) row.td("path").text(gitutils.Repository.constructURL(db, user, path)) first = "" continue if not text: text = table.tr("text").td("text") def translateConfigLinks(text): def linkify(match): item = match.group(1) return "<a href='config?highlight=%s'>%s</a>" % (item, item) return re.sub("CONFIG\\(([^)]+)\\)", linkify, text) def processText(lines): for index, line in enumerate(lines): if line.startswith(" http"): lines[index] = "<a href='%s'>%s</a>" % (line.strip(), line.strip()) return translateConfigLinks("\n".join(lines)).replace("--", "—") if len(block) > 2 and re_h2.match(block[1]): if toc: toc.tr("h3").td().a(href="#" + textToId(block[0])).text(block[0]) div = text.div() div.h3(id=textToId(block[0])).text(block[0]) block = block[2:] if block[0].startswith("|"): pre = text.div().table("pre").tr().td().preformatted() pre.text("\n".join([line[2:] for line in block])) elif block[0].startswith("* ") or block[0].startswith("1 "): if block[0].startswith("* "): items = text.div().ul() else: items = text.div().ol() item = [] for line in block: if line[:2] != " ": if item: items.li().text("\n".join(item), cdata=True) item = [] else: assert line[:2] == " " item.append(line[2:]) if item: items.li().text("\n".join(item), cdata=True) elif block[0].startswith("? "): items = text.div().dl() term = [] definition = None for line in block: if line[:2] == "? ": if definition: items.dt().text(" ".join(term)) items.dd().text(processText(definition)) definition = None term = [line[2:]] elif line[:2] == "= ": assert term assert definition is None definition = [line[2:]] elif definition is None: term.append(line[2:]) else: definition.append(line[2:]) items.dt().text(" ".join(term)) items.dd().text(processText(definition), cdata=True) elif block[0].startswith(" "): text_data = translateConfigLinks("\n".join(block)) if block[0].startswith(" <code>"): className = "example" else: className = "hint" text_data = text_data.replace("--", "—") text.div().div(className).text(text_data, cdata=True) else: text.div().text(processText(block), cdata=True)
def createCommentChain(db, user, review, chain_type, commit_id=None, origin=None, file_id=None, parent_id=None, child_id=None, old_sha1=None, new_sha1=None, offset=None, count=None): if chain_type == "issue" and review.state != "open": raise OperationFailure( code="reviewclosed", title="Review is closed!", message= "You need to reopen the review before you can raise new issues.") cursor = db.cursor() if file_id is not None and (parent_id == child_id or parent_id is None): cursor.execute( """SELECT 1 FROM reviewchangesets JOIN fileversions USING (changeset) WHERE reviewchangesets.review=%s AND fileversions.file=%s AND fileversions.old_sha1!='0000000000000000000000000000000000000000' AND fileversions.new_sha1!='0000000000000000000000000000000000000000'""", (review.id, file_id)) if cursor.fetchone(): cursor.execute( """SELECT parent, child FROM changesets JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id) JOIN fileversions ON (fileversions.changeset=changesets.id) WHERE fileversions.file=%s AND fileversions.new_sha1=%s""", (file_id, new_sha1)) rows = cursor.fetchall() if not rows: cursor.execute( """SELECT parent, child FROM changesets JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id) JOIN fileversions ON (fileversions.changeset=changesets.id) WHERE fileversions.file=%s AND fileversions.old_sha1=%s""", (file_id, new_sha1)) rows = cursor.fetchall() parent = child = None for row_parent_id, row_child_id in rows: if row_child_id == child_id: parent = gitutils.Commit.fromId(db, review.repository, row_parent_id) child = gitutils.Commit.fromId(db, review.repository, row_child_id) break elif row_parent_id == child_id and parent is None: parent = gitutils.Commit.fromId(db, review.repository, row_parent_id) child = gitutils.Commit.fromId(db, review.repository, row_child_id) if parent and child: url = "/%s/%s..%s?review=%d&file=%d" % ( review.repository.name, parent.sha1[:8], child.sha1[:8], review.id, file_id) link = ( "<p>The link below goes to a diff that can be use to create the comment:</p>" + "<p style='padding-left: 2em'><a href='%s'>%s%s</a></p>" ) % (url, dbutils.getURLPrefix(db), url) else: link = "" raise OperationFailure( code="notsupported", title="File changed in review", message= ("<p>Due to limitations in the code used to create comments, " + "it's only possible to create comments via a diff view if " + "the commented file has been changed in the review.</p>" + link), is_html=True) cursor.execute( """INSERT INTO commentchains (review, uid, type, file, first_commit, last_commit) VALUES (%s, %s, %s, %s, %s, %s) RETURNING id""", (review.id, user.id, chain_type, file_id, child_id, child_id)) chain_id = cursor.fetchone()[0] cursor.execute( """INSERT INTO commentchainlines (chain, uid, commit, sha1, first_line, last_line) VALUES (%s, %s, %s, %s, %s, %s)""", (chain_id, user.id, child_id, new_sha1, offset, offset + count - 1)) elif file_id is not None: parents_returned = set() def getFileParent(new_sha1): cursor.execute( """SELECT changesets.id, fileversions.old_sha1 FROM changesets, reviewchangesets, fileversions WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=changesets.id AND fileversions.changeset=changesets.id AND fileversions.file=%s AND fileversions.new_sha1=%s""", [review.id, file_id, new_sha1]) try: changeset_id, old_sha1 = cursor.fetchone() if old_sha1 in parents_returned: return None, None parents_returned.add(old_sha1) return changeset_id, old_sha1 except: return None, None children_returned = set() def getFileChild(old_sha1): cursor.execute( """SELECT changesets.id, fileversions.new_sha1 FROM changesets, reviewchangesets, fileversions WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=changesets.id AND fileversions.changeset=changesets.id AND fileversions.file=%s AND fileversions.old_sha1=%s""", [review.id, file_id, old_sha1]) try: changeset_id, new_sha1 = cursor.fetchone() if new_sha1 in children_returned: return None, None children_returned.add(new_sha1) return changeset_id, new_sha1 except: return None, None cursor.execute( """SELECT changesets.id FROM changesets, reviewchangesets, fileversions WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=changesets.id AND changesets.child=%s AND fileversions.changeset=changesets.id AND fileversions.file=%s AND fileversions.old_sha1=%s AND fileversions.new_sha1=%s""", [review.id, child_id, file_id, old_sha1, new_sha1]) row = cursor.fetchone() if not row: if origin == "old": cursor.execute( """SELECT changesets.id FROM changesets, reviewchangesets, fileversions WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=changesets.id AND fileversions.changeset=changesets.id AND fileversions.file=%s AND fileversions.old_sha1=%s""", [review.id, file_id, old_sha1]) else: cursor.execute( """SELECT changesets.id FROM changesets, reviewchangesets, fileversions WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=changesets.id AND fileversions.changeset=changesets.id AND fileversions.file=%s AND fileversions.new_sha1=%s""", [review.id, file_id, new_sha1]) row = cursor.fetchone() primary_changeset_id = row[0] sha1s_older = {} sha1s_newer = {old_sha1: (primary_changeset_id, new_sha1)} sha1 = new_sha1 while True: changeset_id, next_sha1 = getFileParent(sha1) if changeset_id: sha1s_older[sha1] = changeset_id, next_sha1 sha1s_newer[next_sha1] = changeset_id, sha1 sha1 = next_sha1 else: break sha1 = new_sha1 while True: changeset_id, next_sha1 = getFileChild(sha1) if changeset_id: sha1s_newer[sha1] = changeset_id, next_sha1 sha1 = next_sha1 else: break commentchainlines_values = [] processed = set() def searchOrigin(changeset_id, sha1, search_space, first_line, last_line): try: while sha1 not in processed: processed.add(sha1) changeset_id, next_sha1 = search_space[sha1] changeset = changeset_load.loadChangeset( db, review.repository, changeset_id, filtered_file_ids=set([file_id])) if len(changeset.child.parents) > 1: break verdict, next_first_line, next_last_line = updateCommentChain( first_line, last_line, changeset.files[0].chunks, forward) if verdict == "modified": break sha1 = next_sha1 first_line = next_first_line last_line = next_last_line except: pass return changeset_id, sha1, first_line, last_line first_line = offset last_line = offset + count - 1 if origin == 'old': changeset_id, sha1, first_line, last_line = searchOrigin( primary_changeset_id, old_sha1, sha1s_older, first_line, last_line) commit_id = diff.Changeset.fromId(db, review.repository, changeset_id).parent.id else: changeset_id, sha1, first_line, last_line = searchOrigin( primary_changeset_id, new_sha1, sha1s_older, first_line, last_line) commit_id = diff.Changeset.fromId(db, review.repository, changeset_id).child.id commentchainlines_values.append( (user.id, commit_id, sha1, first_line, last_line)) processed = set() processed.add(sha1) while sha1 in sha1s_newer: changeset_id, sha1 = sha1s_newer[sha1] if sha1 in processed: break else: processed.add(sha1) changeset = changeset_load.loadChangeset(db, review.repository, changeset_id, filtered_file_ids=set( [file_id])) if len(changeset.child.parents) != 1: chunks = diff.parse.parseDifferences( review.repository, from_commit=changeset.parent, to_commit=changeset.child, selected_path=dbutils.describe_file(db, file_id)).chunks else: chunks = changeset.files[0].chunks verdict, first_line, last_line = updateCommentChain( first_line, last_line, chunks) if verdict == "transfer": commentchainlines_values.append( (user.id, changeset.child.getId(db), sha1, first_line, last_line)) else: break cursor.execute( "INSERT INTO commentchains (review, uid, type, origin, file, first_commit, last_commit) VALUES (%s, %s, %s, %s, %s, %s, %s) RETURNING id", [ review.id, user.id, chain_type, origin, file_id, parent_id, child_id ]) chain_id = cursor.fetchone()[0] try: cursor.executemany( "INSERT INTO commentchainlines (chain, uid, commit, sha1, first_line, last_line) VALUES (%s, %s, %s, %s, %s, %s)", [(chain_id, ) + values for values in commentchainlines_values]) except: raise Exception, repr(commentchainlines_values) elif commit_id is not None: commit = gitutils.Commit.fromId(db, review.repository, commit_id) cursor.execute( "INSERT INTO commentchains (review, uid, type, first_commit, last_commit) VALUES (%s, %s, %s, %s, %s) RETURNING id", [review.id, user.id, chain_type, commit_id, commit_id]) chain_id = cursor.fetchone()[0] cursor.execute( "INSERT INTO commentchainlines (chain, uid, commit, sha1, first_line, last_line) VALUES (%s, %s, %s, %s, %s, %s)", (chain_id, user.id, commit_id, commit.sha1, offset, offset + count - 1)) else: cursor.execute( "INSERT INTO commentchains (review, uid, type) VALUES (%s, %s, %s) RETURNING id", [review.id, user.id, chain_type]) chain_id = cursor.fetchone()[0] commentchainusers = set([user.id] + map(int, review.owners)) if file_id is not None: filters = Filters() filters.load(db, review=review) for user_id in filters.listUsers(db, file_id): commentchainusers.add(user_id) cursor.executemany( "INSERT INTO commentchainusers (chain, uid) VALUES (%s, %s)", [(chain_id, user_id) for user_id in commentchainusers]) return chain_id
yield str(document) return except: error_message = traceback.format_exc() environ["wsgi.errors"].write(error_message) db.rollback() if not user or user.hasRole(db, "developer"): title = "You broke the system again:" body = error_message body_html = "<pre>%s</pre>" % htmlify(body) else: prefix = dbutils.getURLPrefix(db) x_forwarded_host = req.getRequestHeader("X-Forwarded-Host") if x_forwarded_host: prefix = "https://" + x_forwarded_host url = "%s/%s?%s" % (prefix, req.path, req.query) mailutils.sendExceptionMessage("wsgi", ("User: %s\nMethod: %s\nPath: %s\nQuery: %s\nURL: %s\n\n%s" % (req.user, req.method, req.path, req.query, url, error_message))) title = "Darn! It seems we have a problem..." body = "A message has been sent to the system administrator(s) with details about the problem." body_html = body if not req.isStarted(): req.setStatus(500)
def renderFormatted(db, user, table, lines, toc=False, title_right=None): re_h1 = re.compile("^=+$") re_h2 = re.compile("^-+$") data = { "configuration.URL": dbutils.getURLPrefix(db, user), "configuration.base.HOSTNAME": configuration.base.HOSTNAME, "configuration.base.SYSTEM_USER_NAME": configuration.base.SYSTEM_USER_NAME, "configuration.base.SYSTEM_GROUP_NAME": configuration.base.SYSTEM_GROUP_NAME, "configuration.paths.CONFIG_DIR": configuration.paths.CONFIG_DIR, "configuration.paths.INSTALL_DIR": configuration.paths.INSTALL_DIR, "configuration.paths.DATA_DIR": configuration.paths.DATA_DIR, "configuration.paths.GIT_DIR": configuration.paths.GIT_DIR } references = {} blocks = [] block = [] for line in lines: match = re.match(r'\[(.*?)\]: (.*?)(?: "(.*?)")?$', line) if match: name, url, title = match.groups() references[name] = (url, title) continue if line.strip(): block.append(line % data) elif block: blocks.append(block) block = [] else: if block: blocks.append(block) text = None for block in blocks: def textToId(text): return text.lower().replace(' ', '_') if len(block) == 2: if re_h1.match(block[1]): table.setTitle(block[0]) h1 = table.tr("h1").td("h1").h1(id=textToId(block[0])) h1.text(block[0]) if title_right: span_right = h1.span("right") if callable(title_right): title_right(span_right) else: span_right.text(title_right) text = None if toc: toc = table.tr("toc").td("toc").div().table("toc callout") toc.tr("heading").th().text("Table of contents") continue elif re_h2.match(block[1]): if toc: toc.tr("h2").td().a(href="#" + textToId(block[0])).text( block[0]) table.tr("h2").td("h2").div().h2(id=textToId(block[0])).text( block[0]) text = None continue if len(block) == 1 and block[0] == "[repositories]": text = None repositories = table.tr().td().table("repositories callout") headings = repositories.thead().tr() headings.th("name").text("Short name") headings.th("path").text("Repository path") repositories.tr().td(colspan=2) cursor = db.cursor() cursor.execute( "SELECT name, path FROM repositories ORDER BY id ASC") for name, path in cursor: row = repositories.tr("repository") row.td("name").text(name) row.td("path").text( gitutils.Repository.constructURL(db, user, path)) continue if not text: text = table.tr("text").td("text") def translateLinks(text): def linkify(match): config_item, reference_text, reference_name = match.groups() if config_item: url = "/config?highlight=%s" % config_item text = config_item title = None else: reference_name = re.sub(r"\s+", " ", reference_name) assert reference_name in references, reference_name url, title = references[reference_name] text = reference_text link = "<a href=%s" % htmlutils.htmlify(url, True) if title: link += " title=%s" % htmlutils.htmlify(title, True) return link + ">%s</a>" % htmlutils.htmlify(text) return re.sub(r"CONFIG\(([^)]+)\)|\[(.*?)\]\n?\[(.*?)\]", linkify, text, flags=re.DOTALL) def processText(lines): if isinstance(lines, basestring): lines = [lines] for index, line in enumerate(lines): if line.startswith(" http"): lines[index] = "<a href='%s'>%s</a>" % (line.strip(), line.strip()) text = translateLinks("\n".join(lines)) # Replace double dashes with —, but only if they are # surrounded by either spaces or word characters on both sides. # # We don't want to translate the double dashes in a # --command-line-argument used in the text. text = re.sub(r"(^| )--( |$)", r"\1—\2", text, flags=re.MULTILINE) text = re.sub(r"(\w)--(\w)", r"\1—\2", text) return text if len(block) > 2 and re_h2.match(block[1]): if toc: toc.tr("h3").td().a(href="#" + textToId(block[0])).text( block[0]) div = text.div() div.h3(id=textToId(block[0])).text(block[0]) block = block[2:] if block[0].startswith("|"): pre = text.div().table("pre callout").tr().td().preformatted() contents = "\n".join([line[2:] for line in block]) if block[0].startswith("||"): pre.innerHTML(contents) else: pre.text(contents) elif block[0].startswith("* ") or block[0].startswith("1 "): if block[0].startswith("* "): items = text.div().ul() else: items = text.div().ol() item = [] for line in block: if line[:2] != ' ': if item: items.li().text(processText(item), cdata=True) item = [] else: assert line[:2] == " " item.append(line[2:]) if item: items.li().text(processText(item), cdata=True) elif block[0].startswith("? "): items = text.div().dl() term = [] definition = None for line in block: if line[:2] == '? ': if definition: items.dt().text(processText(" ".join(term)), cdata=True) items.dd().text(processText(definition), cdata=True) definition = None term = [line[2:]] elif line[:2] == '= ': assert term assert definition is None definition = [line[2:]] elif definition is None: term.append(line[2:]) else: definition.append(line[2:]) items.dt().text(processText(" ".join(term)), cdata=True) items.dd().text(processText(definition), cdata=True) elif block[0].startswith(" "): text_data = translateLinks("\n".join(block)) if block[0].startswith(" <code>"): className = "example" else: className = "hint" text_data = text_data.replace("--", "—") text.div().div(className).text(text_data, cdata=True) else: text.div().text(processText(block), cdata=True)
def createCommentChain(db, user, review, chain_type, commit_id=None, origin=None, file_id=None, parent_id=None, child_id=None, old_sha1=None, new_sha1=None, offset=None, count=None): if chain_type == "issue" and review.state != "open": raise OperationFailure(code="reviewclosed", title="Review is closed!", message="You need to reopen the review before you can raise new issues.") cursor = db.cursor() if file_id is not None and (parent_id == child_id or parent_id is None): cursor.execute("""SELECT 1 FROM reviewchangesets JOIN fileversions USING (changeset) WHERE reviewchangesets.review=%s AND fileversions.file=%s AND fileversions.old_sha1!='0000000000000000000000000000000000000000' AND fileversions.new_sha1!='0000000000000000000000000000000000000000'""", (review.id, file_id)) if cursor.fetchone(): cursor.execute("""SELECT parent, child FROM changesets JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id) JOIN fileversions ON (fileversions.changeset=changesets.id) WHERE fileversions.file=%s AND fileversions.new_sha1=%s""", (file_id, new_sha1)) rows = cursor.fetchall() if not rows: cursor.execute("""SELECT parent, child FROM changesets JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id) JOIN fileversions ON (fileversions.changeset=changesets.id) WHERE fileversions.file=%s AND fileversions.old_sha1=%s""", (file_id, new_sha1)) rows = cursor.fetchall() parent = child = None for row_parent_id, row_child_id in rows: if row_child_id == child_id: parent = gitutils.Commit.fromId(db, review.repository, row_parent_id) child = gitutils.Commit.fromId(db, review.repository, row_child_id) break elif row_parent_id == child_id and parent is None: parent = gitutils.Commit.fromId(db, review.repository, row_parent_id) child = gitutils.Commit.fromId(db, review.repository, row_child_id) if parent and child: url = "/%s/%s..%s?review=%d&file=%d" % (review.repository.name, parent.sha1[:8], child.sha1[:8], review.id, file_id) link = ("<p>The link below goes to a diff that can be use to create the comment:</p>" + "<p style='padding-left: 2em'><a href='%s'>%s%s</a></p>") % (url, dbutils.getURLPrefix(db), url) else: link = "" raise OperationFailure(code="notsupported", title="File changed in review", message=("<p>Due to limitations in the code used to create comments, " + "it's only possible to create comments via a diff view if " + "the commented file has been changed in the review.</p>" + link), is_html=True) cursor.execute("""INSERT INTO commentchains (review, uid, type, file, first_commit, last_commit) VALUES (%s, %s, %s, %s, %s, %s) RETURNING id""", (review.id, user.id, chain_type, file_id, child_id, child_id)) chain_id = cursor.fetchone()[0] cursor.execute("""INSERT INTO commentchainlines (chain, uid, commit, sha1, first_line, last_line) VALUES (%s, %s, %s, %s, %s, %s)""", (chain_id, user.id, child_id, new_sha1, offset, offset + count - 1)) elif file_id is not None: parents_returned = set() def getFileParent(new_sha1): cursor.execute("""SELECT changesets.id, fileversions.old_sha1 FROM changesets, reviewchangesets, fileversions WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=changesets.id AND fileversions.changeset=changesets.id AND fileversions.file=%s AND fileversions.new_sha1=%s""", [review.id, file_id, new_sha1]) try: changeset_id, old_sha1 = cursor.fetchone() if old_sha1 in parents_returned: return None, None parents_returned.add(old_sha1) return changeset_id, old_sha1 except: return None, None children_returned = set() def getFileChild(old_sha1): cursor.execute("""SELECT changesets.id, fileversions.new_sha1 FROM changesets, reviewchangesets, fileversions WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=changesets.id AND fileversions.changeset=changesets.id AND fileversions.file=%s AND fileversions.old_sha1=%s""", [review.id, file_id, old_sha1]) try: changeset_id, new_sha1 = cursor.fetchone() if new_sha1 in children_returned: return None, None children_returned.add(new_sha1) return changeset_id, new_sha1 except: return None, None cursor.execute("""SELECT changesets.id FROM changesets, reviewchangesets, fileversions WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=changesets.id AND changesets.child=%s AND fileversions.changeset=changesets.id AND fileversions.file=%s AND fileversions.old_sha1=%s AND fileversions.new_sha1=%s""", [review.id, child_id, file_id, old_sha1, new_sha1]) row = cursor.fetchone() if not row: if origin == "old": cursor.execute("""SELECT changesets.id FROM changesets, reviewchangesets, fileversions WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=changesets.id AND fileversions.changeset=changesets.id AND fileversions.file=%s AND fileversions.old_sha1=%s""", [review.id, file_id, old_sha1]) else: cursor.execute("""SELECT changesets.id FROM changesets, reviewchangesets, fileversions WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=changesets.id AND fileversions.changeset=changesets.id AND fileversions.file=%s AND fileversions.new_sha1=%s""", [review.id, file_id, new_sha1]) row = cursor.fetchone() primary_changeset_id = row[0] sha1s_older = { } sha1s_newer = { old_sha1: (primary_changeset_id, new_sha1) } sha1 = new_sha1 while True: changeset_id, next_sha1 = getFileParent(sha1) if changeset_id: sha1s_older[sha1] = changeset_id, next_sha1 sha1s_newer[next_sha1] = changeset_id, sha1 sha1 = next_sha1 else: break sha1 = new_sha1 while True: changeset_id, next_sha1 = getFileChild(sha1) if changeset_id: sha1s_newer[sha1] = changeset_id, next_sha1 sha1 = next_sha1 else: break commentchainlines_values = [] processed = set() def searchOrigin(changeset_id, sha1, search_space, first_line, last_line): try: while sha1 not in processed: processed.add(sha1) changeset_id, next_sha1 = search_space[sha1] changeset = changeset_load.loadChangeset(db, review.repository, changeset_id, filtered_file_ids=set([file_id])) if len(changeset.child.parents) > 1: break verdict, next_first_line, next_last_line = updateCommentChain(first_line, last_line, changeset.files[0].chunks, forward) if verdict == "modified": break sha1 = next_sha1 first_line = next_first_line last_line = next_last_line except: pass return changeset_id, sha1, first_line, last_line first_line = offset last_line = offset + count - 1 if origin == 'old': changeset_id, sha1, first_line, last_line = searchOrigin(primary_changeset_id, old_sha1, sha1s_older, first_line, last_line) commit_id = diff.Changeset.fromId(db, review.repository, changeset_id).parent.id else: changeset_id, sha1, first_line, last_line = searchOrigin(primary_changeset_id, new_sha1, sha1s_older, first_line, last_line) commit_id = diff.Changeset.fromId(db, review.repository, changeset_id).child.id commentchainlines_values.append((user.id, commit_id, sha1, first_line, last_line)) processed = set() processed.add(sha1) while sha1 in sha1s_newer: changeset_id, sha1 = sha1s_newer[sha1] if sha1 in processed: break else: processed.add(sha1) changeset = changeset_load.loadChangeset(db, review.repository, changeset_id, filtered_file_ids=set([file_id])) if len(changeset.child.parents) != 1: chunks = diff.parse.parseDifferences(review.repository, from_commit=changeset.parent, to_commit=changeset.child, selected_path=dbutils.describe_file(db, file_id)).chunks else: chunks = changeset.files[0].chunks verdict, first_line, last_line = updateCommentChain(first_line, last_line, chunks) if verdict == "transfer": commentchainlines_values.append((user.id, changeset.child.getId(db), sha1, first_line, last_line)) else: break cursor.execute("INSERT INTO commentchains (review, uid, type, origin, file, first_commit, last_commit) VALUES (%s, %s, %s, %s, %s, %s, %s) RETURNING id", [review.id, user.id, chain_type, origin, file_id, parent_id, child_id]) chain_id = cursor.fetchone()[0] try: cursor.executemany("INSERT INTO commentchainlines (chain, uid, commit, sha1, first_line, last_line) VALUES (%s, %s, %s, %s, %s, %s)", [(chain_id,) + values for values in commentchainlines_values]) except: raise Exception, repr(commentchainlines_values) elif commit_id is not None: commit = gitutils.Commit.fromId(db, review.repository, commit_id) cursor.execute("INSERT INTO commentchains (review, uid, type, first_commit, last_commit) VALUES (%s, %s, %s, %s, %s) RETURNING id", [review.id, user.id, chain_type, commit_id, commit_id]) chain_id = cursor.fetchone()[0] cursor.execute("INSERT INTO commentchainlines (chain, uid, commit, sha1, first_line, last_line) VALUES (%s, %s, %s, %s, %s, %s)", (chain_id, user.id, commit_id, commit.sha1, offset, offset + count - 1)) else: cursor.execute("INSERT INTO commentchains (review, uid, type) VALUES (%s, %s, %s) RETURNING id", [review.id, user.id, chain_type]) chain_id = cursor.fetchone()[0] commentchainusers = set([user.id] + map(int, review.owners)) if file_id is not None: filters = Filters() filters.load(db, review=review) for user_id in filters.listUsers(db, file_id): commentchainusers.add(user_id) cursor.executemany("INSERT INTO commentchainusers (chain, uid) VALUES (%s, %s)", [(chain_id, user_id) for user_id in commentchainusers]) return chain_id
def commitRangeFromReview(db, user, review, filter, file_ids): edges = cursor = db.cursor() if filter == "pending": cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child FROM changesets JOIN reviewfiles ON (reviewfiles.changeset=changesets.id) JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id) WHERE reviewfiles.review=%s AND reviewuserfiles.uid=%s AND reviewfiles.state='pending'""", (review.id, user.id)) elif filter == "reviewable": cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child FROM changesets JOIN reviewfiles ON (reviewfiles.changeset=changesets.id) JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id) WHERE reviewfiles.review=%s AND reviewuserfiles.uid=%s""", (review.id, user.id)) elif filter == "relevant": filters = review_filters.Filters() filters.load(db, review=review, user=user) cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child, reviewfiles.file, reviewuserfiles.uid IS NOT NULL FROM changesets JOIN reviewfiles ON (reviewfiles.changeset=changesets.id) LEFT OUTER JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id AND reviewuserfiles.uid=%s) WHERE reviewfiles.review=%s""", (user.id, review.id)) edges = set() for parent_id, child_id, file_id, is_reviewer in cursor: if is_reviewer or filters.isRelevant(db, user, file_id): edges.add((parent_id, child_id)) elif filter == "files": assert len(file_ids) != 0 cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child FROM changesets JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id) JOIN fileversions ON (fileversions.changeset=changesets.id) WHERE reviewchangesets.review=%s AND fileversions.file=ANY (%s)""", (review.id, list(file_ids))) else: raise Exception, "invalid filter: %s" % filter listed_commits = set() with_pending = set() for parent_id, child_id in edges: listed_commits.add(child_id) with_pending.add((parent_id, child_id)) if len(listed_commits) == 1: return None, gitutils.Commit.fromId(db, review.repository, child_id).sha1, list(listed_commits), listed_commits if filter in ("reviewable", "relevant", "files"): cursor.execute("SELECT child FROM changesets JOIN reviewchangesets ON (changeset=id) WHERE review=%s", (review.id,)) all_commits = [gitutils.Commit.fromId(db, review.repository, commit_id) for (commit_id,) in cursor] commitset = CommitSet(review.branch.commits) tails = commitset.getFilteredTails(review.repository) if len(commitset) == 0: raise Exception, "empty commit-set" elif len(tails) > 1: ancestor = review.repository.getCommonAncestor(tails) paths = [] cursor.execute("SELECT DISTINCT file FROM reviewfiles WHERE review=%s", (review.id,)) files_in_review = set(file_id for (file_id,) in cursor) if filter == "files": files_in_review &= file_ids paths_in_review = set(dbutils.describe_file(db, file_id) for file_id in files_in_review) paths_in_upstreams = set() for tail in tails: paths_in_upstream = set(review.repository.run("diff", "--name-only", "%s..%s" % (ancestor, tail)).splitlines()) paths_in_upstreams |= paths_in_upstream paths.append((tail, paths_in_upstream)) overlapping_changes = paths_in_review & paths_in_upstreams if overlapping_changes: candidates = [] for index1, data in enumerate(paths): for index2, (tail, paths_in_upstream) in enumerate(paths): if index1 != index2 and paths_in_upstream & paths_in_review: break else: candidates.append(data) else: candidates = paths if not candidates: paths.sort(cmp=lambda a, b: cmp(len(a[1]), len(b[1]))) url = "/%s/%s..%s?file=%s" % (review.repository.name, paths[0][0][:8], review.branch.head.sha1[:8], ",".join(map(str, sorted(files_in_review)))) message = """\ <p>It is not possible to generate a diff of the requested set of commits that contains only changes from those commits.</p> <p>The following files would contain unrelated changes:<p> <pre style='padding-left: 2em'>%s</pre> <p>You can use the URL below if you want to view this diff anyway, including the unrelated changes.</p> <pre style='padding-left: 2em'><a href='%s'>%s%s</a></pre>""" % ("\n".join(sorted(overlapping_changes)), url, dbutils.getURLPrefix(db), url) raise page.utils.DisplayMessage(title="Impossible Diff", body=message, review=review, html=True) else: candidates.sort(cmp=lambda a, b: cmp(len(b[1]), len(a[1]))) return candidates[0][0], review.branch.head.sha1, all_commits, listed_commits elif len(tails) == 0: raise Exception, "impossible commit-set (%r)" % commitset return tails.pop(), review.branch.head.sha1, all_commits, listed_commits
def createBranch(user, repository, name, head): processCommits(repository.name, head) cursor = db.cursor() def commit_id(sha1): cursor.execute("SELECT id FROM commits WHERE sha1=%s", [sha1]) return cursor.fetchone()[0] components = name.split("/") for index in range(1, len(components)): try: repository.revparse("refs/heads/%s" % "/".join(components[:index])) except: continue message = ("Cannot create branch with name '%s' since there is already a branch named '%s' in the repository." % (name, "/".join(components[:index]))) raise IndexException, textutils.reflow(message, line_length=80 - len("remote: ")) if name.startswith("r/"): try: review_id = int(name[2:]) cursor.execute("SELECT branches.name FROM reviews JOIN branches ON (branches.id=reviews.branch) WHERE reviews.id=%s", (review_id,)) row = cursor.fetchone() message = "Refusing to create review named as a number." if row: message += "\nDid you mean to push to the branch '%s', perhaps?" % row[0] raise IndexException, message except ValueError: pass if user.getPreference(db, "review.createViaPush"): the_commit = gitutils.Commit.fromSHA1(db, repository, head, commit_id(head)) all_commits = [the_commit] review = review_utils.createReview(db, user, repository, all_commits, name, the_commit.summary(), None, via_push=True) print "Submitted review: %s/r/%d" % (dbutils.getURLPrefix(db), review.id) if review.reviewers: print " Reviewers:" for reviewer in review.reviewers: print " %s <%s>" % (reviewer.fullname, reviewer.email) if review.watchers: print " Watchers:" for watcher in review.watchers: print " %s <%s>" % (watcher.fullname, watcher.email) if configuration.extensions.ENABLED: if extensions.executeProcessCommits(db, user, review, all_commits, None, the_commit, stdout): print print "Thank you!" return True else: raise IndexException, "Refusing to create review; user preference 'review.createViaPush' is not enabled." sha1 = head base = None tail = None cursor.execute("""SELECT 1 FROM reachable JOIN branches ON (branches.id=reachable.branch) JOIN repositories ON (repositories.id=branches.repository) WHERE repositories.id=%s LIMIT 1""", (repository.id,)) if cursor.fetchone(): def reachable(sha1): cursor.execute("""SELECT branches.id FROM branches JOIN reachable ON (reachable.branch=branches.id) JOIN commits ON (commits.id=reachable.commit) WHERE branches.repository=%s AND branches.type='normal' AND commits.sha1=%s ORDER BY reachable.branch ASC LIMIT 1""", (repository.id, sha1)) return cursor.fetchone() else: def reachable(sha1): return None commit_map = {} commit_list = [] row = reachable(sha1) if row: # Head of branch is reachable from an existing branch. Could be because # this branch is actually empty (just created with no "own" commits) or # it could have been merged into some other already existing branch. We # can't tell, so we just record it as empty. base = row[0] tail = sha1 else: stack = [] while True: if sha1 not in commit_map: commit = gitutils.Commit.fromSHA1(db, repository, sha1) commit_map[sha1] = commit commit_list.append(commit) for sha1 in commit.parents: if sha1 not in commit_map: row = reachable(sha1) if not row: stack.append(sha1) elif base is None: base = row[0] tail = sha1 base_chain = [base] while True: cursor.execute("SELECT base FROM branches WHERE id=%s", (base_chain[-1],)) next = cursor.fetchone()[0] if next is None: break else: base_chain.append(next) def reachable(sha1): cursor.execute("""SELECT 1 FROM reachable JOIN commits ON (commits.id=reachable.commit) WHERE reachable.branch=ANY (%s) AND commits.sha1=%s""", (base_chain, sha1)) return cursor.fetchone() if stack: sha1 = stack.pop(0) else: break if len(commit_list) % 10000 > 1000: stdout.write("\n") stdout.flush() if not base: cursor.execute("INSERT INTO branches (repository, name, head) VALUES (%s, %s, %s) RETURNING id", (repository.id, name, commit_id(head))) branch_id = cursor.fetchone()[0] else: cursor.execute("INSERT INTO branches (repository, name, head, base, tail) VALUES (%s, %s, %s, %s, %s) RETURNING id", (repository.id, name, commit_id(head), base, commit_id(tail))) branch_id = cursor.fetchone()[0] cursor.execute("SELECT name FROM branches WHERE id=%s", [base]) print "Added branch based on %s containing %d commit%s:" % (cursor.fetchone()[0], len(commit_list), "s" if len(commit_list) > 1 else "") print " %s/log?repository=%d&branch=%s" % (dbutils.getURLPrefix(db), repository.id, name) if len(commit_list) > 1: print "To create a review of all %d commits:" % len(commit_list) else: print "To create a review of the commit:" print " %s/createreview?repository=%d&branch=%s" % (dbutils.getURLPrefix(db), repository.id, name) reachable_values = [(branch_id, commit.sha1) for commit in commit_list] cursor.executemany("INSERT INTO reachable (branch, commit) SELECT %s, id FROM commits WHERE sha1=%s", reachable_values) if isinstance(user, str): user_name = user else: user_name = user.name if not repository.hasMainBranch() and user_name == configuration.base.SYSTEM_USER_NAME: cursor.execute("UPDATE repositories SET branch=%s WHERE id=%s", (branch_id, repository.id))
def renderFormatted(db, user, table, lines, toc=False, title_right=None): re_h1 = re.compile("^=+$") re_h2 = re.compile("^-+$") data = { "configuration.URL": dbutils.getURLPrefix(db, user), "configuration.base.HOSTNAME": configuration.base.HOSTNAME, "configuration.base.SYSTEM_USER_NAME": configuration.base.SYSTEM_USER_NAME, "configuration.base.SYSTEM_GROUP_NAME": configuration.base.SYSTEM_GROUP_NAME, "configuration.paths.CONFIG_DIR": configuration.paths.CONFIG_DIR, "configuration.paths.INSTALL_DIR": configuration.paths.INSTALL_DIR, "configuration.paths.DATA_DIR": configuration.paths.DATA_DIR, "configuration.paths.GIT_DIR": configuration.paths.GIT_DIR } references = {} blocks = [] block = [] for line in lines: match = re.match(r'\[(.*?)\]: (.*?)(?: "(.*?)")?$', line) if match: name, url, title = match.groups() references[name] = (url, title) continue if line.strip(): block.append(line % data) elif block: blocks.append(block) block = [] else: if block: blocks.append(block) text = None for block in blocks: def textToId(text): return text.lower().replace(' ', '_') if len(block) == 2: if re_h1.match(block[1]): table.setTitle(block[0]) h1 = table.tr("h1").td("h1").h1(id=textToId(block[0])) h1.text(block[0]) if title_right: span_right = h1.span("right") if callable(title_right): title_right(span_right) else: span_right.text(title_right) text = None if toc: toc = table.tr("toc").td("toc").div().table("toc callout") toc.tr("heading").th().text("Table of contents") continue elif re_h2.match(block[1]): if toc: toc.tr("h2").td().a(href="#" + textToId(block[0])).text(block[0]) table.tr("h2").td("h2").div().h2(id=textToId(block[0])).text(block[0]) text = None continue if len(block) == 1 and block[0] == "[repositories]": text = None repositories = table.tr().td().table("repositories callout") headings = repositories.thead().tr() headings.th("name").text("Short name") headings.th("path").text("Repository path") repositories.tr().td(colspan=2) cursor = db.cursor() cursor.execute("SELECT name, path FROM repositories ORDER BY id ASC") for name, path in cursor: row = repositories.tr("repository") row.td("name").text(name) row.td("path").text(gitutils.Repository.constructURL(db, user, path)) continue if not text: text = table.tr("text").td("text") def translateLinks(text): def linkify(match): config_item, reference_text, reference_name = match.groups() if config_item: url = "/config?highlight=%s" % config_item text = config_item title = None else: reference_name = re.sub(r"\s+", " ", reference_name) assert reference_name in references, reference_name url, title = references[reference_name] text = reference_text link = "<a href=%s" % htmlutils.htmlify(url, True) if title: link += " title=%s" % htmlutils.htmlify(title, True) return link + ">%s</a>" % htmlutils.htmlify(text) return re.sub(r"CONFIG\(([^)]+)\)|\[(.*?)\]\n?\[(.*?)\]", linkify, text, flags=re.DOTALL) def processText(lines): if isinstance(lines, basestring): lines = [lines] for index, line in enumerate(lines): if line.startswith(" http"): lines[index] = "<a href='%s'>%s</a>" % (line.strip(), line.strip()) text = translateLinks("\n".join(lines)) # Replace double dashes with —, but only if they are # surrounded by either spaces or word characters on both sides. # # We don't want to translate the double dashes in a # --command-line-argument used in the text. text = re.sub(r"(^| )--( |$)", r"\1—\2", text, flags=re.MULTILINE) text = re.sub(r"(\w)--(\w)", r"\1—\2", text) return text if len(block) > 2 and re_h2.match(block[1]): if toc: toc.tr("h3").td().a(href="#" + textToId(block[0])).text(block[0]) div = text.div() div.h3(id=textToId(block[0])).text(block[0]) block = block[2:] if block[0].startswith("|"): pre = text.div().table("pre callout").tr().td().preformatted() pre.text("\n".join([line[2:] for line in block])) elif block[0].startswith("* ") or block[0].startswith("1 "): if block[0].startswith("* "): items = text.div().ul() else: items = text.div().ol() item = [] for line in block: if line[:2] != ' ': if item: items.li().text(processText(item), cdata=True) item = [] else: assert line[:2] == " " item.append(line[2:]) if item: items.li().text(processText(item), cdata=True) elif block[0].startswith("? "): items = text.div().dl() term = [] definition = None for line in block: if line[:2] == '? ': if definition: items.dt().text(processText(" ".join(term)), cdata=True) items.dd().text(processText(definition), cdata=True) definition = None term = [line[2:]] elif line[:2] == '= ': assert term assert definition is None definition = [line[2:]] elif definition is None: term.append(line[2:]) else: definition.append(line[2:]) items.dt().text(processText(" ".join(term)), cdata=True) items.dd().text(processText(definition), cdata=True) elif block[0].startswith(" "): text_data = translateLinks("\n".join(block)) if block[0].startswith(" <code>"): className = "example" else: className = "hint" text_data = text_data.replace("--", "—") text.div().div(className).text(text_data, cdata=True) else: text.div().text(processText(block), cdata=True)
def addCommitsToReview(db, user, review, commits, new_review=False, commitset=None, pending_mails=None, silent_if_empty=set(), full_merges=set(), replayed_rebases={}, tracked_branch=False): cursor = db.cursor() if not new_review: import index new_commits = log_commitset.CommitSet(commits) old_commits = log_commitset.CommitSet(review.branch.commits) merges = new_commits.getMerges() for merge in merges: # We might have stripped it in a previous pass. if not merge in new_commits: continue tails = filter(lambda sha1: sha1 not in old_commits and sha1 not in merge.parents, new_commits.getTailsFrom(merge)) if tails: if tracked_branch: raise index.IndexException("""\ Merge %s adds merged-in commits. Please push the merge manually and follow the instructions.""" % merge.sha1[:8]) cursor.execute("SELECT id, confirmed, tail FROM reviewmergeconfirmations WHERE review=%s AND uid=%s AND merge=%s", (review.id, user.id, merge.getId(db))) row = cursor.fetchone() if not row or not row[1]: if not row: cursor.execute("INSERT INTO reviewmergeconfirmations (review, uid, merge) VALUES (%s, %s, %s) RETURNING id", (review.id, user.id, merge.getId(db))) confirmation_id = cursor.fetchone()[0] merged = set() for tail_sha1 in tails: children = new_commits.getChildren(tail_sha1) while children: child = children.pop() if child not in merged and new_commits.isAncestorOf(child, merge): merged.add(child) children.update(new_commits.getChildren(child) - merged) merged_values = [(confirmation_id, commit.getId(db)) for commit in merged] cursor.executemany("INSERT INTO reviewmergecontributions (id, merged) VALUES (%s, %s)", merged_values) db.commit() else: confirmation_id = row[0] message = "Merge %s adds merged-in commits:" % merge.sha1[:8] for tail_sha1 in tails: for parent_sha1 in merge.parents: if parent_sha1 in new_commits: parent = new_commits.get(parent_sha1) if tail_sha1 in new_commits.getTailsFrom(parent): message += "\n %s..%s" % (tail_sha1[:8], parent_sha1[:8]) message += """ Please confirm that this is intended by loading: %s/confirmmerge?id=%d""" % (dbutils.getURLPrefix(db, user), confirmation_id) raise index.IndexException(message) elif row[2] is not None: if row[2] == merge.getId(db): cursor.execute("SELECT merged FROM reviewmergecontributions WHERE id=%s", (row[0],)) for (merged_id,) in cursor: merged = gitutils.Commit.fromId(db, review.repository, merged_id) if merged.sha1 in merge.parents: new_commits = new_commits.without([merged]) break else: tail = gitutils.Commit.fromId(db, review.repository, row[2]) cut = [gitutils.Commit.fromSHA1(db, review.repository, sha1) for sha1 in tail.parents if sha1 in new_commits] new_commits = new_commits.without(cut) if commitset: commitset &= set(new_commits) commits = [commit for commit in commits if commit in commitset] changesets = [] silent_commits = set() silent_changesets = set() simple_commits = [] for commit in commits: if commit not in full_merges and commit not in replayed_rebases: simple_commits.append(commit) if simple_commits: changeset_utils.createChangesets(db, review.repository, simple_commits) for commit in commits: if commit in full_merges: commit_changesets = changeset_utils.createFullMergeChangeset( db, user, review.repository, commit, do_highlight=False) elif commit in replayed_rebases: commit_changesets = changeset_utils.createChangeset( db, user, review.repository, from_commit=commit, to_commit=replayed_rebases[commit], conflicts=True, do_highlight=False) else: commit_changesets = changeset_utils.createChangeset( db, user, review.repository, commit, do_highlight=False) if commit in silent_if_empty: for commit_changeset in commit_changesets: if commit_changeset.files: break else: silent_commits.add(commit) silent_changesets.update(commit_changesets) changesets.extend(commit_changesets) if not new_review: print "Adding %d commit%s to the review at:\n %s" % (len(commits), len(commits) > 1 and "s" or "", review.getURL(db)) reviewchangesets_values = [(review.id, changeset.id) for changeset in changesets] cursor.executemany("""INSERT INTO reviewchangesets (review, changeset) VALUES (%s, %s)""", reviewchangesets_values) cursor.executemany("""INSERT INTO reviewfiles (review, changeset, file, deleted, inserted) SELECT reviewchangesets.review, reviewchangesets.changeset, fileversions.file, COALESCE(SUM(chunks.deleteCount), 0), COALESCE(SUM(chunks.insertCount), 0) FROM reviewchangesets JOIN fileversions USING (changeset) LEFT OUTER JOIN chunks USING (changeset, file) WHERE reviewchangesets.review=%s AND reviewchangesets.changeset=%s GROUP BY reviewchangesets.review, reviewchangesets.changeset, fileversions.file""", reviewchangesets_values) new_reviewers, new_watchers = assignChanges(db, user, review, changesets=changesets) cursor.execute("SELECT include FROM reviewrecipientfilters WHERE review=%s AND uid IS NULL", (review.id,)) try: opt_out = cursor.fetchone()[0] is True except: opt_out = True if not new_review: for user_id in new_reviewers: new_reviewuser = dbutils.User.fromId(db, user_id) print "Added reviewer: %s <%s>" % (new_reviewuser.fullname, new_reviewuser.email) if opt_out: # If the user has opted out from receiving e-mails about this # review while only watching it, clear the opt-out now that the # user becomes a reviewer. cursor.execute("DELETE FROM reviewrecipientfilters WHERE review=%s AND uid=%s AND include=FALSE", (review.id, user_id)) for user_id in new_watchers: new_reviewuser = dbutils.User.fromId(db, user_id) print "Added watcher: %s <%s>" % (new_reviewuser.fullname, new_reviewuser.email) review.incrementSerial(db) reviewing.comment.propagateCommentChains(db, user, review, new_commits, replayed_rebases) if pending_mails is None: pending_mails = [] notify_commits = filter(lambda commit: commit not in silent_commits, commits) notify_changesets = filter(lambda changeset: changeset not in silent_changesets, changesets) if not new_review and notify_changesets: recipients = review.getRecipients(db) for to_user in recipients: pending_mails.extend(mail.sendReviewAddedCommits( db, user, to_user, recipients, review, notify_commits, notify_changesets, tracked_branch=tracked_branch)) mail.sendPendingMails(pending_mails) review.reviewers.extend([User.fromId(db, user_id) for user_id in new_reviewers]) for user_id in new_watchers: review.watchers[User.fromId(db, user_id)] = "automatic" return True
def renderItem(target): span = target.span("name") span.b().text(extension.getName()) span.text(" by %s" % author.fullname) span = target.span("details") span.b().text("Details: ") select = span.select("details", critic_author=extension.getAuthorName(), critic_extension=extension.getName()) select.option(value='', selected="selected" if selected_version is False else None).text("Select version") versions = extension.getVersions() if versions: optgroup = select.optgroup(label="Official Versions") for version in extension.getVersions(): optgroup.option(value="version/%s" % version, selected="selected" if selected_version == version else None).text("%s" % version.upper()) optgroup = select.optgroup(label="Development") optgroup.option(value='live', selected="selected" if selected_version is None else None).text("LIVE") if manifest: is_installed = manifest.status in ( "partial", "installed") or installed_version is not False if is_installed: target.span("installed").text(" [installed]") target.div("description").preformatted().text( manifest.description) else: is_installed = False target.div("description broken").preformatted().a( href="loadmanifest?author=%s&name=%s" % (extension.getAuthorName(), extension.getName())).text( "[This extension has an invalid MANIFEST file]") if selected_version is False: return pages = [] injects = [] processcommits = [] processchanges = [] if manifest: for role in manifest.roles: if isinstance(role, extensions.PageRole): pages.append(role) elif isinstance(role, extensions.InjectRole): injects.append(role) elif isinstance(role, extensions.ProcessCommitsRole): processcommits.append(role) elif isinstance(role, extensions.ProcessChangesRole): processchanges.append(role) role_table = target.table("roles") if pages: role_table.tr().th(colspan=2).text("Pages") for role in pages: row = role_table.tr() row.td("pattern").text( "%s/%s" % (dbutils.getURLPrefix(db), role.pattern)) td = row.td("description") td.text(role.description) if is_installed and not role.installed: td.text(" ") td.span("inactive").text("[Not active!]") if injects: role_table.tr().th(colspan=2).text("Page Injections") for role in injects: row = role_table.tr() row.td("pattern").text( "%s/%s" % (dbutils.getURLPrefix(db), role.pattern)) td = row.td("description") td.text(role.description) if is_installed and not role.installed: td.text(" ") td.span("inactive").text("[Not active!]") if processcommits: role_table.tr().th(colspan=2).text("ProcessCommits hooks") ul = role_table.tr().td(colspan=2).ul() for role in processcommits: li = ul.li() li.text(role.description) if is_installed and not role.installed: li.text(" ") li.span("inactive").text("[Not active!]") if processchanges: role_table.tr().th(colspan=2).text("ProcessChanges hooks") ul = role_table.tr().td(colspan=2).ul() for role in processchanges: li = ul.li() li.text(role.description) if is_installed and not role.installed: li.text(" ") li.span("inactive").text("[Not active!]")