diff --git a/.gitignore b/.gitignore index ade15d6..9cafb92 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,11 @@ +data/ +.*.swp +*.pyc *.egg-info/ +.coverage +.ropeproject/ *.mo *.pot -*.pyc -.*.swp -.coverage -.idea/ -.ropeproject/ -/dist/ -data/ -debianmemberportfolio/model/keyringcache.db tags +debianmemberportfolio/model/keyringcache.db +.idea/ diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index 87d3ab4..0000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,14 +0,0 @@ ---- -version: 2 - -build: - os: ubuntu-22.04 - tools: - python: "3.11" - -sphinx: - configuration: docs/source/conf.py - -python: - install: - - requirements: docs/doc-requirements.txt diff --git a/ChangeLog b/ChangeLog index 8c45a78..7cb22ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,24 +1,3 @@ -2023-06-03 Jan Dittberner - * add updated translations from Weblate - * switch to Poetry for dependency management - * describe translation workflow in developer documentation - * fix internal server error when name is missing for non Debian member (thanks Paul Wise for the report) - * fix unicode handling in urlbuilder (thanks Paul Wise for the report) - -2022-09-24 Jan Dittberner - * add updated translations from Weblate - * update dependencies - * replace gpg and pgp with OpenPGP (thanks Guillem Jover) - * remove flattr code - -2021-10-24 Jan Dittberner - * add updated translations from Weblate - * fix NM URL (thanks Diederik de Haas) - -2021-07-03 Jan Dittberner - * Add translations from Weblate - * remove a dead services (thanks Juri Grabowski) - 2020-02-22 Jan Dittberner * Update translations diff --git a/README.md b/README.md deleted file mode 100644 index 8ea677b..0000000 --- a/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Debian Member Portfolio Service - -This is a service implementation that returns a set of personalized URLs as outlined in -https://wiki.debian.org/DDPortfolio. It takes the Debian Member's full name and email address as input and returns -a JSON formatted array or an HTML page of URLs. - -See https://debian-member-portfolio-service.readthedocs.org/ for more documentation (or its source in -docs/source/devdocs.rst), including how to configure a development environment. - -## Translations - -Translations for the Debian Member Portfolio service are maintained using -[Weblate](https://hosted.weblate.org/projects/debian-member-portfolio-service/translations/). Thanks to Weblate for -hosting the translation service and to all contributors of translations. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..1748835 --- /dev/null +++ b/README.txt @@ -0,0 +1,11 @@ + +This is the source code for the Debian Member Portfolio Service +application [0]. + +Cf. https://debian-member-portfolio-service.readthedocs.org/ for more +documentation (or its source in docs/source/devdocs.rst), including +how to configure a development environment. + + +[0] https://wiki.debian.org/DDPortfolio + diff --git a/debianmemberportfolio/forms.py b/debianmemberportfolio/forms.py index fc3d132..24872ee 100644 --- a/debianmemberportfolio/forms.py +++ b/debianmemberportfolio/forms.py @@ -3,7 +3,7 @@ # # Debian Member Portfolio Service views # -# Copyright © 2015-2022 Jan Dittberner +# Copyright © 2015-2020 Jan Dittberner # # This file is part of the Debian Member Portfolio Service. # @@ -51,7 +51,7 @@ def gpg_fingerprint(data): class DeveloperData(FlaskForm): email = StringField('email', validators=[DataRequired(), Email()]) name = StringField('name', validators=[Optional(), DataRequired()]) - openpgpfp = StringField('openpgpfp', filters=[gpg_fingerprint], validators=[ + gpgfp = StringField('gpgfp', filters=[gpg_fingerprint], validators=[ Optional(), FingerPrint(), Length(min=32, max=40) ]) username = StringField('username', validators=[Optional(), PlainText()]) diff --git a/debianmemberportfolio/model/dddatabuilder.py b/debianmemberportfolio/model/dddatabuilder.py index 1fa5be1..c168489 100644 --- a/debianmemberportfolio/model/dddatabuilder.py +++ b/debianmemberportfolio/model/dddatabuilder.py @@ -3,7 +3,7 @@ # # Debian Member Portfolio Service data builder # -# Copyright © 2009-2022 Jan Dittberner +# Copyright © 2009-2015 Jan Dittberner # # This file is part of the Debian Member Portfolio Service. # @@ -39,13 +39,13 @@ def build_data(email_address): """Build a DD data structure from a given email address.""" fields = dict([(field, func(str(email_address))) for (field, func) in - [('openpgpfp', keyfinder.getFingerprintByEmail), + [('gpgfp', keyfinder.getFingerprintByEmail), ('name', keyfinder.getRealnameByEmail), ('username', keyfinder.getLoginByEmail)]]) fields['email'] = email_address - if fields['username'] and fields['openpgpfp'] and fields['name']: + if fields['username'] and fields['gpgfp'] and fields['name']: fields['type'] = TYPE_DD - elif fields['name'] and fields['openpgpfp']: + elif fields['name'] and fields['gpgfp']: fields['type'] = TYPE_DM else: fields['type'] = TYPE_NO diff --git a/debianmemberportfolio/model/keyfinder.py b/debianmemberportfolio/model/keyfinder.py index 044648a..5129e54 100644 --- a/debianmemberportfolio/model/keyfinder.py +++ b/debianmemberportfolio/model/keyfinder.py @@ -3,7 +3,7 @@ # # Debian Member Portfolio Service key finder module # -# Copyright © 2009-2023 Jan Dittberner +# Copyright © 2009-2015 Jan Dittberner # # This file is part of the Debian Member Portfolio Service. # @@ -21,39 +21,42 @@ # along with this program. If not, see . # """ -This module provides tools for finding OpenPGP key information from a +This module provides tools for finding PGP key information from a given keyring. """ import logging -import sys import time -from importlib import resources +import sys db = None -cache_timestamp = 0 +cachetimestamp = 0 def _get_keyring_cache(): - global db, cache_timestamp - if db is None or (time.time() - cache_timestamp) > 86300: + global db, cachetimestamp + if db is None or (time.time() - cachetimestamp) > 86300: import dbm + import pkg_resources import os.path - - dbm_filename = str(resources.files(__package__).joinpath("keyringcache.db")) - logging.debug("reading cache data from %s", dbm_filename) - assert os.path.exists(dbm_filename) and os.path.isfile(dbm_filename) - db = dbm.open(dbm_filename[: -len(".db")], "r") - cache_timestamp = time.time() + filename = pkg_resources.resource_filename(__name__, + 'keyringcache') + logging.debug('reading cache data from %s', filename) + assert ( + os.path.exists(filename + '.db') and + os.path.isfile(filename + '.db') + ) + db = dbm.open(filename, 'r') + cachetimestamp = time.time() return db def _get_cached(cachekey): cache = _get_keyring_cache() - logging.debug("cache lookup for %s", cachekey) + logging.debug('cache lookup for %s', cachekey) if cachekey in cache: - logging.debug("found entry %s", cache[cachekey]) - return cache[cachekey].decode("utf8") + logging.debug('found entry %s', cache[cachekey]) + return cache[cachekey].decode('utf8') return None @@ -62,7 +65,7 @@ def getFingerprintByEmail(email): Gets the fingerprints associated with the given email address if available. """ - return _get_cached("fpr:email:%s" % email) + return _get_cached('fpr:email:%s' % email) def getRealnameByEmail(email): @@ -70,7 +73,7 @@ def getRealnameByEmail(email): Gets the real names associated with the given email address if available. """ - return _get_cached("name:email:%s" % email) + return _get_cached('name:email:%s' % email) def getLoginByEmail(email): @@ -78,34 +81,34 @@ def getLoginByEmail(email): Gets the logins associated with the given email address if available. """ - return _get_cached("login:email:%s" % email) + return _get_cached('login:email:%s' % email) def getLoginByFingerprint(fpr): """ Gets the login associated with the given fingerprint if available. """ - return _get_cached("login:fpr:%s" % fpr) + return _get_cached('login:fpr:%s' % fpr) def _dump_cache(): cache = _get_keyring_cache() fprs = [] - for key in [key.decode("utf8") for key in list(cache.keys())]: - if key.startswith("email:fpr:"): - fpr = key.replace("email:fpr:", "") + for key in [key.decode('utf8') for key in list(cache.keys())]: + if key.startswith('email:fpr:'): + fpr = key.replace('email:fpr:', '') if not fpr in fprs: fprs.append(fpr) for fpr in fprs: login = getLoginByFingerprint(fpr) - email = _get_cached("email:fpr:%s" % fpr) - name = _get_cached("name:fpr:%s" % fpr) + email = _get_cached('email:fpr:%s' % fpr) + name = _get_cached('name:fpr:%s' % fpr) - print(fpr, login, ":") - print(" ", name, email) + print(fpr, login, ':') + print(' ', name, email) -if __name__ == "__main__": +if __name__ == '__main__': logging.basicConfig(stream=sys.stderr, level=logging.WARNING) _dump_cache() diff --git a/debianmemberportfolio/model/keyringanalyzer.py b/debianmemberportfolio/model/keyringanalyzer.py index 0a007b8..9ea3fab 100644 --- a/debianmemberportfolio/model/keyringanalyzer.py +++ b/debianmemberportfolio/model/keyringanalyzer.py @@ -3,7 +3,7 @@ # # Debian Member Portfolio Service application key ring analyzer tool # -# Copyright © 2009-2023 Jan Dittberner +# Copyright © 2009-2015 Jan Dittberner # # This file is part of the Debian Member Portfolio Service. # @@ -21,21 +21,22 @@ # along with this program. If not, see . # """ -This is a tool that analyzes GPG and PGP key rings and stores the +This is a tool that analyzes GPG and PGP keyrings and stores the retrieved data in a file database. The tool was inspired by Debian qa's carnivore. """ -import configparser import dbm -import email.utils +import pkg_resources import glob -import logging +import configparser import os import os.path +import logging import subprocess import sys -from importlib import resources +import email.utils + CONFIG = configparser.ConfigParser() @@ -45,18 +46,18 @@ def _get_keyrings(): Gets the available keyring files from the keyring directory configured in portfolio.ini. """ - keyring_dir = os.path.expanduser(CONFIG.get("DEFAULT", "keyring.dir")) - logging.debug("keyring dir is %s", keyring_dir) - keyrings = glob.glob(os.path.join(keyring_dir, "*.gpg")) - keyrings.extend(glob.glob(os.path.join(keyring_dir, "*.pgp"))) + keyringdir = os.path.expanduser(CONFIG.get('DEFAULT', 'keyring.dir')) + logging.debug("keyring dir is %s", keyringdir) + keyrings = glob.glob(os.path.join(keyringdir, '*.gpg')) + keyrings.extend(glob.glob(os.path.join(keyringdir, '*.pgp'))) keyrings.sort() return keyrings def _parse_uid(uid): """ - Parse an uid of the form 'Real Name ' into email - and real name parts. + Parse a uid of the form 'Real Name ' into email + and realname parts. """ # First try with the Python library, but it doesn't always catch everything @@ -66,65 +67,63 @@ def _parse_uid(uid): if (not name) or (not mail): logging.debug("strange uid %s: '%s' - <%s>", uid, name, mail) # Try and do better than the python library - if "@" not in mail: + if not '@' in mail: uid = uid.strip() # First, strip comment - s = uid.find("(") - e = uid.find(")") + s = uid.find('(') + e = uid.find(')') if s >= 0 and e >= 0: - uid = uid[:s] + uid[e + 1 :] - s = uid.find("<") - e = uid.find(">") + uid = uid[:s] + uid[e + 1:] + s = uid.find('<') + e = uid.find('>') mail = None if s >= 0 and e >= 0: - mail = uid[s + 1 : e] - uid = uid[:s] + uid[e + 1 :] + mail = uid[s + 1:e] + uid = uid[:s] + uid[e + 1:] uid = uid.strip() - if not mail and uid.find("@") >= 0: + if not mail and uid.find('@') >= 0: mail, uid = uid, mail name = uid logging.debug("corrected: '%s' - <%s>", name, mail) - return name, mail + return (name, mail) - -result_dict = {} +resultdict = {} def _get_canonical(key): - if key not in result_dict: - result_dict[key] = [] + if not key in resultdict: + resultdict[key] = [] return key -def _add_to_result(key, new_value): - logging.debug("adding %s: %s", key, new_value) - the_key = _get_canonical(key) - if new_value not in result_dict[the_key]: - result_dict[the_key].append(new_value) +def _add_to_result(key, newvalue): + logging.debug("adding %s: %s", key, newvalue) + thekey = _get_canonical(key) + if newvalue not in resultdict[thekey]: + resultdict[thekey].append(newvalue) def _handle_mail(mail, fpr): - if mail.endswith("@debian.org"): - login = mail[0 : -len("@debian.org")] - _add_to_result("login:email:%s" % mail, login) - _add_to_result("login:fpr:%s" % fpr, login) - _add_to_result("fpr:login:%s" % login, fpr) - _add_to_result("fpr:email:%s" % mail, fpr) - _add_to_result("email:fpr:%s" % fpr, mail) + if mail.endswith('@debian.org'): + login = mail[0:-len('@debian.org')] + _add_to_result('login:email:%s' % mail, login) + _add_to_result('login:fpr:%s' % fpr, login) + _add_to_result('fpr:login:%s' % login, fpr) + _add_to_result('fpr:email:%s' % mail, fpr) + _add_to_result('email:fpr:%s' % fpr, mail) def _handle_uid(uid, fpr): - mail = None # Do stuff with 'uid' if uid: (uid, mail) = _parse_uid(uid) if mail: _handle_mail(mail, fpr) if uid: - _add_to_result("name:fpr:%s" % fpr, uid) + _add_to_result('name:fpr:%s' % fpr, uid) if mail: - _add_to_result("name:email:%s" % mail, uid) + _add_to_result('name:email:%s' % mail, uid) return fpr @@ -132,13 +131,13 @@ def process_gpg_list_keys_line(line, fpr): """ Process a line of gpg --list-keys --with-colon output. """ - items = line.split(":") - if items[0] == "pub": + items = line.split(':') + if items[0] == 'pub': return None - if items[0] == "fpr": + if items[0] == 'fpr': return items[9].strip() - if items[0] == "uid": - if items[1] == "r": + if items[0] == 'uid': + if items[1] == 'r': return fpr return _handle_uid(items[9].strip(), fpr) else: @@ -146,54 +145,41 @@ def process_gpg_list_keys_line(line, fpr): def process_keyrings(): - """Process the keyrings and store the extracted data in an anydbm file.""" + """Process the keyrings and store the extracted data in an anydbm + file.""" for keyring in _get_keyrings(): logging.debug("get data from %s", keyring) - proc = subprocess.Popen( - [ - "gpg", - "--no-options", - "--no-default-keyring", - "--homedir", - os.path.expanduser(CONFIG.get("DEFAULT", "gnupghome")), - "--no-expensive-trust-checks", - "--keyring", - keyring, - "--list-keys", - "--with-colons", - "--fixed-list-mode", - "--with-fingerprint", - "--with-fingerprint", - ], - stdout=subprocess.PIPE, - ) + proc = subprocess.Popen([ + "gpg", "--no-options", "--no-default-keyring", + "--homedir", os.path.expanduser( + CONFIG.get('DEFAULT', 'gnupghome')), + "--no-expensive-trust-checks", + "--keyring", keyring, "--list-keys", + "--with-colons", "--fixed-list-mode", "--with-fingerprint", + "--with-fingerprint"], + stdout=subprocess.PIPE) fpr = None for line in proc.stdout.readlines(): try: - line = line.decode("utf8") + line = line.decode('utf8') except UnicodeDecodeError: - line = line.decode("iso8859-1") + line = line.decode('iso8859-1') fpr = process_gpg_list_keys_line(line, fpr) - ret_code = proc.wait() - if ret_code != 0: - logging.error("subprocess ended with return code %d", ret_code) - dbm_filename = str( - resources.files("debianmemberportfolio.model").joinpath("keyringcache") - ) - db = dbm.open(dbm_filename, "c") - for key in result_dict: - db[key] = ":".join(result_dict[key]) + retcode = proc.wait() + if retcode != 0: + logging.error("subprocess ended with return code %d", retcode) + db = dbm.open(pkg_resources.resource_filename(__name__, + 'keyringcache'), 'c') + for key in resultdict: + db[key] = ":".join(resultdict[key]) db.close() -if __name__ == "__main__": +if __name__ == '__main__': logging.basicConfig(stream=sys.stderr, level=logging.WARNING) - CONFIG.read_string( - resources.files("debianmemberportfolio.model") - .joinpath("portfolio.ini") - .read_text("utf8") - ) - gpg_home = os.path.expanduser(CONFIG.get("DEFAULT", "gnupghome")) - if not os.path.isdir(gpg_home): - os.makedirs(gpg_home, 0o700) + CONFIG.read_string(pkg_resources.resource_string( + __name__, 'portfolio.ini').decode('utf8')) + gpghome = os.path.expanduser(CONFIG.get('DEFAULT', 'gnupghome')) + if not os.path.isdir(gpghome): + os.makedirs(gpghome, 0o700) process_keyrings() diff --git a/debianmemberportfolio/model/portfolio.ini b/debianmemberportfolio/model/portfolio.ini index 6baedad..7cf278a 100644 --- a/debianmemberportfolio/model/portfolio.ini +++ b/debianmemberportfolio/model/portfolio.ini @@ -1,7 +1,7 @@ # # Configuration for Debian Member Portfolio Service # -# Copyright © 2009-2022 Jan Dittberner +# Copyright © 2009-2020 Jan Dittberner # # This file is part of the Debian Member Portfolio Service. # @@ -44,13 +44,13 @@ urls=buildd buildd.pattern=https://buildd.debian.org/status/package.php?p=%(email)s&compact=compact&comaint=yes [qa] -urls=lintian,lintianfull,piuparts,dmd,janitor +urls=lintian,lintianfull,piuparts,patchtracker,dmd,duck,janitor dmd.pattern=https://udd.debian.org/dmd.cgi?email1=%(email)s lintian.pattern=https://lintian.debian.org/maintainer/%(email)s.html lintianfull.pattern=https://lintian.debian.org/full/%(email)s.html piuparts.pattern=https://piuparts.debian.org/sid/maintainer/%(firstchar)s/%(email)s.html -#patchtracker.pattern=http://patch-tracker.debian.org/email/%(email)s -#duck.pattern=http://duck.debian.net/persons/%(email)s.html +patchtracker.pattern=http://patch-tracker.debian.org/email/%(email)s +duck.pattern=http://duck.debian.net/persons/%(email)s.html janitor.pattern=https://janitor.debian.net/m/%(email)s [lists] @@ -69,15 +69,15 @@ people.pattern=https://people.debian.org/~%(username)s/ people.optional=true [membership] -urls=nm,dbfinger,db,salsa,wiki,forum -nm.pattern=https://nm.debian.org/person/%(username)s +urls=nm,dbfinger,db,webid,salsa,wiki,forum +nm.pattern=https://nm.debian.org/public/nmstatus/%(username)s dbfinger.pattern=finger %(username)s@db.debian.org dbfinger.type=finger dbfinger.optional=true db.pattern=https://db.debian.org/search.cgi?uid=%(username)s&dosearch=Search db.optional=true -#webid.pattern=http://webid.debian.net/maintainers/%(username)s -#webid.optional=true +webid.pattern=http://webid.debian.net/maintainers/%(username)s +webid.optional=true salsa.pattern=https://salsa.debian.org/%(salsausername)s salsa.optional=true wiki.pattern=https://wiki.debian.org/%(wikihomepage)s @@ -96,7 +96,7 @@ search.pattern=https://search.debian.org/cgi-bin/omega?P=%%22%(name)s%%22 gpgfinger.pattern=finger %(username)s/key@db.debian.org gpgfinger.type=finger gpgfinger.optional=true -gpgweb.pattern=https://db.debian.org/fetchkey.cgi?fingerprint=%(openpgpfp)s +gpgweb.pattern=https://db.debian.org/fetchkey.cgi?fingerprint=%(gpgfp)s gpgweb.optional=true nm.pattern=https://nm.debian.org/public/person/%(username)s contrib.pattern=https://contributors.debian.org/contributor/%(email)s @@ -119,6 +119,6 @@ groupinfo.pattern=ssh master.debian.org id %(username)s groupinfo.type=ssh groupinfo.optional=true -#[ubuntu] -#urls=ubuntudiff -#ubuntudiff.pattern=http://ubuntudiff.debian.net/q/uploaders/%(email)s +[ubuntu] +urls=ubuntudiff +ubuntudiff.pattern=http://ubuntudiff.debian.net/q/uploaders/%(email)s diff --git a/debianmemberportfolio/model/urlbuilder.py b/debianmemberportfolio/model/urlbuilder.py index 0f180ab..cb2f33f 100644 --- a/debianmemberportfolio/model/urlbuilder.py +++ b/debianmemberportfolio/model/urlbuilder.py @@ -3,7 +3,7 @@ # # Debian Member Portfolio Service url builder # -# Copyright © 2009-2023 Jan Dittberner +# Copyright © 2009-2020 Jan Dittberner # # This file is part of the Debian Member Portfolio Service. # @@ -28,40 +28,36 @@ portfolio.ini. from configparser import ConfigParser, InterpolationMissingOptionError from encodings.utf_8 import StreamReader as UTF8StreamReader -from importlib import resources -from urllib.parse import quote_plus +import pkg_resources from debianmemberportfolio.model import keyfinder -from flask_babel import gettext as _ -from flask_babel import lazy_gettext as N_ +from urllib.parse import quote_plus +from flask_babel import gettext as _, lazy_gettext as N_ + my_config = ConfigParser() -ref = resources.files("debianmemberportfolio.model").joinpath("portfolio.ini") -with ref.open("rb") as fp: - my_config.read_file(UTF8StreamReader(fp)) +my_config.read_file(UTF8StreamReader( + pkg_resources.resource_stream(__name__, 'portfolio.ini'))) _FIELDNAMES_MAP = { - "email": N_("Email address"), - "name": N_("Name"), - "openpgpfp": N_("OpenPGP fingerprint"), - "username": N_("Debian user name"), - "nonddemail": N_("Non Debian email address"), - "salsausername": N_("Salsa user name"), + 'email': N_('Email address'), + 'name': N_('Name'), + 'gpgfp': N_('GPG fingerprint'), + 'username': N_('Debian user name'), + 'nonddemail': N_('Non Debian email address'), + 'salsausername': N_('Salsa user name'), } class DDPortfolioEntry(object): def __init__(self, config, section, key): self.name = key - self.optional = ( - config.has_option(section, key + ".optional") - and config.getboolean(section, key + ".optional") - or False - ) - if config.has_option(section, key + ".type"): - self.type = config.get(section, key + ".type") + self.optional = config.has_option(section, key + '.optional') and \ + config.getboolean(section, key + '.optional') or False + if config.has_option(section, key + '.type'): + self.type = config.get(section, key + '.type') else: - self.type = "url" + self.type = 'url' def _build_quoted_fields(fields): @@ -72,19 +68,19 @@ def _build_quoted_fields(fields): for key, value in fields.items(): if value is not None: if isinstance(value, str): - qfields[key] = quote_plus(value.encode("utf8")) + qfields[key] = quote_plus(value.encode('utf8')) elif isinstance(value, str): qfields[key] = quote_plus(value) else: qfields[key] = value - qfields[key] = str(qfields[key]).replace("%", "%%") + qfields[key] = str(qfields[key]).replace('%', '%%') - if "openpgpfp" not in qfields: - fpr = keyfinder.getFingerprintByEmail(fields["email"]) + if 'gpgfp' not in qfields: + fpr = keyfinder.getFingerprintByEmail(fields['email'].encode('utf8')) if fpr: - qfields["openpgpfp"] = fpr[0] - qfields["firstchar"] = fields["email"][0] - qfields["emailnoq"] = fields["email"] + qfields['gpgfp'] = fpr[0] + qfields['firstchar'] = fields['email'][0].encode('utf8') + qfields['emailnoq'] = fields['email'].encode('utf8') return qfields @@ -92,50 +88,27 @@ def build_urls(fields): """Build personalized URLs using the developer information in fields.""" data = [] - quoted_fields = _build_quoted_fields(fields) - for section in [ - section.strip() - for section in my_config.get("DEFAULT", "urlbuilder.sections").split(",") - ]: - data.append(["section", section]) - if my_config.has_option(section, "urls"): - for entry in [ - DDPortfolioEntry(my_config, section, url) - for url in my_config.get(section, "urls").split(",") - ]: + qfields = _build_quoted_fields(fields) + for section in [section.strip() for section in + my_config.get('DEFAULT', + 'urlbuilder.sections').split(',')]: + data.append(['section', section]) + if my_config.has_option(section, 'urls'): + for entry in ([ + DDPortfolioEntry(my_config, section, url) for url in + my_config.get(section, 'urls').split(',')]): try: data.append( - [ - "url", - section, - entry, - my_config.get( - section, - entry.name + ".pattern", - raw=False, - vars=quoted_fields, - ), - ] - ) + ['url', section, entry, + my_config.get(section, entry.name + '.pattern', + raw=False, vars=qfields)]) except InterpolationMissingOptionError as e: if not entry.optional: if e.reference in _FIELDNAMES_MAP: - data.append( - [ - "error", - section, - entry, - _("Missing input: %s") - % _(_FIELDNAMES_MAP[e.reference]), - ] - ) + data.append(['error', section, entry, + _('Missing input: %s') % + _(_FIELDNAMES_MAP[e.reference])]) else: - data.append( - [ - "error", - section, - entry, - _("Missing input: %s") % e.reference, - ] - ) + data.append(['error', section, entry, + _('Missing input: %s') % e.reference]) return data diff --git a/debianmemberportfolio/templates/base.html b/debianmemberportfolio/templates/base.html index 5409df8..82986b7 100644 --- a/debianmemberportfolio/templates/base.html +++ b/debianmemberportfolio/templates/base.html @@ -2,7 +2,7 @@ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> {# vim: ft=jinja Base template for XHTML templates. -Copyright © 2009-2022 Jan Dittberner +Copyright © 2009-2020 Jan Dittberner This file is part of the Debian Member Portfolio service. @@ -38,7 +38,11 @@ with this program. If not, see . + + diff --git a/debianmemberportfolio/templates/showform.html b/debianmemberportfolio/templates/showform.html index 6bc3191..a0d091a 100644 --- a/debianmemberportfolio/templates/showform.html +++ b/debianmemberportfolio/templates/showform.html @@ -2,7 +2,7 @@ {# Template for the data input form. -Copyright © 2009-2022 Jan Dittberner +Copyright © 2009-2020 Jan Dittberner This file is part of the Debian Member Portfolio service. @@ -46,12 +46,12 @@ with this program. If not, see .
{{ form.name }} -
-