Beispiel #1
0
def sendAdministratorErrorReport(db, source, summary, message):
    if db:
        installed_sha1 = dbutils.getInstalledSHA1(db)
    else:
        installed_sha1 = "<unknown>"
    sendAdministratorMessage(source, summary, "Critic encountered an unexpected error.  If you know a series " +
                             "of steps that can reproduce this error it would be very useful if you submitted " +
                             "a bug report including the steps plus the information below (see bug reporting URL " +
                             "at the bottom of this e-mail).\n\n" +
                             message +
                             "\n\nCritic version: " + installed_sha1 +
                             "\nCritic bug reports can be filed here: https://github.com/jensl/critic/issues/new")
Beispiel #2
0
def sendAdministratorErrorReport(db, source, summary, message):
    if db:
        installed_sha1 = dbutils.getInstalledSHA1(db)
    else:
        installed_sha1 = "<unknown>"
    sendAdministratorMessage(
        source, summary, """\

Critic encountered an unexpected error.  If you know a series of steps that can
reproduce this error it would be very useful if you submitted a bug report
including the steps plus the information below (see bug reporting URL at the
bottom of this e-mail).

%(message)s

Critic version: %(installed_sha1)s
Critic bug reports can be filed here: https://github.com/jensl/critic/issues/new
""" % {
            "message": message,
            "installed_sha1": installed_sha1
        })
