def renderHome(req, db, user): if user.isAnonymous(): raise page.utils.NeedLogin(req) profiler = profiling.Profiler() cursor = db.cursor() readonly = req.getParameter("readonly", "yes" if user.name != req.user else "no") == "yes" repository = req.getParameter("repository", None, gitutils.Repository.FromParameter(db)) verified_email_id = req.getParameter("email_verified", None, int) if not repository: repository = user.getDefaultRepository(db) title_fullname = user.fullname if title_fullname[-1] == 's': title_fullname += "'" else: title_fullname += "'s" cursor.execute("SELECT email FROM usergitemails WHERE uid=%s ORDER BY email ASC", (user.id,)) gitemails = ", ".join([email for (email,) in cursor]) document = htmlutils.Document(req) html = document.html() head = html.head() body = html.body() if user.name == req.user: actual_user = None else: actual_user = req.getUser(db) def renderHeaderItems(target): if readonly and actual_user and actual_user.hasRole(db, "administrator"): target.a("button", href="/home?user=%s&readonly=no" % user.name).text("Edit") page.utils.generateHeader(body, db, user, generate_right=renderHeaderItems, current_page="home") document.addExternalStylesheet("resource/home.css") document.addExternalScript("resource/home.js") document.addExternalScript("resource/autocomplete.js") if repository: document.addInternalScript(repository.getJS()) else: document.addInternalScript("var repository = null;") if actual_user and actual_user.hasRole(db, "administrator"): document.addInternalScript("var administrator = true;") else: document.addInternalScript("var administrator = false;") document.addInternalScript(user.getJS()) document.addInternalScript("user.gitEmails = %s;" % jsify(gitemails)) document.addInternalScript("var verifyEmailAddresses = %s;" % jsify(configuration.base.VERIFY_EMAIL_ADDRESSES)) document.setTitle("%s Home" % title_fullname) target = body.div("main") basic = target.table('paleyellow basic', align='center') basic.tr().td('h1', colspan=3).h1().text("%s Home" % title_fullname) def row(heading, value, help=None, extra_class=None): if extra_class: row_class = "line " + extra_class else: row_class = "line" main_row = basic.tr(row_class) main_row.td('heading').text("%s:" % heading) value_cell = main_row.td('value', colspan=2) if callable(value): value(value_cell) else: value_cell.text(value) basic.tr('help').td('help', colspan=3).text(help) def renderFullname(target): if readonly: target.text(user.fullname) else: target.input("value", id="user_fullname", value=user.fullname) target.span("status", id="status_fullname") buttons = target.span("buttons") buttons.button(onclick="saveFullname();").text("Save") buttons.button(onclick="resetFullname();").text("Reset") def renderEmail(target): if not actual_user or actual_user.hasRole(db, "administrator"): cursor.execute("""SELECT id, email, verified FROM useremails WHERE uid=%s ORDER BY id ASC""", (user.id,)) rows = cursor.fetchall() if rows: if len(rows) > 1: target.addClass("multiple") addresses = target.div("addresses") for email_id, email, verified in rows: checked = "checked" if email == user.email else None selected = " selected" if email == user.email else "" label = addresses.label("address inset flex" + selected, data_email_id=email_id) if len(rows) > 1: label.input(name="email", type="radio", value=email, checked=checked) label.span("value").text(email) actions = label.span("actions") if verified is False: actions.a("action unverified", href="#").text("[unverified]") elif verified is True: now = " now" if email_id == verified_email_id else "" actions.span("action verified" + now).text("[verified]") actions.a("action delete", href="#").text("[delete]") else: target.i().text("No email address") target.span("buttons").button("addemail").text( "Add email address") elif user.email is None: target.i().text("No email address") elif user.email_verified is False: # Pending verification: don't show to other users. target.i().text("Email address not verified") else: target.span("inset").text(user.email) def renderGitEmails(target): if readonly: target.text(gitemails) else: target.input("value", id="user_gitemails", value=gitemails) target.span("status", id="status_gitemails") buttons = target.span("buttons") buttons.button(onclick="saveGitEmails();").text("Save") buttons.button(onclick="resetGitEmails();").text("Reset") def renderPassword(target): cursor.execute("SELECT password IS NOT NULL FROM users WHERE id=%s", (user.id,)) has_password = cursor.fetchone()[0] if not has_password: target.text("not set") else: target.text("****") if not readonly: if not has_password or (actual_user and actual_user.hasRole(db, "administrator")): target.span("buttons").button(onclick="setPassword();").text("Set password") else: target.span("buttons").button(onclick="changePassword();").text("Change password") row("User ID", str(user.id)) row("User Name", user.name) row("Display Name", renderFullname, "This is the name used when displaying commits or comments.") row("Primary Email", renderEmail, "This is the primary email address, to which emails are sent.", extra_class="email") row("Git Emails", renderGitEmails, "These email addresses (comma-separated) are used to map Git commits to the user.") if configuration.base.AUTHENTICATION_MODE == "critic": row("Password", renderPassword, extra_class="password") cursor.execute("""SELECT provider, account FROM externalusers WHERE uid=%s""", (user.id,)) external_accounts = [(auth.PROVIDERS[provider_name], account) for provider_name, account in cursor if provider_name in auth.PROVIDERS] if external_accounts: basic.tr().td('h2', colspan=3).h2().text("External Accounts") for provider, account in external_accounts: def renderExternalAccount(target): url = provider.getAccountURL(account) target.a("external", href=url).text(account) row(provider.getTitle(), renderExternalAccount) profiler.check("user information") filters = page.utils.PaleYellowTable(body, "Filters") filters.titleRight.a("button", href="/tutorial?item=filters").text("Tutorial") cursor.execute("""SELECT repositories.id, repositories.name, repositories.path, filters.id, filters.type, filters.path, NULL, filters.delegate FROM repositories JOIN filters ON (filters.repository=repositories.id) WHERE filters.uid=%s""", (user.id,)) rows = cursor.fetchall() if configuration.extensions.ENABLED: cursor.execute("""SELECT repositories.id, repositories.name, repositories.path, filters.id, 'extensionhook', filters.path, filters.name, filters.data FROM repositories JOIN extensionhookfilters AS filters ON (filters.repository=repositories.id) WHERE filters.uid=%s""", (user.id,)) rows.extend(cursor.fetchall()) FILTER_TYPES = ["reviewer", "watcher", "ignored", "extensionhook"] def rowSortKey(row): (repository_id, repository_name, repository_path, filter_id, filter_type, filter_path, filter_name, filter_data) = row # Rows are grouped by repository first and type second, so sort by # repository name and filter type primarily. # # Secondarily sort by filter name (only for extension hook filters; is # None for regular filters) and filter path. This sorting is mostly to # achieve a stable order; it has no greater meaning. return (repository_name, FILTER_TYPES.index(filter_type), filter_name, filter_path) rows.sort(key=rowSortKey) if rows: repository = None repository_filters = None tbody_reviewer = None tbody_watcher = None tbody_ignored = None tbody_extensionhook = None count_matched_files = {} for (repository_id, repository_name, repository_path, filter_id, filter_type, filter_path, filter_name, filter_data) in rows: if not repository or repository.id != repository_id: repository = gitutils.Repository.fromId(db, repository_id) repository_url = repository.getURL(db, user) filters.addSection(repository_name, repository_url) repository_filters = filters.addCentered().table("filters callout") tbody_reviewer = tbody_watcher = tbody_ignored = tbody_extensionhook = None if filter_type == "reviewer": if not tbody_reviewer: tbody_reviewer = repository_filters.tbody() tbody_reviewer.tr().th(colspan=5).text("Reviewer") tbody = tbody_reviewer elif filter_type == "watcher": if not tbody_watcher: tbody_watcher = repository_filters.tbody() tbody_watcher.tr().th(colspan=5).text("Watcher") tbody = tbody_watcher elif filter_type == "ignored": if not tbody_ignored: tbody_ignored = repository_filters.tbody() tbody_ignored.tr().th(colspan=5).text("Ignored") tbody = tbody_ignored else: if not tbody_extensionhook: tbody_extensionhook = repository_filters.tbody() tbody_extensionhook.tr().th(colspan=5).text("Extension hooks") tbody = tbody_extensionhook row = tbody.tr() row.td("path").text(filter_path) if filter_type != "extensionhook": delegates = row.td("delegates", colspan=2) if filter_data: delegates.i().text("Delegates: ") delegates.span("names").text(", ".join(filter_data.split(","))) else: role = extensions.role.filterhook.getFilterHookRole(db, filter_id) if role: title = row.td("title") title.text(role.title) data = row.td("data") data.text(filter_data) else: row.td(colspan=2).i().text("Invalid filter") if filter_path == "/": row.td("files").text("all files") else: href = "javascript:void(showMatchedFiles(%s, %s));" % (jsify(repository.name), jsify(filter_path)) row.td("files").a(href=href, id=("f%d" % filter_id)).text("? files") count_matched_files.setdefault(repository_id, []).append(filter_id) links = row.td("links") arguments = (jsify(repository.name), filter_id, jsify(filter_type), jsify(filter_path), jsify(filter_data)) links.a(href="javascript:void(editFilter(%s, %d, %s, %s, %s));" % arguments).text("[edit]") if filter_type != "extensionhook": links.a(href="javascript:if (deleteFilterById(%d)) location.reload(); void(0);" % filter_id).text("[delete]") links.a(href="javascript:location.href='/config?filter=%d';" % filter_id).text("[preferences]") else: links.a(href="javascript:if (deleteExtensionHookFilterById(%d)) location.reload(); void(0);" % filter_id).text("[delete]") document.addInternalScript("var count_matched_files = %s;" % json_encode(count_matched_files.values())) else: filters.addCentered().p().b().text("No filters") # Additionally check if there are in fact no repositories. cursor.execute("SELECT 1 FROM repositories") if not cursor.fetchone(): document.addInternalScript("var no_repositories = true;") if not readonly: filters.addSeparator() filters.addCentered().button(onclick="editFilter();").text("Add filter") profiler.check("filters") hidden = body.div("hidden", style="display: none") if configuration.extensions.ENABLED: filterhooks = extensions.role.filterhook.listFilterHooks(db, user) else: filterhooks = [] with hidden.div("filterdialog") as dialog: paragraph = dialog.p() paragraph.b().text("Repository:") paragraph.br() page.utils.generateRepositorySelect(db, user, paragraph, name="repository") paragraph = dialog.p() paragraph.b().text("Filter type:") paragraph.br() filter_type = paragraph.select(name="type") filter_type.option(value="reviewer").text("Reviewer") filter_type.option(value="watcher").text("Watcher") filter_type.option(value="ignored").text("Ignored") for extension, manifest, roles in filterhooks: optgroup = filter_type.optgroup(label=extension.getTitle(db)) for role in roles: option = optgroup.option( value="extensionhook", data_extension_id=extension.getExtensionID(db), data_filterhook_name=role.name) option.text(role.title) paragraph = dialog.p() paragraph.b().text("Path:") paragraph.br() paragraph.input(name="path", type="text") paragraph.span("matchedfiles") regular_div = dialog.div("regular") paragraph = regular_div.p() paragraph.b().text("Delegates:") paragraph.br() paragraph.input(name="delegates", type="text") paragraph = regular_div.p() label = paragraph.label() label.input(name="apply", type="checkbox", checked="checked") label.b().text("Apply to existing reviews") for extension, manifest, roles in filterhooks: for role in roles: if not role.data_description: continue filterhook_id = "%d_%s" % (extension.getExtensionID(db), role.name) extensionhook_div = dialog.div( "extensionhook " + filterhook_id, style="display: none") extensionhook_div.innerHTML(role.data_description) paragraph = extensionhook_div.p() paragraph.b().text("Data:") paragraph.br() paragraph.input(type="text") profiler.output(db, user, document) return document
def renderHome(req, db, user): if user.isAnonymous(): raise page.utils.NeedLogin(req) profiler = profiling.Profiler() cursor = db.cursor() readonly = req.getParameter( "readonly", "yes" if user.name != req.user else "no") == "yes" repository = req.getParameter("repository", None, gitutils.Repository.FromParameter(db)) verified_email_id = req.getParameter("email_verified", None, int) if not repository: repository = user.getDefaultRepository(db) title_fullname = user.fullname if title_fullname[-1] == 's': title_fullname += "'" else: title_fullname += "'s" cursor.execute( "SELECT email FROM usergitemails WHERE uid=%s ORDER BY email ASC", (user.id, )) gitemails = ", ".join([email for (email, ) in cursor]) document = htmlutils.Document(req) html = document.html() head = html.head() body = html.body() if user.name == req.user: actual_user = None else: actual_user = req.getUser(db) def renderHeaderItems(target): if readonly and actual_user and actual_user.hasRole( db, "administrator"): target.a("button", href="/home?user=%s&readonly=no" % user.name).text("Edit") page.utils.generateHeader(body, db, user, generate_right=renderHeaderItems, current_page="home") document.addExternalStylesheet("resource/home.css") document.addExternalScript("resource/home.js") document.addExternalScript("resource/autocomplete.js") if repository: document.addInternalScript(repository.getJS()) else: document.addInternalScript("var repository = null;") if actual_user and actual_user.hasRole(db, "administrator"): document.addInternalScript("var administrator = true;") else: document.addInternalScript("var administrator = false;") document.addInternalScript(user.getJS()) document.addInternalScript("user.gitEmails = %s;" % jsify(gitemails)) document.addInternalScript( "var verifyEmailAddresses = %s;" % jsify(configuration.base.VERIFY_EMAIL_ADDRESSES)) document.setTitle("%s Home" % title_fullname) target = body.div("main") basic = target.table('paleyellow basic', align='center') basic.tr().td('h1', colspan=3).h1().text("%s Home" % title_fullname) def row(heading, value, help=None, extra_class=None): if extra_class: row_class = "line " + extra_class else: row_class = "line" main_row = basic.tr(row_class) main_row.td('heading').text("%s:" % heading) value_cell = main_row.td('value', colspan=2) if callable(value): value(value_cell) else: value_cell.text(value) basic.tr('help').td('help', colspan=3).text(help) def renderFullname(target): if readonly: target.text(user.fullname) else: target.input("value", id="user_fullname", value=user.fullname) target.span("status", id="status_fullname") buttons = target.span("buttons") buttons.button(onclick="saveFullname();").text("Save") buttons.button(onclick="resetFullname();").text("Reset") def renderEmail(target): if not actual_user or actual_user.hasRole(db, "administrator"): cursor.execute( """SELECT id, email, verified FROM useremails WHERE uid=%s ORDER BY id ASC""", (user.id, )) rows = cursor.fetchall() if rows: if len(rows) > 1: target.addClass("multiple") addresses = target.div("addresses") for email_id, email, verified in rows: checked = "checked" if email == user.email else None selected = " selected" if email == user.email else "" label = addresses.label("address inset flex" + selected, data_email_id=email_id) if len(rows) > 1: label.input(name="email", type="radio", value=email, checked=checked) label.span("value").text(email) actions = label.span("actions") if verified is False: actions.a("action unverified", href="#").text("[unverified]") elif verified is True: now = " now" if email_id == verified_email_id else "" actions.span("action verified" + now).text("[verified]") actions.a("action delete", href="#").text("[delete]") else: target.i().text("No email address") target.span("buttons").button("addemail").text("Add email address") elif user.email is None: target.i().text("No email address") elif user.email_verified is False: # Pending verification: don't show to other users. target.i().text("Email address not verified") else: target.span("inset").text(user.email) def renderGitEmails(target): if readonly: target.text(gitemails) else: target.input("value", id="user_gitemails", value=gitemails) target.span("status", id="status_gitemails") buttons = target.span("buttons") buttons.button(onclick="saveGitEmails();").text("Save") buttons.button(onclick="resetGitEmails();").text("Reset") def renderPassword(target): cursor.execute("SELECT password IS NOT NULL FROM users WHERE id=%s", (user.id, )) has_password = cursor.fetchone()[0] if not has_password: target.text("not set") else: target.text("****") if not readonly: if not has_password or (actual_user and actual_user.hasRole( db, "administrator")): target.span("buttons").button( onclick="setPassword();").text("Set password") else: target.span("buttons").button( onclick="changePassword();").text("Change password") row("User ID", str(user.id)) row("User Name", user.name) row("Display Name", renderFullname, "This is the name used when displaying commits or comments.") row("Primary Email", renderEmail, "This is the primary email address, to which emails are sent.", extra_class="email") row("Git Emails", renderGitEmails, "These email addresses are used to map Git commits to the user.") if configuration.base.AUTHENTICATION_MODE == "critic": row("Password", renderPassword, extra_class="password") cursor.execute( """SELECT provider, account FROM externalusers WHERE uid=%s""", (user.id, )) external_accounts = [(auth.PROVIDERS[provider_name], account) for provider_name, account in cursor if provider_name in auth.PROVIDERS] if external_accounts: basic.tr().td('h2', colspan=3).h2().text("External Accounts") for provider, account in external_accounts: def renderExternalAccount(target): url = provider.getAccountURL(account) target.a("external", href=url).text(account) row(provider.getTitle(), renderExternalAccount) profiler.check("user information") filters = page.utils.PaleYellowTable(body, "Filters") filters.titleRight.a("button", href="/tutorial?item=filters").text("Tutorial") cursor.execute( """SELECT repositories.id, repositories.name, repositories.path, filters.id, filters.type, filters.path, NULL, filters.delegate FROM repositories JOIN filters ON (filters.repository=repositories.id) WHERE filters.uid=%s""", (user.id, )) rows = cursor.fetchall() if configuration.extensions.ENABLED: cursor.execute( """SELECT repositories.id, repositories.name, repositories.path, filters.id, 'extensionhook', filters.path, filters.name, filters.data FROM repositories JOIN extensionhookfilters AS filters ON (filters.repository=repositories.id) WHERE filters.uid=%s""", (user.id, )) rows.extend(cursor.fetchall()) FILTER_TYPES = ["reviewer", "watcher", "ignored", "extensionhook"] def rowSortKey(row): (repository_id, repository_name, repository_path, filter_id, filter_type, filter_path, filter_name, filter_data) = row # Rows are grouped by repository first and type second, so sort by # repository name and filter type primarily. # # Secondarily sort by filter name (only for extension hook filters; is # None for regular filters) and filter path. This sorting is mostly to # achieve a stable order; it has no greater meaning. return (repository_name, FILTER_TYPES.index(filter_type), filter_name, filter_path) rows.sort(key=rowSortKey) if rows: repository = None repository_filters = None tbody_reviewer = None tbody_watcher = None tbody_ignored = None tbody_extensionhook = None count_matched_files = {} for (repository_id, repository_name, repository_path, filter_id, filter_type, filter_path, filter_name, filter_data) in rows: if not repository or repository.id != repository_id: repository = gitutils.Repository.fromId(db, repository_id) repository_url = repository.getURL(db, user) filters.addSection(repository_name, repository_url) repository_filters = filters.addCentered().table( "filters callout") tbody_reviewer = tbody_watcher = tbody_ignored = tbody_extensionhook = None if filter_type == "reviewer": if not tbody_reviewer: tbody_reviewer = repository_filters.tbody() tbody_reviewer.tr().th(colspan=5).text("Reviewer") tbody = tbody_reviewer elif filter_type == "watcher": if not tbody_watcher: tbody_watcher = repository_filters.tbody() tbody_watcher.tr().th(colspan=5).text("Watcher") tbody = tbody_watcher elif filter_type == "ignored": if not tbody_ignored: tbody_ignored = repository_filters.tbody() tbody_ignored.tr().th(colspan=5).text("Ignored") tbody = tbody_ignored else: if not tbody_extensionhook: tbody_extensionhook = repository_filters.tbody() tbody_extensionhook.tr().th( colspan=5).text("Extension hooks") tbody = tbody_extensionhook row = tbody.tr() row.td("path").text(filter_path) if filter_type != "extensionhook": delegates = row.td("delegates", colspan=2) if filter_data: delegates.i().text("Delegates: ") delegates.span("names").text(", ".join( filter_data.split(","))) else: role = extensions.role.filterhook.getFilterHookRole( db, filter_id) if role: title = row.td("title") title.text(role.title) data = row.td("data") data.text(filter_data) else: row.td(colspan=2).i().text("Invalid filter") if filter_path == "/": row.td("files").text("all files") else: href = "javascript:void(showMatchedFiles(%s, %s));" % (jsify( repository.name), jsify(filter_path)) row.td("files").a(href=href, id=("f%d" % filter_id)).text("? files") count_matched_files.setdefault(repository_id, []).append(filter_id) links = row.td("links") arguments = (jsify(repository.name), filter_id, jsify(filter_type), jsify(filter_path), jsify(filter_data)) links.a(href="javascript:void(editFilter(%s, %d, %s, %s, %s));" % arguments).text("[edit]") if filter_type != "extensionhook": links.a( href= "javascript:if (deleteFilterById(%d)) location.reload(); void(0);" % filter_id).text("[delete]") links.a(href="javascript:location.href='/config?filter=%d';" % filter_id).text("[preferences]") else: links.a( href= "javascript:if (deleteExtensionHookFilterById(%d)) location.reload(); void(0);" % filter_id).text("[delete]") document.addInternalScript("var count_matched_files = %s;" % json_encode(count_matched_files.values())) else: filters.addCentered().p().b().text("No filters") # Additionally check if there are in fact no repositories. cursor.execute("SELECT 1 FROM repositories") if not cursor.fetchone(): document.addInternalScript("var no_repositories = true;") if not readonly: filters.addSeparator() filters.addCentered().button( onclick="editFilter();").text("Add filter") profiler.check("filters") hidden = body.div("hidden", style="display: none") if configuration.extensions.ENABLED: filterhooks = extensions.role.filterhook.listFilterHooks(db, user) else: filterhooks = [] with hidden.div("filterdialog") as dialog: paragraph = dialog.p() paragraph.b().text("Repository:") paragraph.br() page.utils.generateRepositorySelect(db, user, paragraph, name="repository") paragraph = dialog.p() paragraph.b().text("Filter type:") paragraph.br() filter_type = paragraph.select(name="type") filter_type.option(value="reviewer").text("Reviewer") filter_type.option(value="watcher").text("Watcher") filter_type.option(value="ignored").text("Ignored") for extension, manifest, roles in filterhooks: optgroup = filter_type.optgroup(label=extension.getTitle(db)) for role in roles: option = optgroup.option( value="extensionhook", data_extension_id=extension.getExtensionID(db), data_filterhook_name=role.name) option.text(role.title) paragraph = dialog.p() paragraph.b().text("Path:") paragraph.br() paragraph.input(name="path", type="text") paragraph.span("matchedfiles") regular_div = dialog.div("regular") paragraph = regular_div.p() paragraph.b().text("Delegates:") paragraph.br() paragraph.input(name="delegates", type="text") paragraph = regular_div.p() label = paragraph.label() label.input(name="apply", type="checkbox", checked="checked") label.b().text("Apply to existing reviews") for extension, manifest, roles in filterhooks: for role in roles: if not role.data_description: continue filterhook_id = "%d_%s" % (extension.getExtensionID(db), role.name) extensionhook_div = dialog.div("extensionhook " + filterhook_id, style="display: none") extensionhook_div.innerHTML(role.data_description) paragraph = extensionhook_div.p() paragraph.b().text("Data:") paragraph.br() paragraph.input(type="text") profiler.output(db, user, document) return document
def renderHome(req, db, user): if user.isAnonymous(): raise page.utils.NeedLogin(req) profiler = profiling.Profiler() cursor = db.cursor() readonly = req.getParameter("readonly", "yes" if user.name != req.user else "no") == "yes" repository = req.getParameter("repository", None, gitutils.Repository.FromParameter(db)) if not repository: repository = user.getDefaultRepository(db) title_fullname = user.fullname if title_fullname[-1] == 's': title_fullname += "'" else: title_fullname += "'s" cursor.execute("SELECT email FROM usergitemails WHERE uid=%s ORDER BY email ASC", (user.id,)) gitemails = ", ".join([email for (email,) in cursor]) document = htmlutils.Document(req) html = document.html() head = html.head() body = html.body() page.utils.generateHeader(body, db, user, current_page="home") document.addExternalStylesheet("resource/home.css") document.addExternalScript("resource/home.js") document.addExternalScript("resource/autocomplete.js") if repository: document.addInternalScript(repository.getJS()) else: document.addInternalScript("var repository = null;") if user.name != req.user and req.getUser(db).hasRole(db, "administrator"): document.addInternalScript("var administrator = true;") else: document.addInternalScript("var administrator = false;") document.addInternalScript(user.getJS()) document.addInternalScript("user.gitEmails = %s;" % jsify(gitemails)) document.setTitle("%s Home" % title_fullname) target = body.div("main") basic = target.table('paleyellow basic', align='center') basic.tr().td('h1', colspan=3).h1().text("%s Home" % title_fullname) def row(heading, value, help=None, status_id=None): main_row = basic.tr('line') main_row.td('heading').text("%s:" % heading) value_cell = main_row.td('value', colspan=2) if callable(value): value(value_cell) else: value_cell.text(value) basic.tr('help').td('help', colspan=3).text(help) def renderFullname(target): if readonly: target.text(user.fullname) else: target.input("value", id="user_fullname", value=user.fullname) target.span("status", id="status_fullname") target.button(onclick="saveFullname();").text("Save") target.button(onclick="resetFullname();").text("Reset") def renderEmail(target): if readonly: target.text(user.email) else: target.input("value", id="user_email", value=user.email) target.span("status", id="status_email") target.button(onclick="saveEmail();").text("Save") target.button(onclick="resetEmail();").text("Reset") def renderGitEmails(target): if readonly: target.text(gitemails) else: target.input("value", id="user_gitemails", value=gitemails) target.span("status", id="status_gitemails") target.button(onclick="saveGitEmails();").text("Save") target.button(onclick="resetGitEmails();").text("Reset") def renderPassword(target): target.text("****") if not readonly: target.button(onclick="changePassword();").text("Change") row("User ID", str(user.id)) row("User Name", user.name) row("Display Name", renderFullname, "This is the name used when displaying commits or comments.", status_id="status_fullname") row("Email", renderEmail, "This is the primary email address, to which emails are sent.", status_id="status_email") row("Git Emails", renderGitEmails, "These email addresses are used to map Git commits to the user.", status_id="status_gitemails") if configuration.base.AUTHENTICATION_MODE == "critic": row("Password", renderPassword) profiler.check("user information") filters = page.utils.PaleYellowTable(body, "Filters") filters.titleRight.a("button", href="/tutorial?item=filters").text("Tutorial") cursor.execute("""SELECT repositories.id, repositories.name, repositories.path, filters.id, filters.type, filters.path, filters.delegate FROM repositories JOIN filters ON (filters.repository=repositories.id) WHERE filters.uid=%s ORDER BY repositories.name, filters.type, filters.path""", (user.id,)) rows = cursor.fetchall() if rows: repository = None repository_filters = None tbody_reviewer = None tbody_watcher = None tbody_ignored = None count_matched_files = {} for (repository_id, repository_name, repository_path, filter_id, filter_type, filter_path, filter_delegates) in rows: if not repository or repository.id != repository_id: repository = gitutils.Repository.fromId(db, repository_id) repository_url = repository.getURL(db, user) filters.addSection(repository_name, repository_url) repository_filters = filters.addCentered().table("filters callout") tbody_reviewer = tbody_watcher = tbody_ignored = None if filter_type == "reviewer": if not tbody_reviewer: tbody_reviewer = repository_filters.tbody() tbody_reviewer.tr().th(colspan=4).text("Reviewer") tbody = tbody_reviewer elif filter_type == "watcher": if not tbody_watcher: tbody_watcher = repository_filters.tbody() tbody_watcher.tr().th(colspan=4).text("Watcher") tbody = tbody_watcher else: if not tbody_ignored: tbody_ignored = repository_filters.tbody() tbody_ignored.tr().th(colspan=4).text("Ignored") tbody = tbody_ignored row = tbody.tr() row.td("path").text(filter_path) delegates = row.td("delegates") if filter_delegates: delegates.i().text("Delegates: ") delegates.span("names").text(", ".join(filter_delegates.split(","))) if filter_path == "/": row.td("files").text("all files") else: href = "javascript:void(showMatchedFiles(%s, %s));" % (jsify(repository.name), jsify(filter_path)) row.td("files").a(href=href, id=("f%d" % filter_id)).text("? files") count_matched_files.setdefault(repository_id, []).append(filter_id) links = row.td("links") arguments = (jsify(repository.name), filter_id, jsify(filter_type), jsify(filter_path), jsify(filter_delegates)) links.a(href="javascript:void(editFilter(%s, %d, %s, %s, %s));" % arguments).text("[edit]") links.a(href="javascript:if (deleteFilterById(%d)) location.reload(); void(0);" % filter_id).text("[delete]") links.a(href="javascript:location.href='/config?filter=%d';" % filter_id).text("[preferences]") document.addInternalScript("var count_matched_files = %s;" % json_encode(count_matched_files.values())) else: filters.addCentered().p().b().text("No filters") # Additionally check if there are in fact no repositories. cursor.execute("SELECT 1 FROM repositories") if not cursor.fetchone(): document.addInternalScript("var no_repositories = true;") if not readonly: filters.addSeparator() filters.addCentered().button(onclick="editFilter();").text("Add filter") profiler.check("filters") hidden = body.div("hidden", style="display: none") with hidden.div("filterdialog") as dialog: paragraph = dialog.p() paragraph.b().text("Repository:") paragraph.br() page.utils.generateRepositorySelect(db, user, paragraph, name="repository") paragraph = dialog.p() paragraph.b().text("Filter type:") paragraph.br() filter_type = paragraph.select(name="type") filter_type.option(value="reviewer").text("Reviewer") filter_type.option(value="watcher").text("Watcher") filter_type.option(value="ignored").text("Ignored") paragraph = dialog.p() paragraph.b().text("Path:") paragraph.br() paragraph.input(name="path", type="text") paragraph.span("matchedfiles") paragraph = dialog.p() paragraph.b().text("Delegates:") paragraph.br() paragraph.input(name="delegates", type="text") paragraph = dialog.p() label = paragraph.label() label.input(name="apply", type="checkbox", checked="checked") label.b().text("Apply to existing reviews") profiler.output(db, user, document) return document