diff --git a/.gitignore b/.gitignore index 09b0d287a1c5bd1f2b77a738db877165d539d1ae..79ccbc1968f3fce96e8aef24d61c9902f69e8738 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ *.mo docs/.build/ debexpo.db* -data/ +/data/ *~ *.kpf build diff --git a/debexpo/config/environment.py b/debexpo/config/environment.py index 905f72013f6881ffd7021903ff7f950bbc4b590c..1e4b704ac9513f106695e141eaf06868cb4ee9b9 100644 --- a/debexpo/config/environment.py +++ b/debexpo/config/environment.py @@ -89,7 +89,7 @@ def load_environment(global_conf, app_conf): error_handler=handle_mako_error, module_directory=os.path.join(app_conf['cache_dir'], 'templates'), input_encoding='utf-8', default_filters=['escape'], - imports=['from webhelpers.html import escape']) + imports=['from webhelpers.html import escape', 'from debexpo.lib.filters import semitrusted']) # CONFIGURATION OPTIONS HERE (note: all config options will override # any Pylons config options) diff --git a/debexpo/config/routing.py b/debexpo/config/routing.py index 97dda420060fe4d6408dc20c2cdb6726a04b3a1c..02c94278ed9d65d4a16dd44bb915db3158d28017 100644 --- a/debexpo/config/routing.py +++ b/debexpo/config/routing.py @@ -63,7 +63,9 @@ def make_map(config): map.connect('contact', '/contact', controller='index', action='contact') map.connect('intro-maintainers', '/intro-maintainers', controller='index', action='intro_maintainers') - map.connect('intro-sponsors', '/intro-sponsors', controller='index', action='intro_sponsors') + map.connect('sponsors', '/sponsors', controller='sponsor', action='index') + map.connect('sponsor_tag_save', '/sponsors/save', controller='sponsor', action='save') + map.connect('sponsor_tag_clear', '/sponsors/clear', controller='sponsor', action='clear') map.connect('intro-reviewers', '/intro-reviewers', controller='index', action='intro_reviewers') map.connect('my', '/my', controller='my', action='index') map.connect('login', '/login', controller='login', action='index') diff --git a/debexpo/controllers/index.py b/debexpo/controllers/index.py index bab00c18a650e7c7d831c9e88b2372907784d02c..65cc3c3335d698a8ab668068821c6ab770022901 100644 --- a/debexpo/controllers/index.py +++ b/debexpo/controllers/index.py @@ -38,12 +38,16 @@ __license__ = 'MIT' import logging -from debexpo.lib.base import BaseController, c, config, render +from debexpo.lib.base import BaseController, c, config, render, session +from debexpo.lib import constants from debexpo.controllers.packages import PackagesController, PackageGroups from webhelpers.html import literal from datetime import datetime, timedelta from debexpo.model.package_versions import PackageVersion from debexpo.model.packages import Package +from debexpo.model.users import User + +from debexpo.model import meta log = logging.getLogger(__name__) @@ -89,16 +93,14 @@ class IndexController(BaseController): else: c.custom_html = '' - return render('/index/intro-maintainers.mako') - - - def intro_sponsors(self): - """Return an introduction page for sponsors""" - if 'debexpo.html.sponsors_intro' in config: - f = open(config['debexpo.html.sponsors_intro']) - c.custom_html = literal(f.read()) - f.close() + # The template will need to look at the user details. + if 'user_id' in session: + log.debug('Getting user object for user_id = "%s"' % session['user_id']) + self.user = meta.session.query(User).get(session['user_id']) + c.user = self.user + c.logged_in = True else: - c.custom_html = '' + c.logged_in = False + + return render('/index/intro-maintainers.mako') - return render('/index/intro-sponsors.mako') diff --git a/debexpo/controllers/my.py b/debexpo/controllers/my.py index 2b579d70c97ace0be31bf7bc4285cf39e2180aa2..27b7618965fcee8af6de054883eeedd9bbdcaa1e 100644 --- a/debexpo/controllers/my.py +++ b/debexpo/controllers/my.py @@ -40,12 +40,15 @@ import logging from debexpo.lib.base import * from debexpo.lib import constants, form -from debexpo.lib.schemas import DetailsForm, GpgForm, PasswordForm, OtherDetailsForm +from debexpo.lib.schemas import DetailsForm, GpgForm, PasswordForm, OtherDetailsForm, MetricsForm from debexpo.lib.gnupg import GnuPG from debexpo.model import meta from debexpo.model.users import User from debexpo.model.user_countries import UserCountry +from debexpo.model.sponsor_metrics import SponsorMetrics, SponsorMetricsTags, SponsorTags + +from sqlalchemy.orm import joinedload import debexpo.lib.utils @@ -152,6 +155,45 @@ class MyController(BaseController): redirect(url('my')) + @validate(schema=MetricsForm(), form='index') + def _metrics(self): + """ + Handles a user submitting the metrics form. + """ + log.debug('Metrics form validated successfully') + + if 'user_id' not in session: + log.debug('Requires authentication') + session['path_before_login'] = request.path_info + session.save() + redirect(url('login')) + + sm = SponsorMetrics(user_id=session['user_id']) + sm.contact = int(self.form_result['preferred_contact_method']) + #XXX TODO: WTF?! Find out why on earth package_types is no string + sm.types = str(self.form_result['package_types']) + sm.guidelines_text = self.form_result['packaging_guideline_text'] + sm.social_requirements = self.form_result['social_requirements'] + sm.availability = self.form_result['availability'] + + if self.form_result['packaging_guidelines'] == constants.SPONSOR_GUIDELINES_TYPE_URL: + sm.guidelines = constants.SPONSOR_GUIDELINES_TYPE_URL + elif self.form_result['packaging_guidelines'] == constants.SPONSOR_GUIDELINES_TYPE_TEXT: + sm.guidelines = constants.SPONSOR_GUIDELINES_TYPE_TEXT + else: + sm.guidelines = constants.SPONSOR_GUIDELINES_TYPE_NONE + + for tag in meta.session.query(SponsorTags).all(): + if tag.tag in self.form_result: + log.debug("Weighten tag %s to %s" % (tag.tag, self.form_result[tag.tag])) + metrics = SponsorMetricsTags(tag=tag.tag, user_id=session['user_id'], weight=self.form_result[tag.tag]) + sm.tags.append(metrics) + + meta.session.merge(sm) + meta.session.commit() + + redirect(url('my')) + def index(self, get=False): """ Controller entry point. Displays forms to change user details. @@ -178,7 +220,8 @@ class MyController(BaseController): return { 'details' : self._details, 'gpg' : self._gpg, 'password' : self._password, - 'other_details' : self._other_details + 'other_details' : self._other_details, + 'metrics' : self._metrics, }[request.params['form']]() except KeyError: log.error('Could not find form name; defaulting to main page') @@ -216,8 +259,33 @@ class MyController(BaseController): # Enable the form to show information on the user's GPG key. if self.user.gpg is not None: c.currentgpg = c.user.gpg_id - else: + else: c.currentgpg = None + if self.user.status == constants.USER_STATUS_DEVELOPER: + # Fill in various sponsor metrics + c.constants = constants + c.contact_methods = [ + (constants.SPONSOR_CONTACT_METHOD_NONE, _('None')), + (constants.SPONSOR_CONTACT_METHOD_EMAIL, _('Email')), + (constants.SPONSOR_CONTACT_METHOD_IRC, _('IRC')), + (constants.SPONSOR_CONTACT_METHOD_JABBER, _('Jabber')), + ] + + c.metrics = meta.session.query(SponsorMetrics)\ + .options(joinedload(SponsorMetrics.user))\ + .options(joinedload(SponsorMetrics.tags))\ + .filter_by(user_id = session['user_id'])\ + .first() + c.technical_tags = meta.session.query(SponsorTags).filter_by(tag_type=constants.SPONSOR_METRICS_TYPE_TECHNICAL).all() + c.social_tags = meta.session.query(SponsorTags).filter_by(tag_type=constants.SPONSOR_METRICS_TYPE_SOCIAL).all() + if not c.metrics: + # Set some sane defaults + log.debug("Generating new defaults for sponsor metrics") + c.metrics = SponsorMetrics() + c.metrics.availability = constants.SPONSOR_METRICS_PRIVATE + c.metrics.guidelines = constants.SPONSOR_GUIDELINES_TYPE_NONE + + log.debug('Rendering page') return render('/my/index.mako') diff --git a/debexpo/controllers/sponsor.py b/debexpo/controllers/sponsor.py new file mode 100644 index 0000000000000000000000000000000000000000..5ec3ae04de296946c8ba9d1d8f98bad6785f0a70 --- /dev/null +++ b/debexpo/controllers/sponsor.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +# +# index.py — index controller +# +# This file is part of debexpo - http://debexpo.workaround.org +# +# Copyright © 2011 Arno Töll +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +""" +Holds the IndexController. +""" + +__author__ = 'Arno Töll' +__copyright__ = 'Copyright © 2011 Arno Töll' +__license__ = 'MIT' + +import logging +import random +import struct +import socket + +from debexpo.lib.base import BaseController, c, config, render, session, \ + redirect, url, abort, request +from debexpo.lib import constants +from debexpo.model.sponsor_metrics import SponsorMetrics, SponsorTags, SponsorMetricsTags +from debexpo.model.users import User + +from sqlalchemy.orm import joinedload, contains_eager + +from debexpo.model import meta + +log = logging.getLogger(__name__) + +class SponsorController(BaseController): + + def _validate_tags(self, tags, existing_tags = None): + """ + Validates a list of tags with actual existing ones + + ```tags``` + A list of tags which need to be verified + + ```existing_tags```` + The list of existing tags. Might be None to let this method fetch the tag list + + """ + + if not existing_tags: + existing_tags = [tag.tag for tag in meta.session.query(SponsorTags).all()] + else: + existing_tags = [tag.tag for tag in existing_tags] + + for tag in tags: + if not tag in existing_tags: + return False + return True + + def clear(self): + """ + Clear applied filters in the session. + This method can be safely called, even if no filter was registered + """ + + if 'sponsor_filters' in session: + log.debug("Clear sponsor filter") + del(session['sponsor_filters']) + session.save() + + redirect(url('sponsors')) + + + def save(self): + """ + Toggle a filter within the session. + This method prepares a list of filters to limit results in the sponsor list + + ```tag``` the sponsor tag to be filtered. If the tag is already in the filter + list remove it, add it otherwise. + """ + + tags = request.params.getall('t') + if not self._validate_tags(tags): + abort(404) + + if 'sponsor_filters' not in session: + session['sponsor_filters'] = [] + + session['sponsor_filters'] = tags + session.save() + + redirect(url('sponsors')) + + def index(self): + """ + Return an introduction page for sponsors + This page honors filters configured in the user session + """ + + if not config['debexpo.enable_experimental_code'].lower() == 'true': + return render('/sponsor/index-old.mako') + + if 'debexpo.html.sponsors_intro' in config: + f = open(config['debexpo.html.sponsors_intro']) + c.custom_html = literal(f.read()) + f.close() + else: + c.custom_html = '' + c.constants = constants + + c.sponsors = meta.session.query(SponsorMetrics)\ + .options(joinedload(SponsorMetrics.user))\ + .options(joinedload(SponsorMetrics.tags))\ + .filter(SponsorMetrics.availability >= constants.SPONSOR_METRICS_RESTRICTED)\ + .all() + + def hash_ip(): + """ + This is a simple hashing algorithm not supposed to be called from anywhere + but for internal use only. + It reads the client IP address and returns a float between 0.01 and 0.91 which is + used as input for random.shuffle + """ + ip = request.environ['REMOTE_ADDR'] + try: + ip = struct.unpack( "!L", socket.inet_pton( socket.AF_INET, ip )) + ip = ip[0] + except socket.error: + ip = struct.unpack( "!QQ", socket.inet_pton( socket.AF_INET6, ip )) + ip = ip[1] + ip = (float(ip % 10) + 0.01) / 10 + return ip + + random.shuffle(c.sponsors, hash_ip) + + # The select above works fine, except that it sucks. + # It suffers from a poor performance and it could be significantly improved by querying the tag + # labels and descriptions (i.e. the SponsorTags table by joining them with SponsorMetricsTags. + # However the resulting result set does not quite look like what I imagine. Feel free to replace it + # by something which actually works. + + #c.sponsors = meta.session.query(SponsorMetrics, SponsorMetricsTags, SponsorTags, User)\ + # .join(User)\ + # .join(SponsorMetricsTags)\ + # .join(SponsorTags)\ + # .filter(SponsorMetrics.availability >= constants.SPONSOR_METRICS_RESTRICTED)\ + + + if 'sponsor_filters' in session: + log.debug("Applying tag filter") + c.sponsor_filter = session['sponsor_filters'] + else: + c.sponsor_filter = [] + if request.params.getall('t'): + c.sponsor_filter = request.params.getall('t') + + c.technical_tags = meta.session.query(SponsorTags).filter_by(tag_type=constants.SPONSOR_METRICS_TYPE_TECHNICAL).all() + c.social_tags = meta.session.query(SponsorTags).filter_by(tag_type=constants.SPONSOR_METRICS_TYPE_SOCIAL).all() + + if not self._validate_tags(c.sponsor_filter, c.technical_tags + c.social_tags): + abort(404) + + return render('/sponsor/index.mako') diff --git a/debexpo/controllers/upload.py b/debexpo/controllers/upload.py index 4561a5627fd0e6833b4742908ea525844316e49d..72bab7471849644bee8e4320a4e20515a04177ff 100644 --- a/debexpo/controllers/upload.py +++ b/debexpo/controllers/upload.py @@ -39,7 +39,6 @@ __license__ = 'MIT' import os import logging import subprocess -import md5 import base64 try: @@ -48,7 +47,7 @@ except ImportError: # for sqlalchemy 0.7.1 and above from sqlalchemy.exc import InvalidRequestError from debexpo.lib.base import * -from debexpo.lib.utils import allowed_upload +from debexpo.lib.filesystem import CheckFiles from debexpo.model import meta from debexpo.model.user_upload_key import UserUploadKey @@ -105,7 +104,7 @@ class UploadController(BaseController): # Check whether the file extension is supported by debexpo - if not allowed_upload(filename): + if not CheckFiles().allowed_upload(filename): log.error('File type not supported: %s' % filename) abort(403, 'The uploaded file type is not supported') diff --git a/debexpo/lib/constants.py b/debexpo/lib/constants.py index 03134ab08a91d912ac0e23cf929d4bf8a2f6e513..987b33793934ea18e5748a531d7260d18b848674 100644 --- a/debexpo/lib/constants.py +++ b/debexpo/lib/constants.py @@ -35,6 +35,12 @@ __author__ = 'Jonny Lamb' __copyright__ = 'Copyright © 2008 Jonny Lamb' __license__ = 'MIT' + +# Importer related constants +ORIG_TARBALL_LOCATION_NOT_FOUND = 0 +ORIG_TARBALL_LOCATION_LOCAL = 1 +ORIG_TARBALL_LOCATION_REPOSITORY = 2 + # User constants USER_TYPE_NORMAL = 1 USER_TYPE_ADMIN = 2 @@ -76,3 +82,21 @@ PACKAGE_COMMENT_STATUS_UPLOADED = 2 # Package subscriptions SUBSCRIPTION_LEVEL_UPLOADS = 1 SUBSCRIPTION_LEVEL_COMMENTS = 2 + +#Sponsor metrics +SPONSOR_METRICS_PRIVATE = 0 +SPONSOR_METRICS_RESTRICTED = 1 +SPONSOR_METRICS_PUBLIC = 2 + +SPONSOR_METRICS_TYPE_TECHNICAL = 1 +SPONSOR_METRICS_TYPE_SOCIAL = 2 + +SPONSOR_CONTACT_METHOD_NONE = 0 +SPONSOR_CONTACT_METHOD_EMAIL = 1 +SPONSOR_CONTACT_METHOD_IRC = 2 +SPONSOR_CONTACT_METHOD_JABBER = 3 + +SPONSOR_GUIDELINES_TYPE_NONE = 0 +SPONSOR_GUIDELINES_TYPE_URL = 1 +SPONSOR_GUIDELINES_TYPE_TEXT = 2 + diff --git a/debexpo/lib/filesystem.py b/debexpo/lib/filesystem.py index 332ed91f90225fcea9aeaaeaa0592da8ee1cda9f..81b040237373f8190cddbfef9ad7ab0c132a5770 100644 --- a/debexpo/lib/filesystem.py +++ b/debexpo/lib/filesystem.py @@ -106,8 +106,8 @@ class CheckFiles(object): def find_orig_tarball(self, changes_file): """ Look to see whether there is an orig tarball present, if the dsc refers to one. - If it is present or not necessary, this returns True. Otherwise, it returns the - name of the file required. + This method returns a triple (filename, file_found, location_hint), returning the (expected) + name of the original tarball, and whether it was found in the local repository ```changes_file``` The changes file to parse for the orig.tar (note the dsc file referenced must exist) @@ -119,12 +119,22 @@ class CheckFiles(object): if (file['name'].endswith('orig.tar.gz') or file['name'].endswith('orig.tar.bz2') or file['name'].endswith('orig.tar.xz')): - if os.path.isfile(file['name']): - return (file['name'], True) - else: - return (file['name'], False) - - return (None, False) + full_filename = os.path.join(pylons.config['debexpo.repository'], changes_file.get_pool_path(), file['name']) + # tar.gz was found in the local directory + if os.path.isfile(file['name']): + sum = md5sum(file['name']) + if sum == file['md5sum']: + return (file['name'], constants.ORIG_TARBALL_LOCATION_LOCAL) + # tar.gz was found, but does not seem to be the same file + break + # tar.gz was found in the repository + elif os.path.isfile(full_filename): + return (file['name'], constants.ORIG_TARBALL_LOCATION_REPOSITORY) + # tar.gz was expected but not found at all + else: + return (file['name'], constants.ORIG_TARBALL_LOCATION_NOT_FOUND) + + return (None, constants.ORIG_TARBALL_LOCATION_NOT_FOUND) def find_files_for_package(self, package, absolute_path=False): @@ -161,6 +171,26 @@ class CheckFiles(object): if os.path.exists(file): log.debug("Removing file '%s'" % (file)) os.unlink(file) - if os.path.isdir(path): + if os.path.isdir(path) and os.listdir(path) == []: log.debug("Remove empty package repository '%s'" % (path)) os.rmdir(path) + + + + + + def allowed_upload(self, filename): + """ + Looks at a filename's extension and decides whether to accept it. + We only want package files to be uploaded, after all. + It returns a boolean of whether to accept the file or not. + + ``filename`` + File to test. + """ + for suffix in ['.changes', '.dsc', '.tar.gz', '.diff.gz', '.deb', '.udeb', '.tar.bz2', ".tar.xz"]: + if filename.endswith(suffix): + return True + + return False + diff --git a/debexpo/lib/filters.py b/debexpo/lib/filters.py new file mode 100644 index 0000000000000000000000000000000000000000..1b72899a1cb4688a30220038b00af0082f5c4cfe --- /dev/null +++ b/debexpo/lib/filters.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# filter.py — Output filters for the template engine +# +# This file is part of debexpo - http://debexpo.workaround.org +# +# Copyright © 2011 Arno Töll +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +""" +Holds various output filters which can be applied to templates +""" + +__author__ = 'Arno Töll' +__copyright__ = 'Copyright © 2011 Arno Töll' +__license__ = 'MIT' + +import cgi + +def semitrusted(input_filter): + """ + This filter filters all input, but keeps formatting, e.g. newlines + + ``input_filter`` The data to be filtered + """ + escaped_filter = cgi.escape(input_filter, True) + escaped_filter = escaped_filter.replace('\n', '
') + return escaped_filter diff --git a/debexpo/lib/plugins.py b/debexpo/lib/plugins.py index b4251e172c47a0471ddc2259be961bade00198eb..29eab59d1904292512e397da8ee3f6c2a537b323 100644 --- a/debexpo/lib/plugins.py +++ b/debexpo/lib/plugins.py @@ -141,8 +141,11 @@ class Plugins(object): for item in dsc['Files']: if item['name'] not in self.changes.get_files(): src_file = os.path.join(self.config['debexpo.upload.incoming'], item['name']) + repository_src_file = os.path.join(self.config['debexpo.repository'], self.changes.get_pool_path(), item['name']) if os.path.exists(src_file): shutil.copy(src_file, self.tempdir) + elif os.path.exists(repository_src_file): + shutil.copy(repository_src_file, self.tempdir) else: log.critical("Trying to copy non-existing file %s" % (src_file)) diff --git a/debexpo/lib/schemas.py b/debexpo/lib/schemas.py index 0958711004d396a74af799ad093064eb3c442c8b..fc71e79470673fa611231987b8c86c8bbf6b778d 100644 --- a/debexpo/lib/schemas.py +++ b/debexpo/lib/schemas.py @@ -39,10 +39,14 @@ __license__ = 'MIT' import formencode from pylons import config +from debexpo.lib.base import meta from debexpo.lib import constants from debexpo.lib.validators import NewEmailToSystem, GpgKey, \ - CurrentPassword, CheckBox, NewNameToSystem, ValidateSponsorEmail + CurrentPassword, CheckBox, NewNameToSystem, ValidateSponsorEmail, \ + ValidatePackagingGuidelines, DummyValidator +from debexpo.model.sponsor_metrics import SponsorTags + class LoginForm(formencode.Schema): """ @@ -97,6 +101,44 @@ class OtherDetailsForm(MyForm): jabber = formencode.validators.String() status = CheckBox() +class MetricsForm(MyForm): + """ + Schema for updating the metrics in the controller + """ + + def __init__(self, *args, **kwargs): + for tag in meta.session.query(SponsorTags).all(): + kwargs[tag.tag] = formencode.validators.Number(min=-10, max=10, not_empty=True) + MyForm.__init__(self, *args, **kwargs) + + preferred_contact_method = formencode.compound.All( + formencode.validators.OneOf([ + constants.SPONSOR_CONTACT_METHOD_NONE, + constants.SPONSOR_CONTACT_METHOD_EMAIL, + constants.SPONSOR_CONTACT_METHOD_IRC, + constants.SPONSOR_CONTACT_METHOD_JABBER + ]), formencode.validators.Int(not_empty=True)) + package_types = formencode.validators.String() + packaging_guidelines = formencode.compound.All( + formencode.validators.OneOf([ + constants.SPONSOR_GUIDELINES_TYPE_NONE, + constants.SPONSOR_GUIDELINES_TYPE_URL, + constants.SPONSOR_GUIDELINES_TYPE_TEXT]), formencode.validators.Int(not_empty=True)) + + availability = formencode.compound.All( + formencode.validators.OneOf([ + constants.SPONSOR_METRICS_PRIVATE, + constants.SPONSOR_METRICS_RESTRICTED, + constants.SPONSOR_METRICS_PUBLIC]), formencode.validators.Int(not_empty=True)) + social_requirements = formencode.validators.String() + + # Postpone validation of packaging_guideline_text, as its validation + # depends on the value of packaging_guidelines + packaging_guideline_text = DummyValidator + chained_validators = [ + formencode.schema.SimpleFormValidator(ValidatePackagingGuidelines) + ] + class RegisterForm(formencode.Schema): """ Schema for the general fields in the register controller. The maintainer diff --git a/debexpo/lib/utils.py b/debexpo/lib/utils.py index ee1ad4eaf2ab6ede125aa8598e2503534128364a..1d8757a8694d820846e857961ebaf48d98444320 100644 --- a/debexpo/lib/utils.py +++ b/debexpo/lib/utils.py @@ -37,28 +37,12 @@ __license__ = 'MIT' import logging import hashlib -import md5 import os from pylons import config log = logging.getLogger(__name__) -def allowed_upload(filename): - """ - Looks at a filename's extension and decides whether to accept it. - We only want package files to be uploaded, after all. - It returns a boolean of whether to accept the file or not. - - ``filename`` - File to test. - """ - for suffix in ['.changes', '.dsc', '.tar.gz', '.diff.gz', '.deb', '.udeb', '.tar.bz2', ".tar.xz"]: - if filename.endswith(suffix): - return True - - return False - def parse_section(section): """ Works out the component and section from the "Section" field. @@ -101,7 +85,7 @@ def md5sum(filename): except: raise AttributeError('Failed to open file %s.' % filename) - sum = md5.new() + sum = hashlib.md5() while True: chunk = f.read(10240) if not chunk: diff --git a/debexpo/lib/validators.py b/debexpo/lib/validators.py index b6d470aeacc29fa45df4ee1247993e7fe3664830..3d4b5f1571ffd1dc4aba4df76ff4da0cab67d395 100644 --- a/debexpo/lib/validators.py +++ b/debexpo/lib/validators.py @@ -43,6 +43,7 @@ from debexpo.lib.gnupg import GnuPG from debexpo.model import meta from debexpo.model.users import User +from debexpo.lib import constants import debexpo.lib.utils @@ -173,3 +174,17 @@ def ValidateSponsorEmail(values, state, validator): if values['sponsor'] == '1' and not values['email'].endswith('@debian.org'): return {'sponsor': 'A sponsor account must be registered with your @debian.org address' } +class DummyValidator(formencode.FancyValidator): + pass + +def ValidatePackagingGuidelines(values, state, validator): + try: + if values['packaging_guidelines'] == constants.SPONSOR_GUIDELINES_TYPE_TEXT: + formencode.validators.String(min=1).to_python(values['packaging_guideline_text']) + elif values['packaging_guidelines'] == constants.SPONSOR_GUIDELINES_TYPE_URL: + formencode.validators.URL(add_http=True).to_python(values['packaging_guideline_text']) + else: + formencode.validators.Empty().to_python(values['packaging_guideline_text']) + except Exception as e: + return {'packaging_guideline_text': e} + return None diff --git a/debexpo/model/__init__.py b/debexpo/model/__init__.py index 61ec34dea8f3e3efe05a3460d590a5c560d7f889..7619c077ae65c1a198b8a01e5999521de1663ff3 100644 --- a/debexpo/model/__init__.py +++ b/debexpo/model/__init__.py @@ -61,7 +61,7 @@ def import_all_models(): from debexpo.model import binary_packages, package_files, packages, source_packages, \ user_metrics, package_comments, package_info, package_versions, user_countries, \ - users, package_subscriptions, user_upload_key, password_reset + users, package_subscriptions, user_upload_key, password_reset, sponsor_metrics class OrmObject(object): """ @@ -85,9 +85,8 @@ class OrmObject(object): 'not column in mapped table: %s' % (key,)) def __repr__(self): - atts = [] - for key in dir(self): - if not key.startswith('_') and key is not "foreign" and key not in self.foreign: - atts.append((key, getattr(self, key))) - - return self.__class__.__name__ + '(' + ', '.join(x[0] + '=' + repr(x[1]) for x in atts) + ')' + attrs = [] + for key in self.__dict__: + if not key.startswith('_'): + attrs.append((key, getattr(self, key))) + return self.__class__.__name__ + '(' + ', '.join(x[0] + '=' + repr(x[1]) for x in attrs) + ')' diff --git a/debexpo/model/data/__init__.py b/debexpo/model/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/debexpo/model/data/tags.py b/debexpo/model/data/tags.py new file mode 100644 index 0000000000000000000000000000000000000000..73c867a2c7fe6b637f3cb97ffad5be22a81f47ed --- /dev/null +++ b/debexpo/model/data/tags.py @@ -0,0 +1,36 @@ +import debexpo.lib.constants + +TAGS = { + debexpo.lib.constants.SPONSOR_METRICS_TYPE_TECHNICAL: [ + ('CDBS','cdbs', 'Your package makes use of the The Common Debian Build System'), + ('(Plain) debhelper','debhelper', 'Your package makes use of the debhelper build system'), + ('(Short) dh-style debhelper', 'dh', 'Your package makes use of short dh(1) style build system'), + ('No build helper / home brewed debian/rules','yada', 'Your package is using a completely customized, yet policy compliant debian/rules file, which does not make use of either debhelper or CDBS.'), + ('NMUs','nmu', 'Your package is a NMU'), + ('QA uploads','qa', 'Your package is a QA upload'), + ('Backports','backports', 'Your package is a backported package'), + ('Modified tarballs (but good reasons)','modified-tarballs', 'Your package modified the original source tarball somehow. It does not match the original checksum anymore but you have a good reason to do so'), + ('Library package', 'library-package', 'You are packaging a policy compliant library'), + ('VCS snapshot tarballs','vcs-tarball', 'Your package is not based on a original source tarball at all but is based on a VCS snapshot',), + ('contrib/non-free packages', 'non-free-package', 'Your package it targetting the contrib or non-free branches (Information)'), + ('1.0 format packages', '1.0-format', 'Your package is using the 1.0 format (the traditional source format that is)'), + ('3.0 format packages', '3.0-format', 'Your package is using the 3.0/quilt format'), + ('Embedded code copies', 'embedded-code-copies', 'Your package makes use of embedded code copies in a reasonable way'), + ('DEP-5 copyright', 'dep5', 'Your package does make use of DEP-5 copyright files'), + ('No Lintian cleanliness (but good reasons)', 'not-lintian-clean', 'Your package is not Lintian clean down to the informational level but you have a good reason why not'), + ], + + debexpo.lib.constants.SPONSOR_METRICS_TYPE_SOCIAL: [ + ('Prospective DM/DD', 'prospective-dm-dd', 'You are willing to become a Debian Maintainer/Debian Developer some day'), + ('(Willing to be) DM', 'applicant-is-dm', 'You are a Debian Maintainer already, or you plan to become one soon'), + ('(Willing to enter) NM', 'applicant-is-nm', 'You are in the New Maintainer process to become a developer or you plan to apply soon'), + ('Signed GPG key', 'signed-gpg-key', 'Your GPG matches the guidelines of the Debian keyring maintainers and/or is signed by any Debian developer'), + ('One time uploads', 'no-one-time-upload', 'Your package is a single shot upload'), + ('Sharing a time zone', 'sharing-time-zone', 'You share a time zone with your sponsors. This can be useful to get together more easily'), + ('Possibility to meet-up', 'possibility-to-meetup', 'You are living close to your sponsor and you are willing to meet him eventually'), + ('Having already packages in Debian', 'maintainer-already', 'You do already have one or more packages in Debian'), + ('Maintainer is not upstream', 'maintainer-is-not-upstream', 'You are not upstream of the package you are proposing to Debian'), + ('Notable user base', 'notable-user-base', 'You can show us that people are already using your program and consider it useful'), + ('Minimizing differences to derivatives', 'package-in-a-derivative', 'Your package feeds changes introduced by any derivative back to Debian to minimize differences'), + ] +} diff --git a/debexpo/model/packages.py b/debexpo/model/packages.py index 997b5753dd25b5419ff1d382d67fca55c48d7caf..ebd9416c0020c2b687807acc37d53a98daffbc1a 100644 --- a/debexpo/model/packages.py +++ b/debexpo/model/packages.py @@ -55,11 +55,8 @@ t_packages = sa.Table('packages', meta.metadata, class Package(OrmObject): foreign = ['user'] - def get_description_nl2br(self): - s = self.description - s = s.replace('<', '<') - s = s.replace('\n', '
') - return s + def get_description(self): + return self.description orm.mapper(Package, t_packages, properties={ 'user' : orm.relation(User, backref='packages') diff --git a/debexpo/model/sponsor_metrics.py b/debexpo/model/sponsor_metrics.py new file mode 100644 index 0000000000000000000000000000000000000000..dadeb6eb25f999a7f2e1ec53af04e4d7cbbf81d6 --- /dev/null +++ b/debexpo/model/sponsor_metrics.py @@ -0,0 +1,178 @@ +# -*- coding: utf-8 -*- +# +# sponsor_metrics.py — rudimentary model for sponsor metrics +# +# This file is part of debexpo - http://debexpo.workaround.org +# +# Copyright © 2011 Arno Töll +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +""" +Holds Sponsor Metrics Model +""" + +__author__ = 'Arno Töll' +__copyright__ = 'Copyright © 2011 Arno Töll' +__license__ = 'MIT' + +import os +import datetime + +import sqlalchemy as sa +from sqlalchemy import orm +from sqlalchemy.ext.associationproxy import association_proxy + +from debexpo.model import meta, OrmObject +from debexpo.model.users import User +from debexpo.lib import constants +import debexpo.lib.utils + +t_sponsor_metrics = sa.Table( + 'sponsor_metrics', meta.metadata, + sa.Column('user_id', sa.types.Integer, sa.ForeignKey('users.id'), primary_key=True), + sa.Column('availability', sa.types.Integer, nullable=False), + sa.Column('contact', sa.types.Integer, nullable=False), + sa.Column('types', sa.types.Text, nullable=True), + sa.Column('guidelines', sa.types.Integer, nullable=True), + sa.Column('guidelines_text', sa.types.Text, nullable=True), + sa.Column('social_requirements', sa.types.Text, nullable=True), + ) + +t_sponsor_tags = sa.Table( + 'sponsor_tags', meta.metadata, + sa.Column('tag_type', sa.types.Integer, nullable=False), + sa.Column('tag', sa.types.Text, primary_key=True), + sa.Column('label', sa.types.Text, nullable=False), + sa.Column('long_description', sa.types.Text, nullable=False), +) + +t_sponsor_metrics_tags = sa.Table( + 'sponsor_metrics_tags', meta.metadata, + sa.Column('tag', sa.Integer, sa.ForeignKey('sponsor_tags.tag'), primary_key=True), + sa.Column('user_id', sa.Integer, sa.ForeignKey('sponsor_metrics.user_id'), primary_key=True), + sa.Column('weight', sa.Integer), +) + +class SponsorMetrics(OrmObject): + foreign = ['user'] + + def get_all_tags_weighted(self, weight = 0): + if weight > 0: + return [x.tag for x in self.tags if x.weight > 0] + elif weight < 0: + return [x.tag for x in self.tags if x.weight < 0] + else: + return [x.tag for x in self.tags] + + def get_all_tags(self): + return get_all_tags_weighted(0) + + def get_technical_tags(self): + return [x.tag for x in self.get_technical_tags_full()] + + def get_social_tags(self): + return [x.tag for x in self.get_social_tags_full()] + + def get_technical_tags_full(self): + return [x for x in self.tags if x.full_tag.tag_type == constants.SPONSOR_METRICS_TYPE_TECHNICAL] + + def get_social_tags_full(self): + return [x for x in self.tags if x.full_tag.tag_type == constants.SPONSOR_METRICS_TYPE_SOCIAL] + + def get_tag_weight(self, tag): + for t in self.tags: + if t.tag == tag: + return t.weight + return 0.0 + + def get_guidelines(self): + """ + Return a formatted and sanitized string of the guidelines the sponsor + configured + """ + return self.guidelines_text + + def get_types(self): + """ + Return a formatted and sanitized string of the packages the sponsor + is interested in + """ + + if self.types: + return self.types + return "" + + def get_social_requirements(self): + """ + Return a formatted and sanitized string of the social requirements the sponsor + configured + """ + + if self.social_requirements: + return self.social_requirements + else: + return "" + + def allowed(self, flag): + """ + Returns true if the user associated with this object allowed to display a + contact address. This method also honors the SPONSOR_METRICS_RESTRICTED flag + + ```flag``` A SPONSOR_CONTACT_METHOD_* flag which should be checked + """ + if self.availability == constants.SPONSOR_METRICS_RESTRICTED and self.contact == flag: + return True + elif self.availability == constants.SPONSOR_METRICS_PUBLIC: + return True + return False + + +class SponsorTags(OrmObject): + pass + #keywords = association_proxy('metrics', 'metric') + +class SponsorMetricsTags(OrmObject): + foreign = ['user', 'tags'] + +orm.mapper(SponsorMetrics, t_sponsor_metrics, properties={ + 'tags' : orm.relationship(SponsorMetricsTags), + 'user' : orm.relationship(User), +}) + + +orm.mapper(SponsorTags, t_sponsor_tags) + +orm.mapper(SponsorMetricsTags, t_sponsor_metrics_tags, properties={ + 'full_tag': orm.relationship(SponsorTags) +}) + + +def create_tags(): + import debexpo.model.data.tags + import logging + for metrics_type in [constants.SPONSOR_METRICS_TYPE_TECHNICAL, constants.SPONSOR_METRICS_TYPE_SOCIAL]: + for (description, tag, long_description) in debexpo.model.data.tags.TAGS[metrics_type]: + st = SponsorTags(tag_type=metrics_type, tag=tag, label=description, long_description=long_description) + logging.info("Adding tag %s" % (tag)) + meta.session.merge(st) + meta.session.commit() diff --git a/debexpo/plugins/getorigtarball.py b/debexpo/plugins/getorigtarball.py index 2b1d30fa418a472ae1648ea02fd50c5b96029f80..0513631b80dde72ef7c40cf1eba9470e2b1bbf55 100644 --- a/debexpo/plugins/getorigtarball.py +++ b/debexpo/plugins/getorigtarball.py @@ -6,6 +6,7 @@ # # Copyright © 2008 Jonny Lamb # Copyright © 2010 Jan Dittberner +# Copyright © 2011 Arno Töll # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation @@ -33,7 +34,7 @@ Holds the getorigtarball plugin. """ __author__ = 'Jonny Lamb' -__copyright__ = 'Copyright © 2008 Jonny Lamb, Copyright © 2010 Jan Dittberner' +__copyright__ = 'Copyright © 2008 Jonny Lamb, Copyright © 2010 Jan Dittberner, Copyright © 2011 Arno Töll' __license__ = 'MIT' from debian import deb822 @@ -42,7 +43,9 @@ import os import urllib import re +from debexpo.lib import constants from debexpo.lib.utils import md5sum +from debexpo.lib.filesystem import CheckFiles from debexpo.plugins import BasePlugin import pylons @@ -67,27 +70,27 @@ class GetOrigTarballPlugin(BasePlugin): size = 15728640 log.debug('Checking whether an orig tarball mentioned in the dsc is missing') dsc = deb822.Dsc(file(self.changes.get_dsc())) + filecheck = CheckFiles() + + if filecheck.is_native_package(self.changes): + log.debug('No orig.tar.gz file found; native package?') + return + + # An orig.tar.gz was found in the dsc, and also in the upload. + (orig, orig_file_found) = filecheck.find_orig_tarball(self.changes) + if orig_file_found > constants.ORIG_TARBALL_LOCATION_NOT_FOUND: + log.debug('%s found successfully', orig) + return - orig = None for dscfile in dsc['Files']: dscfile['size'] = int(dscfile['size']) - if re.search('(orig\.tar\.(gz|bz2|xz))$', dscfile['name']): - orig = dscfile + if orig == dscfile['name']: if dscfile['size'] > size: log.warning("Skipping eventual download of orig.tar.gz %s: size %d > %d" % (dscfile['name'], dscfile['size'], size)) return + orig = dscfile break - # There is no orig.tar.gz file in the dsc file. This is probably a native package. - if orig is None: - log.debug('No orig.tar.gz file found; native package?') - return - - # An orig.tar.gz was found in the dsc, and also in the upload. - if os.path.isfile(orig['name']): - log.debug('%s found successfully', orig['name']) - return - log.debug('Could not find %s; looking in Debian for it', orig['name']) url = os.path.join(pylons.config['debexpo.debian_mirror'], self.changes.get_pool_path(), orig['name']) diff --git a/debexpo/scripts/debexpo_importer.py b/debexpo/scripts/debexpo_importer.py index d24b7be0dec418ebd298949965864e64eee0d7d9..7c9860dbff748c19ff6e001e87939abc15be651a 100755 --- a/debexpo/scripts/debexpo_importer.py +++ b/debexpo/scripts/debexpo_importer.py @@ -1,3 +1,4 @@ +#! /usr/bin/env python # -*- coding: utf-8 -*- # # debexpo-importer — executable script to import new packages @@ -382,22 +383,26 @@ class Importer(object): self.files = self.changes.get_files() distribution = self.changes['Distribution'].lower() - allowed_distributions = ('unstable', 'stable-backports', 'oldstable-backports', 'stable-backports-sloppy', 'oldstable-backports') + allowed_distributions = ('oldstable', 'stable', 'unstable', 'experimental', 'stable-backports', 'oldstable-backports', + 'oldstable-backports-sloppy', 'stable-security', 'testing-security', 'stable-proposed-updates', + 'testing-proposed-updates', 'sid', 'wheezy', 'squeeze', 'lenny', 'squeeze-backports', 'lenny-backports', + 'lenny-security', 'lenny-backports-sloppy', 'lenny-volatile', 'squeeze-security', 'squeeze-updates', 'wheezy-security') if distribution not in allowed_distributions: self._remove_changes() self._reject("You are not uploading to one of those Debian distributions: %s" % - (reduce(lambda x,xs: x + xs + " ", allowed_distributions))) + (reduce(lambda x,xs: x + " " + xs, allowed_distributions))) # Look whether the orig tarball is present, and if not, try and get it from # the repository. (orig, orig_file_found) = filecheck.find_orig_tarball(self.changes) - if orig and not orig_file_found: + if orig and not orig_file_found == constants.ORIG_TARBALL_LOCATION_NOT_FOUND: log.debug("Upload does not contain orig.tar.gz - trying to find it elsewhere") filename = os.path.join(pylons.config['debexpo.repository'], self.changes.get_pool_path(), orig) if os.path.isfile(filename): + log.debug("Found tar.gz in repository as %s" % (filename)) shutil.copy(filename, pylons.config['debexpo.upload.incoming']) - self.files.append(orig) + #self.files.append(orig) destdir = pylons.config['debexpo.repository'] @@ -427,11 +432,14 @@ class Importer(object): pool_dir = os.path.join(destdir, self.changes.get_pool_path()) log.debug("Pool directory: %s", pool_dir) for file in self.files: - if os.path.isfile(os.path.join(pool_dir, file)): + if os.path.isfile(file) and os.path.isfile(os.path.join(pool_dir, file)): log.warning('%s is being installed even though it already exists' % file) - else: + toinstall.append(file) + elif os.path.isfile(file): log.debug('File %s is safe to install' % os.path.join(pool_dir, file)) - toinstall.append(file) + toinstall.append(file) + # skip another corner case, where the dsc contains a orig.tar.gz but wasn't uploaded + # by doing nothing here for that case # Run post-upload plugins. post_upload = Plugins('post-upload', self.changes, self.changes_file, @@ -444,16 +452,21 @@ class Importer(object): # Check whether a post-upload plugin has got the orig tarball from somewhere. if not orig_file_found and not filecheck.is_native_package(self.changes): (orig, orig_file_found) = filecheck.find_orig_tarball(self.changes) - if not orig_file_found: + if orig_file_found == constants.ORIG_TARBALL_LOCATION_NOT_FOUND: # When coming here it means: # a) The uploader did not include a orig.tar.gz in his upload # b) We couldn't find a orig.tar.gz in our repository # c) No plugin could get the orig.tar.gz # ... time to give up self._remove_changes() - self._reject("Rejecting incomplate upload. " - "You did not upload %s and we didn't find it on either one of our backup resources" % + if orig == None: + orig = "any original tarball (orig.tar.gz)" + self._reject("Rejecting incomplete upload. " + "You did not upload %s and we didn't find it on any of our alternative resources.\n" \ + "If you tried to upload a package which only increased the Debian revision part, make sure you include the full source (pass -sa to dpkg-buildpackage)" % ( orig )) + else: + toinstall.append(orig) # Check whether the debexpo.repository variable is set if 'debexpo.repository' not in pylons.config: diff --git a/debexpo/templates/base.mako b/debexpo/templates/base.mako index 7216ecc643f59cd6a15aa6a61ba1a35034f2aa7b..b8de44a2f2b2debcf41048f78cc44115ce2f5b1f 100644 --- a/debexpo/templates/base.mako +++ b/debexpo/templates/base.mako @@ -40,13 +40,13 @@
  • ${ h.tags.link_to( - _('Sponsors'), - h.url('intro-sponsors')) } + _('Reviews'), + h.url('intro-reviewers')) }
  • ${ h.tags.link_to( - _('Reviews'), - h.url('intro-reviewers')) } + _('Sponsors'), + h.url('sponsors')) }
  • ${ h.tags.link_to( @@ -95,13 +95,15 @@ diff --git a/debexpo/templates/index/index.mako b/debexpo/templates/index/index.mako index 6e018428242a1a51061007931189b7e74774bd10..436b217a5018ca3de804f8144140147adfa92517 100644 --- a/debexpo/templates/index/index.mako +++ b/debexpo/templates/index/index.mako @@ -12,17 +12,15 @@ ${ c.custom_html } --> -

    Only approved members of the Debian project (Debian Developers) are granted the permission to upload software packages into the Debian distribution. Still a large number of packages is maintained by non-official developers. How do they get their work into Debian when they are not allowed to upload their own packages directly? By means of a process called sponsorship. Don't worry - it does not deal with money. Sponsorship means that a Debian Developer uploads the package on behalf of the actual maintainer. The Debian Developer will also check the package for technical correctness and help the maintainer to improve the package if necessary. Therefore the sponsor is sometimes also called a mentor.

    +

    Only approved members of the Debian project (Debian Developers) are granted the permission to upload software packages into the Debian distribution. Still a large number of packages is maintained by non-official developers. How do they get their work into Debian when they are not allowed to upload their own packages directly? By means of a process called sponsorship. Sponsorship means that a Debian Developer uploads the package on behalf of the actual maintainer. The Debian Developer will also check the package for technical correctness and help the maintainer to improve the package if necessary. Therefore the sponsor is sometimes also called a mentor.

    -

    You can even help us to review packages if you are not a developer. We appreciate your efforts as well.

    +

    Note, not only Debian Developer are allowed to review packages. Everyone is encouraged to review packages! We appreciate your efforts as well.

    -
      -
    • I want to have my package uploaded to Debian: Please go to ${ h.tags.link_to("our introductory page", h.url('intro-maintainers')) } for maintainers and learn how to use ${ c.config['debexpo.sitename'] }.
    • -
    • I am a Debian Developer and want to offer sponsorship: Please go to ${ h.tags.link_to("our introductory page for sponsors", h.url('intro-sponsors')) } to learn how you can help best.
    • -
    • I am a Debian Maintainer or a skilled sponsored maintainer and want to help: Please go to ${ h.tags.link_to("our page dedicated to reviewers", h.url('intro-reviewers')) }.
    • -
    +

    Getting your package into Debian

    -

    You can read more about debexpo on the Debian Wiki.

    +

    See ${ h.tags.link_to("our introductory page", h.url('intro-maintainers')) } for maintainers and learn how to use ${ c.config['debexpo.sitename'] } and get your packages into Debian. Furthermore see our ${ h.tags.link_to("our introductory page for sponsors", h.url('sponsors')) } to learn how to get in touch with a sponsor.

    + +

    If you are curious about Debexpo, the software which is running this site, you can read more about Debexpo on the Debian Wiki.

    Recently uploaded packages

    diff --git a/debexpo/templates/index/intro-maintainers.mako b/debexpo/templates/index/intro-maintainers.mako index 9fa0efdec9f71d6086e89b1ac58e2f3855c24e82..affb254dd2e1cb80040816adf90b86d0fd64abef 100644 --- a/debexpo/templates/index/intro-maintainers.mako +++ b/debexpo/templates/index/intro-maintainers.mako @@ -3,47 +3,135 @@ ${ c.custom_html } -

    Introduction for maintainers

    - -

    Welcome to ${ c.config['debexpo.sitetitle'] }

    +

    Introduction for maintainers: How will my package get into Debian

    We are glad that you found your way to our web site. Honestly the whole web site is just there to get your work into Debian. Your contribution is appreciated and it would be a pity if you did not find a sponsor to upload your packages. Read on to see in what ways we will be helping you find a sponsor.

    +

    1. Find a requested package

    + +

    Debian is a distribution, not a general purpose repository. Many of us do not believe every piece of free software necessarily belongs in Debian. Please do not treat Debian as a platform to advertise your own software, unless there is some real request for it. That said, there is no one who ultimately judges about that. Eventually you may get some feedback on that discussion after filing your WNPP bug (see below) however you are free to interpret that as a suggestion, not as a final vote. After all, you need to find a sponsor believing in the benefit of having your package in Debian. Please have a look at our ${ h.tags.link_to("sponsors page", h.url(controller='sponsor', action='index')) } to learn about personal interests of sponsors. + +

    If you want to contribute to Debian, but you do not know which packages to contribute to, take a look at "Request for help" (RFH), "Request for adopter" (RFA), "Orphaned package" (O) and "Request for package" (RFP) bugs. See WNPP below.

    + +

    2. File a WNPP bug

    + +

    Work-Needing and Prospective Packages (WNPP) is our system of announcing your intent to markup packages being worked on. In particular it is a bug against the WNPP pseudo package (or use a nice frontend to browse WNPP bugs). If you want to package package something not currently available in Debian, the very first step should be to file an "Intent to package" (ITP) bug against WNPP. You may want to use the reportbug tool to achieve that by selecting "wnpp" as package to report a bug to.

    + +

    3. Make the package

    + +

    Debian packages must comply to several normative requirements and guidelines. We can't give you exhaustive instructions on how to build packages here. In short, we expect you to provide a source package complying to the Debian policy at least (see below). Please take a look at those excellent resources:

    + +
    +
    The Debian Policy Manual
    +
    A must-read resource to learn about technical specification and technical requirements of Debian packages.
    +
    New Maintainer's Guide
    +
    A must-read resource to learn basics of Debian source packages
    +
    Lucas Nussbaum's packaging tutorial
    +
    A quick introduction to Debian packaging
    +
    Developer's Reference
    +
    A comprehensive manual describing typical workflows and best practices related to Debian packages
    +
    Debian Mentors FAQ (Debian Wiki)
    +
    Another valuable resource to learn about common terms and workflows related to Debian
    +
    -

    How will my package get into Debian?

    -

    This web site is a public package repository of source packages. You can upload your package to this server (through special tools like 'dupload' or 'dput') and after a few checks it will be stored in our repository. Interested sponsors can then download the package and upload it to Debian. So the basic procedure is:

    +

    4. Publish your package

    + +

    This web site is a public package repository of source packages. You can upload your package to this server (through special tools like 'dupload' or 'dput') and after a few checks it will be stored in our repository. ${ h.tags.link_to("Interested sponsors", h.url(controller='sponsor', action='index')) } can then download the package and upload it to Debian.

    + +

    Using ${ c.config['debexpo.sitetitle'] }

      -
    • ${ h.tags.link_to("Sign up for an account", h.url(controller='register', action='register')) }. Getting an account on this server is an automatic process and will just take a moment. We require registration so we have a valid email address where sponsors can reach you.
    • -
    • Upload your package to mentors.debian.net. You don not need to put your packages into any other web space on the Internet. Everybody will be able to download your package using either the web browser, the 'dget' tools or even through a simple run of apt-get source ....
    • -
    • Your package is on display on the main page of ${ c.config['debexpo.sitetitle'] } so interested sponsors will see it and hopefully check it out.
    • -
    • You will be shown a RFS (request-for-sponsorship) template that you can send to the debian-mentors mailing list to draw attention to your package.
    • -
    • Finally a sponsor will hopefully pick up your package and upload it on your behalf. Bingo - your package is publicly available in Debian. And this server will automatically send you an email in case you did not notice the upload.
    • +
    • ${ h.tags.link_to("Sign up for an account", h.url(controller='register', action='register')) }. Getting an account on this server is an automatic process and will just take a moment. We require registration so we have a valid email address where sponsors can reach you.
    • +
    • Upload your package to ${ config['debexpo.sitename'] }. You do not need to put your packages into any other web space on the Internet. Everybody will be able to download your package using either the web browser, the 'dget' tools or even through a simple run of apt-get source ....
    • +
    • Have a look at your ${ h.tags.link_to("personal package page", h.url(controller='package', action='my')) }. Your uploaded package should show up there. From there you can toggle several settings and retrieve the RFS (request-for-sponsorship) template
    • +
    • Your package is on display on the main page of ${ c.config['debexpo.sitetitle'] } (if you enable the "Needs a Sponsor" button) so interested sponsors will see it and hopefully check it out.
    • +
    • You will be shown a RFS (request-for-sponsorship) template that you should send to the debian-mentors mailing list to draw attention to your package.
    -

    Is my package technically okay?

    +

    How to upload packages to ${ config['debexpo.sitename'] }?

    + +

    You need to use dput to upload packages. We accept your uploads through HTTP or FTP. If you are using FTP your package must be signed with the GnuPG key you configured in your control panel.

    + + + + + + + + + + +
    HTTP uploadsFTP uploads
    + % if c.logged_in: + +

    See your ${ h.tags.link_to("account page", h.url('my')) } to see how to configure dput to use HTTP, or put the following content to your ~/.dput.cf file:

    + +
    +[mentors]
    +fqdn = ${ config['debexpo.sitename'] }
    +incoming = /upload/${ c.user.email }/${ c.user.get_upload_key() }
    +method = http
    +allow_unsigned_uploads = 0
    +progress_indicator = 2
    +
    + +Please keep this configuration private. + % else: + +

    You need to configure dput. Please ${ h.tags.link_to("login", h.url(controller='login', action='index')) } to see your personal ~/.dput.cf here.

    -

    When you upload your package to ${ c.config['debexpo.sitename'] } it will automatically be checked for common mistakes. You will get an information email after the upload. Either your package contains bugs and will be rejected, or the package is clean except for some minor technical issues. You will get hints about how to fix the package. If the email tells you that your package is fine then a sponsor will still do further checks. Don't worry too much. If your package is accepted by mentors.debian.net then let the sponsor help you with the rest. + % endif +

    +

    You can use FTP to upload packages to ${ c.config['debexpo.sitetitle'] }. If you prefer that method make sure you sign your uploads with your GPG key! This is the corresponding ~/.dput.cf file:

    + +
    +[mentors-ftp]
    +fqdn = ${ config['debexpo.sitename'] }
    +login = anonymous
    +progress_indicator = 2
    +passive_ftp = 1
    +incoming = /
    +method = ftp
    +allow_unsigned_uploads = 0
     
    -

    How to upload packages?

    +
    +
    -

    You need to use dput to upload packages. -See your ${ h.tags.link_to("account page", h.url('my')) } to see how to configure it.

    Once you have it set up, you can run it from your shell like this:

    +
    -dput debexpo yourpackage_yourversion_arch.changes
    +$ dput mentors your_sourcepackage_1.0.changes
     
    +If you did everything right, you will get a confirmation mail from our site and you can start seeking a sponsor for your package. + +

    5. Find a sponsor

    + +

    Once your package is publicly available on any resource, including but not limited to ${ c.config['debexpo.sitetitle'] } you may start searching for a sponsor for your package. If you have already uploaded packages to Debian, you should ask your former sponsor. A sponsor is any Debian Developer willing to upload your package to Debian on your behalf. Have a look to our ${ h.tags.link_to("sponsor's page", h.url(controller='sponsor', action='index')) } to learn more on sponsors and how to find one.

    + +

    The main point of the sponsoring process is to review your package to make sure it meets our technical requirements. Everyone can and should review other people's packages. Also, a clean package will increase your likelihood to find a sponsor. Please take a look at our page ${ h.tags.link_to("about package reviews", h.url('intro-reviewers')) }.

    + +

    The relation between you and your sponsor

    + +

    A sponsor is not technically responsible for your package. You will be listed as the official maintainer of the package in Debian. You will even get the bug reports if people discover problems in your package. Apart from not being able to upload the package directly into Debian you are treated as a full member of the community. The Debian project appreciates the work you do.

    + +

    What can I do if I don't find a sponsor?

    + +

    Do not give up. Sponsoring can take a while. Nonetheless, here are a few hints:

    + +
      +
    • Ask again on the debian-mentors mailing list. It is common practice to ask again after a few weeks.
    • +
    • Offer your package directly to relevant teams and individual developers. We made a ${ h.tags.link_to("a list of sponsors", h.url(controller='sponsor', action='index')) }, eventually willing to upload packages for you. Please don't contact every sponsor listed there. Instead, read their individual requirements and choose the sponsor which matches you and your package best. +
    -

    How long will it take until my upload is available to sponsors?

    +

    6. Getting an upload to Debian

    -

    If you upload via HTTP, which is what we recommend, then it will take between 0 and 2 minutes.

    +

    Once you find a sponsor interested in your package, he will sponsor it. That means reviewing, building and testing it and then uploading it and then uploading it to Debian. You will get notified by dak - the software used by Debian to manage its repositories - about the upload. Please note, if your package was not at all in Debian before, it needs manual approval by ftpmasters to clear the NEW queue. They will do consistency checks, and review your debian/copyright file whether your package matches the Debian Free Software Guidelines. ftpmaster's opinion is binding here for both your sponsor and you.

    -

    If you upload via FTP, which you must do if a package is too large for the HTTP uploader, then there can be up to a 30 minute delay before your package gets processed.

    +

    7. Maintaining your package in Debian

    -

    During those 0-2 minutes, the server does quality assurance and other checks on your package. You will receive an email when it is ready.

    +

    Please see the corresponding chapter in the New Maintainer's Guide to get the idea. Whenever you feel like, you should update your package in Debian. The general procedure is not different from your fist upload. Please upload your updated package to ${ config['debexpo.sitename'] } and notify your former sponsor about your change. Alternatively, follow ${ h.tags.link_to("the usual procedures", h.url(controller='sponsor', action='index')) }.

    -

    Will my name stay visible on the package?

    +

    If your package passes through the sponsoring process for a few successive uploads without any notable correction by your sponsor, you can become a Debian Maintainer which grants you limited upload rights to Debian directly. Get in touch with your sponsor to discuss your chances here. You can also become a Debian Developer giving you full membership in the project.

    -

    Yes. The Debian project appreciates the work you do. So you will be named as the official maintainer of the package in Debian. You will even get the bug reports if people discover problems in your package. Besides from not being able to upload the package directly into Debian you are treated as a full member of the community.

    diff --git a/debexpo/templates/index/qa.mako b/debexpo/templates/index/qa.mako index 8843de314125356c9f9a939c7a41938c2e536aeb..4e1435d4a1618f180bbf31fb7de622d8e277a26c 100644 --- a/debexpo/templates/index/qa.mako +++ b/debexpo/templates/index/qa.mako @@ -8,18 +8,31 @@ ${ c.config['debexpo.sitename'] } is a rather complex service. Things can go wro

    Getting started

    How do I build a package?

    -

    If you want to learn how to create a proper Debian package you can find some interesting documentation to start with at:

    +

    If you want to learn how to create a proper Debian package you can find some interesting documentation to start with:

    - +
    +
    The Debian Policy Manual
    +
    A must read resource to learn about technical specification and technical requirements of Debian packages.
    +
    New Maintainer's Guide
    +
    A must read resource to learn basics of Debian source packages
    +
    Lucas Nussbaum's packaging tutorial
    +
    A quick introduction to Debian packaging
    +
    Developer's Reference
    +
    A comprehensive manual describing typical workflows and best practices related to Debian packages
    +
    Debian Mentors FAQ (Debian Wiki)
    +
    Another valuable resource to learn about common terms and workflows related to Debian
    +

    What is the Debian policy?

    Every package must comply with the Debian policy before it can enter the Debian distribution. This is a document which describes the structure and contents of the Debian archive and several design issues of the operating system, as well as technical requirements that each package must satisfy to be included in the distribution.

    +

    The policy is not a tutorial. Its a technical manual which specifies normative requirements of packages. Read it to learn package specifications. If you don't know how to package something for Debian, please read additionally the New Maintainer's Guide.

    + +

    Is my package technically okay?

    + +

    When you upload your package to ${ c.config['debexpo.sitename'] } it will automatically be checked for common mistakes. You will get an information email after the upload. Either your package contains bugs and will be rejected, or the package is clean except for some minor technical issues. You will get hints about how to fix the package. If the email tells you that your package is fine then a sponsor will still do further checks. Don't worry too much. If your package is accepted by mentors.debian.net then let the sponsor help you with the rest. +

    What is Lintian?

    Lintian is a Debian package checker (and available as Debian package so you can run it yourself). It can be used to check binary and source packages for compliance with the Debian policy and for other common packaging errors.

    @@ -35,3 +48,26 @@ ${ c.config['debexpo.sitename'] } is a rather complex service. Things can go wro

    My binary packages do not show up

    The current policy on ${ c.config['debexpo.sitename'] } is to deliberately throw away the binary packages. This is done for two main reasons. First, it saves a lot of disk space. And second, in the past Debian users downloaded the packages and used them carelessly to get brand-new versions. This led to a lot of support questions. So we decided to just keep the source packages.

    + +

    How long will it take until my upload is available to sponsors?

    + +

    If you upload via HTTP, which is what we recommend, then it will take between 0 and 2 minutes.

    + +

    If you upload via FTP, which you must do if a package is too large for the HTTP uploader, then there can be up to a 30 minute delay before your package gets processed.

    + +

    During those 0-2 minutes, the server does quality assurance and other checks on your package. You will receive an email when it is ready.

    + +

    Information for sponsors

    + +

    What is a sponsor?

    + +

    Someone who uploads the package and is responsible for the package in the archive. The sponsor is responsible for the quality of the package and checks the work of the package maintainer to improve his skills.

    + +

    Why should I sponsor uploads

    + +

    Thanks for your interest. There are a lot of sponsorees waiting for a Developer to help them with their packages, and if you want to help with the New Maintainer process this is a good step to get involved.

    + +

    What to do for sponsoring?

    + +

    Look for packages that you would like to sponsor on this website. Once you have found some you should download, build and test them. Please notify your sponsoree of every problem that you find in order to give him a chance to fix them. We believe that it is of uttermost importance to stay in contact with your sponsorees to keep them interested in working on Debian. Moreover, they will also learn how to maintain packages within a team and will learn skills that are crucial for Debian Developers more easily.

    + diff --git a/debexpo/templates/index/reviewers.mako b/debexpo/templates/index/reviewers.mako index c542abe6d93ebf3541211958a1b75148466d1379..b632c4f861d7935f6a130d1416f3bfddb4eb8d18 100644 --- a/debexpo/templates/index/reviewers.mako +++ b/debexpo/templates/index/reviewers.mako @@ -10,7 +10,7 @@

    Glad you ask! There are many reasons why you should review packages even if you can't actually upload them.

      -
    • The person you are reviewing will appreciate it. Chances are, you find problems in a package the person was not aware of yet. So he can learn from you.
    • +
    • The person you are reviewing will appreciate it. Chances are, you find problems in a package the person was not aware of yet. So they can learn from you.
    • Eventually the package you are reviewing will be in a very good shape and you happen to learn something yourself. Moreover, you will also learn about best practices and workflows other people are using. Even if the package does not meet Debian's quality standards you can learn how not to do things.
    • People who can upload may decide based on your review whether the package in question is a suitable candidate or not.
    @@ -32,13 +32,4 @@

    Established sponsor guidelines

    -

    Several Debian Developers published their personal sponsor guidelines. Those are rules applying for a particular person or a specific packaging team in case you want to have a package sponsored by them. Typically those rules extend the Debian policy by custom requirements, or require a particular workflow from you. You can have a look at some guidelines from different people here

    - - +

    Several Debian Developers published their personal sponsor guidelines. Those are rules applying for a particular person or a specific packaging team in case you want to have a package sponsored by them. Typically those rules extend the Debian policy by custom requirements, or require a particular workflow from you. You can have a look at some guidelines from different people on our ${ h.tags.link_to( _('sponsors side'), h.url('sponsors')) }

    diff --git a/debexpo/templates/my/index.mako b/debexpo/templates/my/index.mako index 6597db4674d2696665e1df4ae7848d9f96252303..4597c1c0e6dd8d6a0a470ef3c8bb7b05731a027f 100644 --- a/debexpo/templates/my/index.mako +++ b/debexpo/templates/my/index.mako @@ -14,15 +14,15 @@ allow_unsigned_uploads = 0
    - ${ _('Change details') } + ${ _('Change details') } ${ h.html.tags.form(h.url.current()) } ${ h.html.tags.hidden('form', 'details') } - +
    - - + + @@ -38,20 +38,20 @@ allow_unsigned_uploads = 0 ${ h.html.tags.end_form() } - +
    - ${ _('Change GPG key') } + ${ _('Change GPG key') } ${ h.html.tags.form(h.url.current(), multipart=True) } ${ h.html.tags.hidden('form', 'gpg') } -
    ${ _('Name') }:${ h.html.tags.text('name', value=c.user.name) }${ _('Name') }:${ h.html.tags.text('name', value=c.user.name) }
    +
    % if c.currentgpg: - - + + @@ -79,17 +79,17 @@ allow_unsigned_uploads = 0 ${ h.html.tags.end_form() } - +
    - ${ _('Change password') } + ${ _('Change password') } ${ h.html.tags.form(h.url.current()) } ${ h.html.tags.hidden('form', 'password') } -
    ${ _('Current GPG key') }:${ c.currentgpg }${ _('Current GPG key') }:${ c.currentgpg }
    +
    - - + + @@ -110,17 +110,17 @@ allow_unsigned_uploads = 0 ${ h.html.tags.end_form() } - +
    - ${ _('Change other details') } + ${ _('Change other details') } ${ h.html.tags.form(h.url.current()) } ${ h.html.tags.hidden('form', 'other_details') } -
    ${ _('Current password') }:${ h.html.tags.password('password_current') }${ _('Current password') }:${ h.html.tags.password('password_current') }
    +
    - - + + @@ -162,7 +162,142 @@ allow_unsigned_uploads = 0
    ${ _('Country') }:${ h.html.tags.select('country', c.current_country, sorted(c.countries.iteritems(), key=lambda x: x[1])) }${ _('Country') }:${ h.html.tags.select('country', c.current_country, sorted(c.countries.iteritems(), key=lambda x: x[1])) }
    ${ h.html.tags.submit('commit', _('Submit')) }
    + ${ h.html.tags.end_form() } +
    +% if c.debian_developer and config['debexpo.enable_experimental_code'] == 'true': +
    +
    + ${ _('Public sponsor info') } + + ${ h.html.tags.form(h.url.current()) } + ${ h.html.tags.hidden('form', 'metrics') } + + + + + + + + + + + + + + + + + + + + + + + % for requirement in c.social_tags: + + + + + % endfor + + + + + + + + + + % for requirement in c.technical_tags: + + + + + % endfor + + + + + + + + + +
    ${ _('Public visibility of your profile') }: + % for availability,label in [(c.constants.SPONSOR_METRICS_PRIVATE, _("None")), \ + (c.constants.SPONSOR_METRICS_RESTRICTED, _("Restricted")), \ + (c.constants.SPONSOR_METRICS_PUBLIC, _("Full")) ]: + ${ h.html.tags.radio('availability', value=availability, label=label, checked=(c.metrics.availability == availability)) } + % endfor +
      +
    • None - Do not show up in the list of willing sponsors.
    • +
    • Restricted - Show only your preferred contact method publicly.
    • +
    • Full - Show full contact details publicly.
    • +
    +
    ${ _('Preferred contact method for sponsored maintainer') }:${ h.html.tags.select('preferred_contact_method', c.metrics.contact, c.contact_methods)}
    ${ _('Type of packages you are interested in') }:
    ${ h.html.tags.textarea('package_types', c.metrics.types, cols=82, rows=10) }
    Social requirements +
      +
    • - (first column) You are not accepting packages qualifying for that tag.
    • +
    • 0 (middle column) You have no strong opinion on that tag.
    • +
    • + (last column) You endorse usage of the implied meaning of the tag.
    • +
    • Please note, the personal pronouns in the long description address your sponsor. Please see ${ h.tags.link_to("the sponsoring page", h.url('sponsors')) }
    • +
    +
      +
    +
    + % for weight,label in [(-1, _("-")), \ + (0, _("0")), \ + (1, _("+")) ]: + ${ h.html.tags.radio(requirement.tag, value=weight, label=label, checked=(c.metrics.get_tag_weight(requirement.tag) == weight)) } + % endfor + ${ requirement.label }
    +
    "${ requirement.long_description | n}"
    +
    +
    +
    +
    + ${ _("Additional social notes") } + +
    ${ h.html.tags.textarea('social_requirements', c.metrics.social_requirements, cols=82, rows=10) } +
    Technical choices within packages +
      +
    • - (first column) You are not accepting packages qualifying for that tag.
    • +
    • 0 (middle column) You have no strong opinion on that tag.
    • +
    • + (last column) You endorse usage of the implied meaning of the tag.
    • +
    • Please note, the personal pronouns in the long description address your sponsor. Please see ${ h.tags.link_to("the sponsoring page", h.url('sponsors')) }
    • +
    +
      +
    +
    + % for weight,label in [(-1, _("-")), \ + (0, _("0")), \ + (1, _("+")) ]: + ${ h.html.tags.radio(requirement.tag, value=weight, label=label, checked=(c.metrics.get_tag_weight(requirement.tag) == weight)) } + % endfor + ${ requirement.label }
    +
    "${ requirement.long_description | n}"
    +
    +
    +
    +
    + ${ _("Additional technical notes") } + +
    + % for guideline,label in [(c.constants.SPONSOR_GUIDELINES_TYPE_NONE, _("None")), \ + (c.constants.SPONSOR_GUIDELINES_TYPE_TEXT, _("Free text")), \ + (c.constants.SPONSOR_GUIDELINES_TYPE_URL, _("URL reference")) ]: + ${ h.html.tags.radio('packaging_guidelines', value=guideline, label=label, checked=(c.metrics.guidelines == guideline)) } + % endfor +
      +
    • ${_("None")} - You don't have any additional notes.
    • +
    • ${_("Free text")} - You have additional notes you can enter below as free text.
    • +
    • ${_("URL reference")} - You have your own website for sponsoring guidelines. Enter the address below.
    • +
    +
    + ${ h.html.tags.textarea('packaging_guideline_text', c.metrics.guidelines_text, cols=82, rows=10) } +
    ${ h.html.tags.submit('commit', _('Submit')) }
    +
    ${ h.html.tags.end_form() } +% endif diff --git a/debexpo/templates/package/index.mako b/debexpo/templates/package/index.mako index 254c1515a4d8e227ed5c3382f9c3686d2b580df5..79da4bd9c61dfb1d3ced71586bb46ac4d2487e0f 100644 --- a/debexpo/templates/package/index.mako +++ b/debexpo/templates/package/index.mako @@ -24,7 +24,7 @@ ${ _('Description') }: - ${ c.package.get_description_nl2br() | n } + ${ c.package.get_description() | semitrusted} % if 'user_id' in c.session: diff --git a/debexpo/templates/packages/list.mako b/debexpo/templates/packages/list.mako index bc277b409d8e80fd84c812f64fd5e598c7197ba3..90b0202540dec655c7778896568525bb5fe83ab8 100644 --- a/debexpo/templates/packages/list.mako +++ b/debexpo/templates/packages/list.mako @@ -19,7 +19,7 @@ % for package in packagegroup.packages: ${ package.name } - ${ package.get_description_nl2br() | n } + ${ package.get_description() | n,semitrusted } ${ package.package_versions[-1].version } ${ package.user.name } diff --git a/debexpo/templates/index/intro-sponsors.mako b/debexpo/templates/sponsor/index-old.mako similarity index 98% rename from debexpo/templates/index/intro-sponsors.mako rename to debexpo/templates/sponsor/index-old.mako index 63399a7b17fcb1cf8a9c7e050a7c5e31a167950d..2cf2b3c63461b8e4b8f871df423248f031e2a473 100644 --- a/debexpo/templates/index/intro-sponsors.mako +++ b/debexpo/templates/sponsor/index-old.mako @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- <%inherit file="/base.mako"/> -${ c.custom_html } -

    Introduction for sponsors

    What is a sponsor?

    @@ -16,3 +14,4 @@ ${ c.custom_html }

    What to do for sponsoring

    Look for packages that you would like to sponsor on this website. Once you have found some you should download, build and test them. Please notify your sponsoree of every problem that you find in order to give him a chance to fix them. We believe that it is of uttermost importance to stay in contact with your sponsorees to keep them interested in working on Debian. Moreover, they will also learn how to maintain packages within a team and will learn skills that are crucial for Debian Developers more easily.

    + diff --git a/debexpo/templates/sponsor/index.mako b/debexpo/templates/sponsor/index.mako new file mode 100644 index 0000000000000000000000000000000000000000..9ffb3f123975724e6e62efc597ca3ab85cc61fe4 --- /dev/null +++ b/debexpo/templates/sponsor/index.mako @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.mako"/> +${ c.custom_html } + +

    The sponsoring process

    + +

    As sponsored maintainer you do not have upload permissions to the Debian repository. Therefore you have three possibilities to get your package into Debian:

    + +
      +
    • Join a packaging team
    • +
    • Ask the debian-mentors mailing list
    • +
    • Talk directly to people willing to sponsor your package
    • +
    + +

    A sponsor, regardless of how you found one will ${ h.tags.link_to("review", h.url('intro-reviewers')) } your package. Everyone is invited to review packages, including yourself. We encourage you to review other people's packages - both of you will benefit.

    + +

    Join a packaging team

    + +

    There are teams in Debian who maintain packages collaboratively. If your package deals with libraries for programming languages or is part of an ecosystem of associated packages, think of KDE or Gnome packages for example, you may want to join the respective team. Have a look at the (incomplete) list of packaging teams in Debian.

    + +

    Please note, each of those teams may have their own workflows and policies covering how to deal with package uploads. Contact their respective mailing lists and home pages to learn more.

    + +

    Ask the debian-mentors mailing list

    + +

    If your package does not match the interests of any team or you are not sure whether a team could be interested in your package, please write to the debian-mentors mailing list to draw attention to your package. Your request should be formatted according to our RFS ("request for sponsorship") template. If you uploaded your package to ${ config['debexpo.sitename'] }, a RFS template can be shown on your package page.

    + +

    If you are unsure or in doubt, choose this alternative.

    + +

    Typically you will reach the greatest audience by writing to our public mailing list. Eventually also some non-uploading reviewer may have a look at your package. Please do not worry if you get no answer: It happens from time to time that all interested people might be distracted or busy. It does not mean your package is bad. Feel free to ask again after a few weeks or try any of the alternative methods to find a sponsor.

    + +

    Finding a sponsor

    + +

    If you want, you can contact sponsors willing to upload packages of other maintainers directly. Please do not send out mass emails! Instead watch out for their individual requirements and guidelines. Contact individuals only if your package is compatible to their respective requirements and matches their area of interest. To tell apart sponsors who are interested in your package from those who are not, we asked developers to formulate their own sponsor traits. Please read them carefully and compare your package with their expectations.

    + +

    Similarly, you can also try to get in touch with other package maintainers directly. This makes sense if you prepared a package which extends the functionality of a related package in a useful way or can be used together. Consider you packaged log analysis tool for a web server, the maintainer of that web server might be interested to sponsor you. If you consider to contact a maintainer of such a related package directly, make sure he is actually able to sponsor you. Remember: Only Debian Developer are allowed to sponsor packages. You can identify developers by looking up their name at db.debian.org.

    + +

    Sponsor guidelines

    + +To help you find a sponsor interested in your package, they can formulate sponsor traits for either social or technical aspects. Additionally a sponsor may not usually be interested in every package but only in a certain category. + +

    Sponsor's personal interests

    + +

    Typically, sponsors are not interested in uploading any package for you. However, they could be interested if your package matched their area of interest. Please compare those package types with your package. Such categories eventually are certain programming languages your program is written in, a field of endeavour or software fulfilling a certain task.

    + +<%def name="tag_helper(requirement)"> + % if not c.sponsor_filter: +
    ${ requirement.label } (${ h.tags.link_to( _('Filter'), h.url.current(action='index', t=requirement.tag)) })
    +
    ${ requirement.long_description | n}
    + % elif requirement.tag not in c.sponsor_filter: + <% + new_tag_list = c.sponsor_filter[:] + new_tag_list.append(requirement.tag) + %> +
    ${ requirement.label } (${ h.tags.link_to( _('Add to filter'), h.url.current(action='index', t=new_tag_list)) })
    + % else: + <% + new_tag_list = c.sponsor_filter[:] + new_tag_list.remove(requirement.tag) + %> +
    ${ requirement.label } (${ h.tags.link_to( _('Remove filter'), h.url.current(action='index', t=new_tag_list)) })
    +
    ${ requirement.long_description | n}
    + % endif + + + + + + + + + + + + + + + +
    Acceptable package traitsSocial requirements
    + Debian allows several workflows and best practices to co-exist with each other. All packages must comply to the Debian policy as a bare essential minimum and although some workflows and best practices beyond that are optional it is nonetheless mandatory for you to ask someone to sponsor your upload. + + Some sponsors prefer to only upload packages from people who fulfill certain social criteria. Please don't ask an uploader to sponsor your request if you don't match them. +
    +
    + % for requirement in c.technical_tags: + <% tag_helper(requirement) %> + % endfor +
    +
    + +
    + % for requirement in c.social_tags: + <% tag_helper(requirement) %> + % endfor +
    +
    + +
    + +

    +% if c.sponsor_filter: + Applied filters: + % for filter in c.sponsor_filter: + ${ filter } + % endfor +- ${ h.tags.link_to( _('Store filter as default'), h.url('sponsor_tag_save', t=c.sponsor_filter)) } +- ${ h.tags.link_to( _('Remove all filters'), h.url.current(action='clear')) } +% endif +

    + + + + + + + +<% + def preferred(flag): + if flag: + return "(preferred)" + else: + return "" + + def put_s(name): + if name[-1] != 's': + return 's' + + sponsors_found = False +%> +% for sponsor in c.sponsors: + <% + sponsor_tags = set(sponsor.get_all_tags_weighted(1)) + filters = set(c.sponsor_filter) + %> + % if len(filters & sponsor_tags) != len(filters): + <% continue %> + % endif + <% sponsors_found = True %> + + + + + +% endfor +%if not sponsors_found and c.sponsor_filter: + + + +%elif not sponsors_found: + + + +%endif +
    Sponsor name and contact dataAcceptable package traitsSocial Requirements
    + ${ sponsor.user.name } +
    +
      + % if sponsor.user.email and sponsor.allowed(c.constants.SPONSOR_CONTACT_METHOD_EMAIL): +
    • Email: ${ sponsor.user.email } ${ preferred(sponsor.contact == c.constants.SPONSOR_CONTACT_METHOD_EMAIL) }
    • + + % endif + % if sponsor.user.ircnick and sponsor.allowed(c.constants.SPONSOR_CONTACT_METHOD_IRC): +
    • IRC: ${ sponsor.user.ircnick } ${ preferred(sponsor.contact == c.constants.SPONSOR_CONTACT_METHOD_IRC) }
    • + % endif + % if sponsor.user.jabber and sponsor.allowed(c.constants.SPONSOR_CONTACT_METHOD_JABBER): +
    • Jabber: ${ sponsor.user.jabber } ${ preferred(sponsor.contact == c.constants.SPONSOR_CONTACT_METHOD_JABBER) }
    • + % endif +
    + Personal interests +

    ${ sponsor.get_types() | n,semitrusted}

    +
    +
      + % for tag in sponsor.get_technical_tags_full(): + % if tag.weight > 0: +
    • + ${ tag.full_tag.label } (?)
    • + % elif tag.weight < 0: +
    • - ${ tag.full_tag.label } (?)
    • + % endif + % endfor +
    +

    + % if sponsor.guidelines == c.constants.SPONSOR_GUIDELINES_TYPE_URL: + ${ sponsor.user.name }'${ put_s(sponsor.user.name) } personal guidelines + % else: + ${ sponsor.get_guidelines() | semitrusted} + % endif +

    +
    +
      + % for tag in sponsor.get_social_tags_full(): + % if tag.weight > 0: +
    • + ${ tag.full_tag.label } (?)
    • + % elif tag.weight < 0: +
    • - ${ tag.full_tag.label } (?)
    • + % endif + % endfor +
    + ${ sponsor.get_social_requirements() | n,semitrusted} +

    ${ _('No sponsor matched your criteria') }

    ${ _('No sponsors found') }
    + +% if c.sponsor_filter: +

    ${ h.tags.link_to( _('Remove all filters'), h.url.current(action='clear')) }

    +% endif diff --git a/debexpo/websetup.py b/debexpo/websetup.py index b7a7c68ac51c686d968fb5f90820424749e245c5..e71425e81d4b9ff1264f4198c76568144afe7880 100644 --- a/debexpo/websetup.py +++ b/debexpo/websetup.py @@ -93,3 +93,6 @@ def setup_config(command, filename, section, vars): log.info('Making sure all ISO countries are in the database') debexpo.model.user_countries.create_iso_countries() + + log.info('Making sure all tags do exist') + debexpo.model.sponsor_metrics.create_tags() diff --git a/development.ini b/development.ini index ed1dac502fc362b42292125357eb2d9209828828..d66788a8e96b1bc5ee0fca04cada4216fde3bc72 100644 --- a/development.ini +++ b/development.ini @@ -93,6 +93,10 @@ debexpo.server = http://localhost:5000 # Path to the gpg binary debexpo.gpg_path = /usr/bin/gpg +# Enable experimental and/or broken code +debexpo.enable_experimental_code = false + + # DEBEXPO # The folling configuration settings allow you to customize the looks # of your web site. You can either comment it out or specify a path diff --git a/docs/database.dia b/docs/database.dia index a600259d1d2121123cd4cd9f7843fde4c25ecbfa..d17312f98512013910217b72e025f84cfc268c9e 100644 Binary files a/docs/database.dia and b/docs/database.dia differ