Beispiel #3
0
def renderConfig(req, db, user):
    highlight = req.getParameter("highlight", None)
    repository = req.getParameter("repository", None, gitutils.Repository.FromParameter(db))
    filter_id = req.getParameter("filter", None, int)
    defaults = req.getParameter("defaults", "no") == "yes"

    if filter_id is not None:
        # There can't be system-wide defaults for one of a single user's
        # filters.
        defaults = False

    cursor = db.cursor()

    if filter_id is not None:
        cursor.execute("""SELECT filters.path, repositories.name
                            FROM filters
                            JOIN repositories ON (repositories.id=filters.repository)
                           WHERE filters.id=%s""",
                       (filter_id,))
        row = cursor.fetchone()
        if not row:
            raise page.utils.InvalidParameterValue(
                name="filter",
                value=str(filter_id),
                expected="valid filter id")
        title = "Filter preferences: %s in %s" % row
    elif repository is not None:
        title = "Repository preferences: %s" % repository.name
    else:
        title = "User preferences"

    document = htmlutils.Document(req)
    document.setTitle(title)

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

    if user.isAnonymous():
        disabled = "disabled"
    else:
        disabled = None

    def generate_right(target):
        if defaults:
            url = "/config"
            if repository is not None:
                url += "?repository=%d" % repository.id
            target.a("button", href=url).text("Edit Own")
        elif user.hasRole(db, "administrator"):
            url = "/config?defaults=yes"
            if repository is not None:
                url += "&repository=%d" % repository.id
                what = "Repository Defaults"
            else:
                what = "System Defaults"
            target.a("button", href=url).text("Edit " + what)

    injected = page.utils.generateHeader(body, db, user, current_page="config",
                                         generate_right=generate_right)

    document.addExternalStylesheet("resource/config.css")
    document.addExternalScript("resource/config.js")
    document.addInternalScript(user.getJS())
    document.addInternalScript("var repository_id = %s, filter_id = %s, defaults = %s;"
                               % (htmlutils.jsify(repository.id if repository else None),
                                  htmlutils.jsify(filter_id),
                                  htmlutils.jsify(defaults)))

    target = body.div("main")

    table = target.table('preferences paleyellow', align='center', cellspacing=0)
    h1 = table.tr().td('h1', colspan=3).h1()
    h1.text(title)

    if filter_id is None:
        page.utils.generateRepositorySelect(
            db, user, h1.span("right"), allow_selecting_none=True,
            selected=repository.name if repository else False)

    if filter_id is not None:
        conditional = "per_filter"
    elif repository is not None:
        conditional = "per_repository"
    elif defaults:
        conditional = "per_system"
    else:
        conditional = "per_user"

    cursor = db.cursor()
    cursor.execute("""SELECT item, type, description, per_repository, per_filter
                        FROM preferences
                       WHERE %(conditional)s"""
                   % { "conditional": conditional })

    preferences = dict((item, [preference_type, description, None, None, per_repository, per_filter])
                       for item, preference_type, description, per_repository, per_filter in cursor)

    def set_values(rows, is_overrides):
        index = 3 if is_overrides else 2
        for item, integer, string in rows:
            if preferences[item][0] == "boolean":
                preferences[item][index] = bool(integer)
            elif preferences[item][0] == "integer":
                preferences[item][index] = integer
            else:
                preferences[item][index] = string

    cursor.execute("""SELECT item, integer, string
                        FROM userpreferences
                       WHERE item=ANY (%s)
                         AND uid IS NULL
                         AND repository IS NULL""",
                   (preferences.keys(),))

    set_values(cursor, is_overrides=False)

    if repository is not None:
        cursor.execute("""SELECT item, integer, string
                            FROM userpreferences
                           WHERE item=ANY (%s)
                             AND uid IS NULL
                             AND repository=%s""",
                       (preferences.keys(), repository.id))

        # These are overrides if we're editing the defaults for a specific
        # repository.
        set_values(cursor, is_overrides=defaults)

    if not defaults:
        cursor.execute("""SELECT item, integer, string
                            FROM userpreferences
                           WHERE item=ANY (%s)
                             AND uid=%s
                             AND repository IS NULL
                             AND filter IS NULL""",
                       (preferences.keys(), user.id))

        if filter_id is not None or repository is not None:
            # We're looking at per-filter or per-repository settings, so the
            # user's global settings are defaults, not the overrides.  If a
            # per-filter or per-repository override is deleted, the user's
            # global setting kicks in instead.
            set_values(cursor, is_overrides=False)

            if filter_id is not None:
                cursor.execute("""SELECT item, integer, string
                                    FROM userpreferences
                                   WHERE item=ANY (%s)
                                     AND uid=%s
                                     AND filter=%s""",
                               (preferences.keys(), user.id, filter_id))
            else:
                cursor.execute("""SELECT item, integer, string
                                    FROM userpreferences
                                   WHERE item=ANY (%s)
                                     AND uid=%s
                                     AND repository=%s""",
                               (preferences.keys(), user.id, repository.id))

        # Set the overrides.  This is either the user's global settings, if
        # we're not looking at per-filter or per-repository settings, or the
        # user's per-filter or per-repository settings if we are.
        set_values(cursor, is_overrides=True)
    elif repository is None:
        # When editing global defaults, use the values from preferences.json
        # used when initially installing Critic as the default values.
        defaults_path = os.path.join(configuration.paths.INSTALL_DIR,
                                     "data/preferences.json")
        with open(defaults_path) as defaults_file:
            factory_defaults = textutils.json_decode(defaults_file.read())
        for item, data in preferences.items():
            data[3] = data[2]
            if item in factory_defaults:
                data[2] = factory_defaults[item]["default"]
                if data[2] == data[3]:
                    data[3] = None

    if req.getParameter("recalculate", "no") == "yes":
        for item, data in preferences.items():
            if data[2] == data[3]:
                user.setPreference(db, item, None, repository=repository, filter_id=filter_id)
                data[3] = None
        db.commit()

    debug_enabled = user.getPreference(db, "debug.enabled")

    for item, (preference_type, description, default_value, current_value, per_repository, per_filter) in sorted(preferences.items()):
        if item.startswith("debug.") and item != "debug.enabled" and not debug_enabled:
            continue

        line_class_name = "line"
        help_class_name = "help"

        if highlight is not None and not fnmatch.fnmatch(item, highlight):
            continue

        if current_value is None:
            current_value = default_value
        else:
            line_class_name += " customized"

        row = table.tr(line_class_name)
        heading = row.td("heading")
        heading.text("%s:" % item)
        value = row.td("value", colspan=2)
        value.preformatted()

        options = None
        optgroup = None
        def addOption(value, name, selected=lambda value: value==current_value, **attributes):
            (optgroup or options).option(
                value=value, selected="selected" if selected(value) else None,
                **attributes).text(name)

        if preference_type == "boolean":
            value.input(
                "setting", type="checkbox", name=item,
                checked="checked" if current_value else None, disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))
        elif preference_type == "integer":
            value.input(
                "setting", type="number", min=0, max=2**31 - 1,
                name=item, value=current_value, disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))
        elif item == "defaultRepository":
            page.utils.generateRepositorySelect(
                db, user, value, allow_selecting_none=True,
                placeholder_text="No default repository", selected=current_value,
                name=item, disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))
        elif item == "defaultPage":
            options = value.select(
                "setting", name=item, disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))

            addOption("home", "Home")
            addOption("dashboard", "Dashboard")
            addOption("branches", "Branches")
            addOption("config", "Config")
            addOption("tutorial", "Tutorial")
        elif item == "email.urlType":
            cursor2 = db.cursor()
            cursor2.execute("""SELECT key, description, authenticated_scheme, hostname
                                 FROM systemidentities
                             ORDER BY description ASC""")

            identities = cursor2.fetchall()
            selected = set(current_value.split(","))

            options = value.select(
                "setting", name=item, size=len(identities),
                multiple="multiple", disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))

            for key, label, authenticated_scheme, hostname in identities:
                prefix = "%s://%s/" % (authenticated_scheme, hostname)
                addOption(key, label, selected=lambda value: value in selected,
                          class_="url-type flex",
                          data_text=label,
                          data_html=("<span class=label>%s</span>"
                                     "<span class=prefix>%s</span>"
                                     % (htmlutils.htmlify(label),
                                        htmlutils.htmlify(prefix))))

        elif item == "email.updatedReview.quotedComments":
            options = value.select(
                "setting", name=item, disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))

            addOption("all", "All")
            addOption("first", "First")
            addOption("last", "Last")
            addOption("firstlast", "First & Last")
        elif item == "timezone":
            options = value.select(
                "setting", name=item, disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))

            for group, zones in dbutils.timezones.sortedTimezones(db):
                optgroup = options.optgroup(label=group)
                for name, abbrev, utc_offset in zones:
                    seconds = utc_offset.total_seconds()
                    offset = "%s%02d:%02d" % ("-" if seconds < 0 else "+", abs(seconds) / 3600, (abs(seconds) % 3600) / 60)
                    addOption("%s/%s" % (group, name), "%s (%s / UTC%s)" % (name, abbrev, offset))
        elif item == "repository.urlType":
            options = value.select(
                "setting", name=item, disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))
            long_path = os.path.join(configuration.paths.GIT_DIR, "<path>.git")

            if "git" in configuration.base.REPOSITORY_URL_TYPES:
                addOption("git", "git://%s/<path>.git" % configuration.base.HOSTNAME)
            if "http" in configuration.base.REPOSITORY_URL_TYPES:
                scheme = configuration.base.ACCESS_SCHEME
                if scheme == "both":
                    if user.isAnonymous():
                        scheme = "http"
                    else:
                        scheme = "https"
                addOption("http", "%s://%s/<path>.git" % (scheme, configuration.base.HOSTNAME))
            if "ssh" in configuration.base.REPOSITORY_URL_TYPES:
                addOption("ssh", "ssh://%s%s" % (configuration.base.HOSTNAME, long_path))
            if "host" in configuration.base.REPOSITORY_URL_TYPES:
                addOption("host", "%s:%s" % (configuration.base.HOSTNAME, long_path))
        else:
            if item.startswith("email.subjectLine."):
                placeholder = "Email type disabled"
            else:
                placeholder = None
            value.input(
                "setting", type="text", size=80, name=item,
                placeholder=placeholder, value=current_value, disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))

        also_configurable_per = []

        if per_repository and repository is None:
            also_configurable_per.append("repository")
        if per_filter and filter_id is None:
            also_configurable_per.append("filter")

        if also_configurable_per:
            value.span("also-configurable-per").text(
                "Also configurable per: %s" % ", ".join(also_configurable_per))

        reset = value.span("reset")
        reset.a(href="javascript:saveSettings(%s);" % htmlutils.jsify(item)).text("[reset to default]")

        cell = table.tr(help_class_name).td("help", colspan=3)

        magic_description_links = {
            "format string for subject line":
                "/tutorial?item=reconfigure#subject_line_formats",
            "phony recipients":
                "/tutorial?item=reconfigure#review_association_recipients"
            }

        for link_text, link_href in magic_description_links.items():
            prefix, link_text, suffix = description.partition(link_text)
            if link_text:
                cell.text(prefix)
                cell.a(href=link_href).text(link_text)
                cell.text(suffix)
                break
        else:
            cell.text(description)

    if injected and injected.has_key("preferences") \
            and not defaults \
            and repository is None \
            and filter_id is None:
        for extension_name, author, preferences in injected["preferences"]:
            if highlight is not None:
                prefix = "%s/%s" % (author.name, extension_name)
                preferences = [
                    preference for preference in preferences
                    if fnmatch.fnmatch("%s/%s" % (prefix, preference["name"]),
                                       highlight)]

                if not preferences:
                    continue

            h2 = table.tr("extension").td("extension", colspan=3).h2()
            h2.span("name").text(extension_name)
            h2.text(" by ")
            h2.span("author").text(author.fullname)

            for preference in preferences:
                preference_url = preference["url"]
                preference_name = preference["name"]
                preference_type = preference["type"]
                preference_value = preference["value"]
                preference_default = preference["default"]
                preference_description = preference["description"]

                line_class_name = "line"
                help_class_name = "help"

                if preference_value != preference_default:
                    line_class_name += " customized"

                row = table.tr(line_class_name)
                heading = row.td("heading")
                heading.text("%s:" % preference_name)
                value = row.td("value", colspan=2)
                value.preformatted()

                if preference_type == "boolean":
                    value.input(
                        "setting", type="checkbox",
                        name=preference_name, disabled=disabled,
                        checked="checked" if preference_value else None,
                        critic_url=preference_url,
                        critic_default=htmlutils.jsify(bool(preference_value)),
                        critic_extension=extension_name)
                elif preference_type == "integer":
                    value.input(
                        "setting", type="number", min=0,
                        name=preference_name, value=preference_value,
                        disabled=disabled, critic_url=preference_url,
                        critic_default=htmlutils.jsify(preference_default),
                        critic_extension=extension_name)
                elif preference_type == "string":
                    value.input(
                        "setting", type="text",
                        name=preference_name, value=preference_value,
                        disabled=disabled, critic_url=preference_url,
                        critic_default=htmlutils.jsify(preference_default),
                        critic_extension=extension_name)
                else:
                    select = value.select(
                        "setting", name=preference_name,
                        disabled=disabled, critic_url=preference_url,
                        critic_value=preference_value,
                        critic_default=htmlutils.jsify(preference_default),
                        critic_extension=extension_name)

                    for choice in preference_type:
                        select.option(value=choice["value"], selected="selected" if preference_value == choice["value"] else None).text(choice["title"])

                cell = table.tr(help_class_name).td("help", colspan=3)
                cell.text(preference_description)

    critic_installed_sha1 = dbutils.getInstalledSHA1(db)
    div = body.div("installed_sha1")
    div.text("Critic version: ")
    div.a(href="https://critic-review.org/critic/%s" % critic_installed_sha1).text(critic_installed_sha1)

    return document
Beispiel #4
0
def renderConfig(req, db, user):
    highlight = req.getParameter("highlight", None)
    repository = req.getParameter("repository", None,
                                  gitutils.Repository.FromParameter(db))
    filter_id = req.getParameter("filter", None, int)
    defaults = req.getParameter("defaults", "no") == "yes"

    if filter_id is not None:
        # There can't be system-wide defaults for one of a single user's
        # filters.
        defaults = False

    cursor = db.cursor()

    if filter_id is not None:
        cursor.execute(
            """SELECT filters.path, repositories.name
                            FROM filters
                            JOIN repositories ON (repositories.id=filters.repository)
                           WHERE filters.id=%s""", (filter_id, ))
        row = cursor.fetchone()
        if not row:
            raise page.utils.InvalidParameterValue(name="filter",
                                                   value=str(filter_id),
                                                   expected="valid filter id")
        title = "Filter preferences: %s in %s" % row
    elif repository is not None:
        title = "Repository preferences: %s" % repository.name
    else:
        title = "User preferences"

    document = htmlutils.Document(req)
    document.setTitle(title)

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

    if user.isAnonymous():
        disabled = "disabled"
    else:
        disabled = None

    def generate_right(target):
        if defaults:
            url = "/config"
            if repository is not None:
                url += "?repository=%d" % repository.id
            target.a("button", href=url).text("Edit Own")
        elif user.hasRole(db, "administrator"):
            url = "/config?defaults=yes"
            if repository is not None:
                url += "&repository=%d" % repository.id
                what = "Repository Defaults"
            else:
                what = "System Defaults"
            target.a("button", href=url).text("Edit " + what)

    injected = page.utils.generateHeader(body,
                                         db,
                                         user,
                                         current_page="config",
                                         generate_right=generate_right)

    document.addExternalStylesheet("resource/config.css")
    document.addExternalScript("resource/config.js")
    document.addInternalScript(user.getJS())
    document.addInternalScript(
        "var repository_id = %s, filter_id = %s, defaults = %s;" %
        (htmlutils.jsify(repository.id if repository else None),
         htmlutils.jsify(filter_id), htmlutils.jsify(defaults)))

    target = body.div("main")

    table = target.table('preferences paleyellow',
                         align='center',
                         cellspacing=0)
    h1 = table.tr().td('h1', colspan=3).h1()
    h1.text(title)

    if filter_id is None:
        page.utils.generateRepositorySelect(
            db,
            user,
            h1.span("right"),
            allow_selecting_none=True,
            selected=repository.name if repository else False)

    if filter_id is not None:
        conditional = "per_filter"
    elif repository is not None:
        conditional = "per_repository"
    elif defaults:
        conditional = "per_system"
    else:
        conditional = "per_user"

    cursor = db.cursor()
    cursor.execute(
        """SELECT item, type, description, per_repository, per_filter
                        FROM preferences
                       WHERE %(conditional)s""" % {"conditional": conditional})

    preferences = dict((
        item,
        [preference_type, description, None, None, per_repository, per_filter])
                       for item, preference_type, description, per_repository,
                       per_filter in cursor)

    def set_values(rows, is_overrides):
        index = 3 if is_overrides else 2
        for item, integer, string in rows:
            if preferences[item][0] == "boolean":
                preferences[item][index] = bool(integer)
            elif preferences[item][0] == "integer":
                preferences[item][index] = integer
            else:
                preferences[item][index] = string

    cursor.execute(
        """SELECT item, integer, string
                        FROM userpreferences
                       WHERE item=ANY (%s)
                         AND uid IS NULL
                         AND repository IS NULL""", (preferences.keys(), ))

    set_values(cursor, is_overrides=False)

    if repository is not None:
        cursor.execute(
            """SELECT item, integer, string
                            FROM userpreferences
                           WHERE item=ANY (%s)
                             AND uid IS NULL
                             AND repository=%s""",
            (preferences.keys(), repository.id))

        # These are overrides if we're editing the defaults for a specific
        # repository.
        set_values(cursor, is_overrides=defaults)

    if not defaults:
        cursor.execute(
            """SELECT item, integer, string
                            FROM userpreferences
                           WHERE item=ANY (%s)
                             AND uid=%s
                             AND repository IS NULL
                             AND filter IS NULL""",
            (preferences.keys(), user.id))

        if filter_id is not None or repository is not None:
            # We're looking at per-filter or per-repository settings, so the
            # user's global settings are defaults, not the overrides.  If a
            # per-filter or per-repository override is deleted, the user's
            # global setting kicks in instead.
            set_values(cursor, is_overrides=False)

            if filter_id is not None:
                cursor.execute(
                    """SELECT item, integer, string
                                    FROM userpreferences
                                   WHERE item=ANY (%s)
                                     AND uid=%s
                                     AND filter=%s""",
                    (preferences.keys(), user.id, filter_id))
            else:
                cursor.execute(
                    """SELECT item, integer, string
                                    FROM userpreferences
                                   WHERE item=ANY (%s)
                                     AND uid=%s
                                     AND repository=%s""",
                    (preferences.keys(), user.id, repository.id))

        # Set the overrides.  This is either the user's global settings, if
        # we're not looking at per-filter or per-repository settings, or the
        # user's per-filter or per-repository settings if we are.
        set_values(cursor, is_overrides=True)
    elif repository is None:
        # When editing global defaults, use the values from preferences.json
        # used when initially installing Critic as the default values.
        defaults_path = os.path.join(configuration.paths.INSTALL_DIR,
                                     "data/preferences.json")
        with open(defaults_path) as defaults_file:
            factory_defaults = textutils.json_decode(defaults_file.read())
        for item, data in preferences.items():
            data[3] = data[2]
            if item in factory_defaults:
                data[2] = factory_defaults[item]["default"]
                if data[2] == data[3]:
                    data[3] = None

    if req.getParameter("recalculate", "no") == "yes":
        for item, data in preferences.items():
            if data[2] == data[3]:
                user.setPreference(db,
                                   item,
                                   None,
                                   repository=repository,
                                   filter_id=filter_id)
                data[3] = None
        db.commit()

    debug_enabled = user.getPreference(db, "debug.enabled")

    for item, (preference_type, description, default_value, current_value,
               per_repository, per_filter) in sorted(preferences.items()):
        if item.startswith(
                "debug.") and item != "debug.enabled" and not debug_enabled:
            continue

        line_class_name = "line"
        help_class_name = "help"

        if highlight is not None and not fnmatch.fnmatch(item, highlight):
            continue

        if current_value is None:
            current_value = default_value
        else:
            line_class_name += " customized"

        row = table.tr(line_class_name)
        heading = row.td("heading")
        heading.text("%s:" % item)
        value = row.td("value", colspan=2)
        value.preformatted()

        options = None
        optgroup = None

        def addOption(value,
                      name,
                      selected=lambda value: value == current_value,
                      **attributes):
            (optgroup or options).option(
                value=value,
                selected="selected" if selected(value) else None,
                **attributes).text(name)

        if preference_type == "boolean":
            value.input("setting",
                        type="checkbox",
                        name=item,
                        checked="checked" if current_value else None,
                        disabled=disabled,
                        critic_current=htmlutils.jsify(current_value),
                        critic_default=htmlutils.jsify(default_value))
        elif preference_type == "integer":
            value.input("setting",
                        type="number",
                        min=0,
                        max=2**31 - 1,
                        name=item,
                        value=current_value,
                        disabled=disabled,
                        critic_current=htmlutils.jsify(current_value),
                        critic_default=htmlutils.jsify(default_value))
        elif item == "defaultRepository":
            page.utils.generateRepositorySelect(
                db,
                user,
                value,
                allow_selecting_none=True,
                placeholder_text="No default repository",
                selected=current_value,
                name=item,
                disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))
        elif item == "defaultPage":
            options = value.select(
                "setting",
                name=item,
                disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))

            addOption("home", "Home")
            addOption("dashboard", "Dashboard")
            addOption("branches", "Branches")
            addOption("config", "Config")
            addOption("tutorial", "Tutorial")
        elif item == "email.urlType":
            cursor2 = db.cursor()
            cursor2.execute(
                """SELECT key, description, authenticated_scheme, hostname
                                 FROM systemidentities
                             ORDER BY description ASC""")

            identities = cursor2.fetchall()
            selected = set(current_value.split(","))

            options = value.select(
                "setting",
                name=item,
                size=len(identities),
                multiple="multiple",
                disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))

            for key, label, authenticated_scheme, hostname in identities:
                prefix = "%s://%s/" % (authenticated_scheme, hostname)
                addOption(
                    key,
                    label,
                    selected=lambda value: value in selected,
                    class_="url-type flex",
                    data_text=label,
                    data_html=(
                        "<span class=label>%s</span>"
                        "<span class=prefix>%s</span>" %
                        (htmlutils.htmlify(label), htmlutils.htmlify(prefix))))

        elif item == "email.updatedReview.quotedComments":
            options = value.select(
                "setting",
                name=item,
                disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))

            addOption("all", "All")
            addOption("first", "First")
            addOption("last", "Last")
            addOption("firstlast", "First & Last")
        elif item == "timezone":
            options = value.select(
                "setting",
                name=item,
                disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))

            for group, zones in dbutils.timezones.sortedTimezones(db):
                optgroup = options.optgroup(label=group)
                for name, abbrev, utc_offset in zones:
                    seconds = utc_offset.total_seconds()
                    offset = "%s%02d:%02d" % ("-" if seconds < 0 else "+",
                                              abs(seconds) / 3600,
                                              (abs(seconds) % 3600) / 60)
                    addOption("%s/%s" % (group, name),
                              "%s (%s / UTC%s)" % (name, abbrev, offset))
        elif item == "repository.urlType":
            options = value.select(
                "setting",
                name=item,
                disabled=disabled,
                critic_current=htmlutils.jsify(current_value),
                critic_default=htmlutils.jsify(default_value))
            long_path = os.path.join(configuration.paths.GIT_DIR, "<path>.git")

            if "git" in configuration.base.REPOSITORY_URL_TYPES:
                addOption("git",
                          "git://%s/<path>.git" % configuration.base.HOSTNAME)
            if "http" in configuration.base.REPOSITORY_URL_TYPES:
                scheme = configuration.base.ACCESS_SCHEME
                if scheme == "both":
                    if user.isAnonymous():
                        scheme = "http"
                    else:
                        scheme = "https"
                addOption(
                    "http", "%s://%s/<path>.git" %
                    (scheme, configuration.base.HOSTNAME))
            if "ssh" in configuration.base.REPOSITORY_URL_TYPES:
                addOption(
                    "ssh",
                    "ssh://%s%s" % (configuration.base.HOSTNAME, long_path))
            if "host" in configuration.base.REPOSITORY_URL_TYPES:
                addOption("host",
                          "%s:%s" % (configuration.base.HOSTNAME, long_path))
        else:
            if item.startswith("email.subjectLine."):
                placeholder = "Email type disabled"
            else:
                placeholder = None
            value.input("setting",
                        type="text",
                        size=80,
                        name=item,
                        placeholder=placeholder,
                        value=current_value,
                        disabled=disabled,
                        critic_current=htmlutils.jsify(current_value),
                        critic_default=htmlutils.jsify(default_value))

        also_configurable_per = []

        if per_repository and repository is None:
            also_configurable_per.append("repository")
        if per_filter and filter_id is None:
            also_configurable_per.append("filter")

        if also_configurable_per:
            value.span("also-configurable-per").text(
                "Also configurable per: %s" % ", ".join(also_configurable_per))

        reset = value.span("reset")
        reset.a(href="javascript:saveSettings(%s);" %
                htmlutils.jsify(item)).text("[reset to default]")

        cell = table.tr(help_class_name).td("help", colspan=3)

        magic_description_links = {
            "format string for subject line":
            "/tutorial?item=reconfigure#subject_line_formats",
            "phony recipients":
            "/tutorial?item=reconfigure#review_association_recipients"
        }

        for link_text, link_href in magic_description_links.items():
            prefix, link_text, suffix = description.partition(link_text)
            if link_text:
                cell.text(prefix)
                cell.a(href=link_href).text(link_text)
                cell.text(suffix)
                break
        else:
            cell.text(description)

    if injected and injected.has_key("preferences") \
            and not defaults \
            and repository is None \
            and filter_id is None:
        for extension_name, author, preferences in injected["preferences"]:
            if highlight is not None:
                prefix = "%s/%s" % (author.name, extension_name)
                preferences = [
                    preference for preference in preferences
                    if fnmatch.fnmatch("%s/%s" %
                                       (prefix, preference["name"]), highlight)
                ]

                if not preferences:
                    continue

            h2 = table.tr("extension").td("extension", colspan=3).h2()
            h2.span("name").text(extension_name)
            h2.text(" by ")
            h2.span("author").text(author.fullname)

            for preference in preferences:
                preference_url = preference["url"]
                preference_name = preference["name"]
                preference_type = preference["type"]
                preference_value = preference["value"]
                preference_default = preference["default"]
                preference_description = preference["description"]

                line_class_name = "line"
                help_class_name = "help"

                if preference_value != preference_default:
                    line_class_name += " customized"

                row = table.tr(line_class_name)
                heading = row.td("heading")
                heading.text("%s:" % preference_name)
                value = row.td("value", colspan=2)
                value.preformatted()

                if preference_type == "boolean":
                    value.input(
                        "setting",
                        type="checkbox",
                        name=preference_name,
                        disabled=disabled,
                        checked="checked" if preference_value else None,
                        critic_url=preference_url,
                        critic_default=htmlutils.jsify(bool(preference_value)),
                        critic_extension=extension_name)
                elif preference_type == "integer":
                    value.input(
                        "setting",
                        type="number",
                        min=0,
                        name=preference_name,
                        value=preference_value,
                        disabled=disabled,
                        critic_url=preference_url,
                        critic_default=htmlutils.jsify(preference_default),
                        critic_extension=extension_name)
                elif preference_type == "string":
                    value.input(
                        "setting",
                        type="text",
                        name=preference_name,
                        value=preference_value,
                        disabled=disabled,
                        critic_url=preference_url,
                        critic_default=htmlutils.jsify(preference_default),
                        critic_extension=extension_name)
                else:
                    select = value.select(
                        "setting",
                        name=preference_name,
                        disabled=disabled,
                        critic_url=preference_url,
                        critic_value=preference_value,
                        critic_default=htmlutils.jsify(preference_default),
                        critic_extension=extension_name)

                    for choice in preference_type:
                        select.option(value=choice["value"],
                                      selected="selected" if preference_value
                                      == choice["value"] else None).text(
                                          choice["title"])

                cell = table.tr(help_class_name).td("help", colspan=3)
                cell.text(preference_description)

    critic_installed_sha1 = dbutils.getInstalledSHA1(db)
    div = body.div("installed_sha1")
    div.text("Critic version: ")
    div.a(href="https://critic-review.org/critic/%s" %
          critic_installed_sha1).text(critic_installed_sha1)

    return document