diff options
26 files changed, 627 insertions, 147 deletions
diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index 09e48559..3f1e9ddf 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -88,11 +88,13 @@ class Pkg(object): bare = ( 'name', 'base', 'arch', 'desc', 'filename', 'md5sum', 'url', 'builddate', 'packager' ) number = ( 'csize', 'isize' ) + version_re = re.compile(r'^((\d+):)?(.+)-([^-]+)$') def __init__(self, repo): self.repo = repo self.ver = None self.rel = None + self.epoch = 0 for k in self.bare + self.number: setattr(self, k, None) @@ -106,13 +108,22 @@ class Pkg(object): elif k == 'force': setattr(self, k, True) elif k == 'version': - ver, rel = v[0].rsplit('-') - setattr(self, 'ver', ver) - setattr(self, 'rel', rel) + match = self.version_re.match(v[0]) + self.ver = match.group(3) + self.rel = match.group(4) + if match.group(2): + self.epoch = int(match.group(2)) else: # files, depends, etc. setattr(self, k, v) + @property + def full_version(self): + '''Very similar to the main.models.Package method.''' + if self.epoch > 0: + return u'%d:%s-%s' % (self.epoch, self.ver, self.rel) + return u'%s-%s' % (self.ver, self.rel) + def find_user(userstring): ''' @@ -184,6 +195,7 @@ def populate_pkg(dbpkg, repopkg, force=False, timestamp=None): dbpkg.pkgbase = repopkg.name dbpkg.pkgver = repopkg.ver dbpkg.pkgrel = repopkg.rel + dbpkg.epoch = repopkg.epoch dbpkg.pkgdesc = repopkg.desc dbpkg.url = repopkg.url dbpkg.filename = repopkg.filename @@ -230,11 +242,11 @@ def populate_pkg(dbpkg, repopkg, force=False, timestamp=None): def populate_files(dbpkg, repopkg, force=False): if not force: - if dbpkg.pkgver != repopkg.ver or dbpkg.pkgrel != repopkg.rel: - logger.info("db version (%s-%s) didn't match repo version (%s-%s) " - "for package %s, skipping file list addition", - dbpkg.pkgver, dbpkg.pkgrel, repopkg.ver, repopkg.rel, - dbpkg.pkgname) + if dbpkg.pkgver != repopkg.ver or dbpkg.pkgrel != repopkg.rel \ + or dbpkg.epoch != repopkg.epoch: + logger.info("DB version (%s) didn't match repo version " + "(%s) for package %s, skipping file list addition", + dbpkg.full_version, repopkg.full_version, dbpkg.pkgname) return if not dbpkg.files_last_update or not dbpkg.last_update: pass @@ -273,25 +285,23 @@ def db_update(archname, reponame, pkgs, options): filesonly = options.get('filesonly', False) repository = Repo.objects.get(name__iexact=reponame) architecture = Arch.objects.get(name__iexact=archname) - dbpkgs = Package.objects.filter(arch=architecture, repo=repository) - # It makes sense to fully evaluate our DB query now because we will - # be using 99% of the objects in our "in both sets" loop. Force eval - # by calling list() on the QuerySet. - list(dbpkgs) + # no-arg order_by() removes even the default ordering; we don't need it + dbpkgs = Package.objects.filter( + arch=architecture, repo=repository).order_by() # This makes our inner loop where we find packages by name *way* more # efficient by not having to go to the database for each package to # SELECT them by name. dbdict = dict([(pkg.pkgname, pkg) for pkg in dbpkgs]) logger.debug("Creating sets") - dbset = set([pkg.pkgname for pkg in dbpkgs]) + dbset = set(dbdict.keys()) syncset = set([pkg.name for pkg in pkgs]) logger.info("%d packages in current web DB", len(dbset)) logger.info("%d packages in new updating db", len(syncset)) in_sync_not_db = syncset - dbset logger.info("%d packages in sync not db", len(in_sync_not_db)) - # Try to catch those random orphaning issues that make Eric so unhappy. + # Try to catch those random package deletions that make Eric so unhappy. if len(dbset): dbpercent = 100.0 * len(syncset) / len(dbset) else: @@ -302,12 +312,14 @@ def db_update(archname, reponame, pkgs, options): # means we expect the repo to fluctuate a lot. msg = "Package database has %.1f%% the number of packages in the " \ "web database" % dbpercent - if not filesonly and \ + if len(dbset) == 0 and len(syncset) == 0: + pass + elif not filesonly and \ len(dbset) > 20 and dbpercent < 50.0 and \ not repository.testing: logger.error(msg) raise Exception(msg) - if dbpercent < 75.0: + elif dbpercent < 75.0: logger.warning(msg) if not filesonly: @@ -321,8 +333,8 @@ def db_update(archname, reponame, pkgs, options): in_db_not_sync = dbset - syncset for p in in_db_not_sync: logger.info("Removing package %s from database", p) - Package.objects.get( - pkgname=p, arch=architecture, repo=repository).delete() + dbp = dbdict[p] + dbp.delete() # packages in both database and in syncdb (update in database) pkg_in_both = syncset & dbset @@ -334,7 +346,8 @@ def db_update(archname, reponame, pkgs, options): # for a non-force, we don't want to do anything at all. if filesonly: pass - elif p.ver == dbp.pkgver and p.rel == dbp.pkgrel: + elif p.ver == dbp.pkgver and p.rel == dbp.pkgrel \ + and p.epoch == dbp.epoch: if not force: continue else: @@ -421,10 +434,9 @@ def parse_repo(repopath): logger.info("Finished repo parsing, %d total packages", len(pkgs)) return (reponame, pkgs.values()) -def validate_arch(arch): +def validate_arch(archname): "Check if arch is valid." - available_arches = [x.name for x in Arch.objects.all()] - return arch in available_arches + return Arch.objects.filter(name__iexact=archname).exists() def read_repo(primary_arch, repo_file, options): """ @@ -432,21 +444,22 @@ def read_repo(primary_arch, repo_file, options): """ repo, packages = parse_repo(repo_file) - # sort packages by arch -- to handle noarch stuff + # group packages by arch -- to handle noarch stuff packages_arches = {} - packages_arches['any'] = [] + for arch in Arch.objects.filter(agnostic=True): + packages_arches[arch.name] = [] packages_arches[primary_arch] = [] for package in packages: - if package.arch in ('any', primary_arch): + if package.arch in packages_arches: packages_arches[package.arch].append(package) else: # we don't include mis-arched packages logger.warning("Package %s arch = %s", package.name,package.arch) logger.info('Starting database updates.') - for (arch, pkgs) in packages_arches.items(): - db_update(arch, repo, pkgs, options) + for arch in sorted(packages_arches.keys()): + db_update(arch, repo, packages_arches[arch], options) logger.info('Finished database updates.') return 0 diff --git a/devel/urls.py b/devel/urls.py index bcf9c071..41be2b31 100644 --- a/devel/urls.py +++ b/devel/urls.py @@ -5,6 +5,8 @@ urlpatterns = patterns('devel.views', (r'^clock/$', 'clock'), (r'^profile/$', 'change_profile'), (r'^newuser/$', 'new_user_form'), + (r'^admin_log/(?P<username>.*)/$','admin_log'), + (r'^admin_log/$','admin_log'), ) # vim: set ts=4 sw=4 et: diff --git a/devel/views.py b/devel/views.py index 311922ca..5b8965d8 100644 --- a/devel/views.py +++ b/devel/views.py @@ -1,9 +1,11 @@ from django import forms from django.http import HttpResponseRedirect -from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.decorators import \ + login_required, permission_required, user_passes_test from django.contrib.auth.models import User from django.contrib.sites.models import Site from django.core.mail import send_mail +from django.shortcuts import get_object_or_404 from django.template import loader, Context from django.views.decorators.cache import never_cache from django.views.generic.simple import direct_to_template @@ -34,11 +36,22 @@ def index(request): maintainers = get_annotated_maintainers() + maintained = PackageRelation.objects.filter( + type=PackageRelation.MAINTAINER).values('pkgbase') + total_orphans = Package.objects.exclude(pkgbase__in=maintained).count() + total_flagged_orphans = Package.objects.filter( + flag_date__isnull=False).exclude(pkgbase__in=maintained).count() + orphan = { + 'package_count': total_orphans, + 'flagged_count': total_flagged_orphans, + } + page_dict = { 'todos': Todolist.objects.incomplete().order_by('-date_added'), 'repos': Repo.objects.all(), 'arches': Arch.objects.all(), 'maintainers': maintainers, + 'orphan': orphan, 'flagged' : flagged, 'todopkgs' : todopkgs, } @@ -55,7 +68,9 @@ def clock(request): now = datetime.datetime.now() utc_now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) for dev in devs: - tz = pytz.timezone(dev.userprofile.time_zone) + # Work around https://bugs.launchpad.net/pytz/+bug/718673 + timezone = str(dev.userprofile.time_zone) + tz = pytz.timezone(timezone) dev.current_time = utc_now.astimezone(tz) page_dict = { @@ -167,4 +182,16 @@ def new_user_form(request): } return direct_to_template(request, 'general_form.html', context) +@user_passes_test(lambda u: u.is_superuser) +@never_cache +def admin_log(request, username=None): + user = None + if username: + user = get_object_or_404(User, username=username) + context = { + 'title': "Admin Action Log", + 'log_user': user, + } + return direct_to_template(request, 'devel/admin_log.html', context) + # vim: set ts=4 sw=4 et: diff --git a/main/migrations/0043_auto__add_field_package_epoch.py b/main/migrations/0043_auto__add_field_package_epoch.py new file mode 100644 index 00000000..77cd9b49 --- /dev/null +++ b/main/migrations/0043_auto__add_field_package_epoch.py @@ -0,0 +1,162 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'Package.epoch' + db.add_column('packages', 'epoch', self.gf('django.db.models.fields.PositiveIntegerField')(default=0), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'Package.epoch' + db.delete_column('packages', 'epoch') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.arch': { + 'Meta': {'ordering': "['name']", 'object_name': 'Arch', 'db_table': "'arches'"}, + 'agnostic': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) + }, + 'main.donor': { + 'Meta': {'ordering': "['name']", 'object_name': 'Donor', 'db_table': "'donors'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'main.package': { + 'Meta': {'ordering': "('pkgname',)", 'object_name': 'Package', 'db_table': "'packages'"}, + 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'to': "orm['main.Arch']"}), + 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'compressed_size': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}), + 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'files_last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'flag_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'installed_size': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}), + 'last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'pkgdesc': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'to': "orm['main.Repo']"}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}) + }, + 'main.packagedepend': { + 'Meta': {'object_name': 'PackageDepend', 'db_table': "'package_depends'"}, + 'depname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'depvcmp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}) + }, + 'main.packagefile': { + 'Meta': {'object_name': 'PackageFile', 'db_table': "'package_files'"}, + 'directory': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_directory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}) + }, + 'main.repo': { + 'Meta': {'ordering': "['name']", 'object_name': 'Repo', 'db_table': "'repos'"}, + 'bugs_project': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'svn_root': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'testing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'main.signoff': { + 'Meta': {'object_name': 'Signoff'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}), + 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'main.todolist': { + 'Meta': {'object_name': 'Todolist', 'db_table': "'todolists'"}, + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'date_added': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'main.todolistpkg': { + 'Meta': {'unique_together': "(('list', 'pkg'),)", 'object_name': 'TodolistPkg', 'db_table': "'todolist_pkgs'"}, + 'complete': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Todolist']"}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}) + }, + 'main.userprofile': { + 'Meta': {'object_name': 'UserProfile', 'db_table': "'user_profiles'"}, + 'alias': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'allowed_repos': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Repo']", 'symmetrical': 'False', 'blank': 'True'}), + 'favorite_distros': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'interests': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'languages': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'notify': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'occupation': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'other_contact': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'picture': ('django.db.models.fields.files.FileField', [], {'default': "'devs/silhouette.png'", 'max_length': '100'}), + 'public_email': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'roles': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'time_zone': ('django.db.models.fields.CharField', [], {'default': "'UTC'", 'max_length': '100'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'userprofile'", 'unique': 'True', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'yob': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['main'] diff --git a/main/migrations/0044_auto__chg_field_todolist_date_added.py b/main/migrations/0044_auto__chg_field_todolist_date_added.py new file mode 100644 index 00000000..d4099891 --- /dev/null +++ b/main/migrations/0044_auto__chg_field_todolist_date_added.py @@ -0,0 +1,156 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + db.alter_column('todolists', 'date_added', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + def backwards(self, orm): + db.alter_column('todolists', 'date_added', self.gf('django.db.models.fields.DateField')(auto_now_add=True)) + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.arch': { + 'Meta': {'ordering': "['name']", 'object_name': 'Arch', 'db_table': "'arches'"}, + 'agnostic': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) + }, + 'main.donor': { + 'Meta': {'ordering': "['name']", 'object_name': 'Donor', 'db_table': "'donors'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'main.package': { + 'Meta': {'ordering': "('pkgname',)", 'object_name': 'Package', 'db_table': "'packages'"}, + 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'to': "orm['main.Arch']"}), + 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'compressed_size': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}), + 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'files_last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'flag_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'installed_size': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}), + 'last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'pkgdesc': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'to': "orm['main.Repo']"}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}) + }, + 'main.packagedepend': { + 'Meta': {'object_name': 'PackageDepend', 'db_table': "'package_depends'"}, + 'depname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'depvcmp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}) + }, + 'main.packagefile': { + 'Meta': {'object_name': 'PackageFile', 'db_table': "'package_files'"}, + 'directory': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_directory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}) + }, + 'main.repo': { + 'Meta': {'ordering': "['name']", 'object_name': 'Repo', 'db_table': "'repos'"}, + 'bugs_project': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'svn_root': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'testing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'main.signoff': { + 'Meta': {'object_name': 'Signoff'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}), + 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'main.todolist': { + 'Meta': {'object_name': 'Todolist', 'db_table': "'todolists'"}, + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'main.todolistpkg': { + 'Meta': {'unique_together': "(('list', 'pkg'),)", 'object_name': 'TodolistPkg', 'db_table': "'todolist_pkgs'"}, + 'complete': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Todolist']"}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}) + }, + 'main.userprofile': { + 'Meta': {'object_name': 'UserProfile', 'db_table': "'user_profiles'"}, + 'alias': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'allowed_repos': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Repo']", 'symmetrical': 'False', 'blank': 'True'}), + 'favorite_distros': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'interests': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'languages': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'notify': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'occupation': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'other_contact': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'picture': ('django.db.models.fields.files.FileField', [], {'default': "'devs/silhouette.png'", 'max_length': '100'}), + 'public_email': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'roles': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'time_zone': ('django.db.models.fields.CharField', [], {'default': "'UTC'", 'max_length': '100'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'userprofile'", 'unique': 'True', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'yob': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['main'] diff --git a/main/models.py b/main/models.py index a7cc2335..24befddd 100644 --- a/main/models.py +++ b/main/models.py @@ -107,6 +107,7 @@ class Package(models.Model): pkgbase = models.CharField(max_length=255, db_index=True) pkgver = models.CharField(max_length=255) pkgrel = models.CharField(max_length=255) + epoch = models.PositiveIntegerField(default=0) pkgdesc = models.CharField(max_length=255, null=True) url = models.CharField(max_length=255, null=True) filename = models.CharField(max_length=255) @@ -130,6 +131,12 @@ class Package(models.Model): def __unicode__(self): return self.pkgname + @property + def full_version(self): + if self.epoch > 0: + return u'%d:%s-%s' % (self.epoch, self.pkgver, self.pkgrel) + return u'%s-%s' % (self.pkgver, self.pkgrel) + def get_absolute_url(self): return '/packages/%s/%s/%s/' % (self.repo.name.lower(), self.arch.name, self.pkgname) @@ -286,7 +293,8 @@ class Package(models.Model): 'is this package similar, name and version-wise, to another' return self.pkgname == other.pkgname \ and self.pkgver == other.pkgver \ - and self.pkgrel == other.pkgrel + and self.pkgrel == other.pkgrel \ + and self.epoch == other.epoch def in_testing(self): '''attempt to locate this package in a testing repo; if we are in @@ -341,7 +349,7 @@ class Todolist(models.Model): creator = models.ForeignKey(User) name = models.CharField(max_length=255) description = models.TextField() - date_added = models.DateField(auto_now_add=True) + date_added = models.DateTimeField(auto_now_add=True) objects = TodolistManager() def __unicode__(self): return self.name diff --git a/media/archweb.css b/media/archweb.css index 6ae720c1..c6f612ca 100644 --- a/media/archweb.css +++ b/media/archweb.css @@ -248,18 +248,15 @@ table.dash-stats .key { width: 50%; } span.dash-click { font-weight: normal; font-size: 0.8em; color: #888; } div.dash-stats h3 { color: #07b; } -/* read only (public) todo lists */ -#public_todo_lists .todo_list { - margin-left: 2em; -} - /* dev dashboard: admin actions (add news items, todo list, etc) */ ul.admin-actions { float: right; list-style: none; margin-top: -2.5em; } ul.admin-actions li { display: inline; padding-left: 1.5em; } -/* dev: todo list */ +/* todo lists (public and private) */ .todo-table .complete { color: green; } .todo-table .incomplete { color: red; } +.todo-info { margin: 0; color: #999; } +.todo-list h4 { margin-top: 0; margin-bottom: 0.4em; } /* dev: signoff page */ #dev-signoffs ul { list-style: none; margin: 0; padding: 0; } diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py index d59a5562..dd5b9347 100644 --- a/packages/templatetags/package_extras.py +++ b/packages/templatetags/package_extras.py @@ -1,4 +1,9 @@ -import cgi, urllib +import urllib +try: + from urlparse import parse_qs +except ImportError: + from cgi import parse_qs + from django import template from django.utils.html import escape @@ -9,15 +14,15 @@ class BuildQueryStringNode(template.Node): self.sortfield = sortfield def render(self, context): - qs = dict(cgi.parse_qsl(context['current_query'][1:])) - if qs.has_key('sort') and qs['sort'] == self.sortfield: + qs = parse_qs(context['current_query']) + if qs.has_key('sort') and self.sortfield in qs['sort']: if self.sortfield.startswith('-'): - qs['sort'] = self.sortfield[1:] + qs['sort'] = [self.sortfield[1:]] else: - qs['sort'] = '-' + self.sortfield + qs['sort'] = ['-' + self.sortfield] else: - qs['sort'] = self.sortfield - return '?' + urllib.urlencode(qs) + qs['sort'] = [self.sortfield] + return urllib.urlencode(qs, True) @register.tag(name='buildsortqs') def do_buildsortqs(parser, token): diff --git a/packages/utils.py b/packages/utils.py index 8d9f13ab..29a3087f 100644 --- a/packages/utils.py +++ b/packages/utils.py @@ -96,6 +96,8 @@ SELECT p.id, q.id p.pkgver != q.pkgver OR p.pkgrel != q.pkgrel + OR + p.epoch != q.epoch ) """ cursor = connection.cursor() diff --git a/packages/views.py b/packages/views.py index 59779fe4..4101c538 100644 --- a/packages/views.py +++ b/packages/views.py @@ -126,6 +126,25 @@ def getmaintainer(request, name, repo, arch): return HttpResponse(str('\n'.join(names)), mimetype='text/plain') +def coerce_limit_value(value): + if not value: + return None + if value == 'all': + # negative value indicates show all results + return -1 + value = int(value) + if value < 0: + raise ValueError + return value + +class LimitTypedChoiceField(forms.TypedChoiceField): + def valid_value(self, value): + try: + coerce_limit_value(value) + return True + except ValueError, TypeError: + return False + class PackageSearchForm(forms.Form): repo = forms.MultipleChoiceField(required=False) arch = forms.MultipleChoiceField(required=False) @@ -136,25 +155,12 @@ class PackageSearchForm(forms.Form): flagged = forms.ChoiceField( choices=[('', 'All')] + make_choice(['Flagged', 'Not Flagged']), required=False) - limit = forms.ChoiceField( + limit = LimitTypedChoiceField( choices=make_choice([50, 100, 250]) + [('all', 'All')], + coerce=coerce_limit_value, required=False, initial=50) - def clean_limit(self): - limit = self.cleaned_data['limit'] - if limit == 'all': - limit = None - elif limit: - try: - limit = int(limit) - except: - raise forms.ValidationError("Should be an integer") - else: - limit = 50 - return limit - - def __init__(self, *args, **kwargs): super(PackageSearchForm, self).__init__(*args, **kwargs) self.fields['repo'].choices = make_choice( @@ -168,12 +174,10 @@ class PackageSearchForm(forms.Form): [(m.username, m.get_full_name()) for m in maints] def search(request, page=None): - current_query = '?' limit = 50 packages = Package.objects.select_related('arch', 'repo') if request.GET: - current_query += request.GET.urlencode() form = PackageSearchForm(data=request.GET) if form.is_valid(): if form.cleaned_data['repo']: @@ -188,7 +192,8 @@ def search(request, page=None): inner_q = PackageRelation.objects.all().values('pkgbase') packages = packages.exclude(pkgbase__in=inner_q) elif form.cleaned_data['maintainer']: - inner_q = PackageRelation.objects.filter(user__username=form.cleaned_data['maintainer']).values('pkgbase') + inner_q = PackageRelation.objects.filter( + user__username=form.cleaned_data['maintainer']).values('pkgbase') packages = packages.filter(pkgbase__in=inner_q) if form.cleaned_data['flagged'] == 'Flagged': @@ -204,16 +209,26 @@ def search(request, page=None): lu = form.cleaned_data['last_update'] packages = packages.filter(last_update__gte= datetime(lu.year, lu.month, lu.day, 0, 0)) - limit = form.cleaned_data['limit'] + + asked_limit = form.cleaned_data['limit'] + if asked_limit and asked_limit < 0: + limit = None + elif asked_limit: + limit = asked_limit + else: + # Form had errors, don't return any results, just the busted form + packages = Package.objects.none() else: form = PackageSearchForm() - page_dict = {'search_form': form, - 'current_query': current_query - } if packages.count() == 1: return redirect(packages[0]) + current_query = request.GET.urlencode() + page_dict = { + 'search_form': form, + 'current_query': current_query + } allowed_sort = ["arch", "repo", "pkgname", "last_update", "flag_date"] allowed_sort += ["-" + s for s in allowed_sort] sort = request.GET.get('sort', None) diff --git a/public/utils.py b/public/utils.py index 81f589f7..8ce2af45 100644 --- a/public/utils.py +++ b/public/utils.py @@ -4,19 +4,20 @@ from main.models import Arch, Package from main.utils import cache_function @cache_function(300) -def get_recent_updates(): +def get_recent_updates(number=15): # This is a bit of magic. We are going to show 15 on the front page, but we # want to try and eliminate cross-architecture wasted space. Pull enough # packages that we can later do some screening and trim out the fat. pkgs = [] + # grab a few extra so we can hopefully catch everything we need + fetch = number * 4 for arch in Arch.objects.all(): - # grab a few extra so we can hopefully catch everything we need pkgs += list(Package.objects.select_related( - 'arch', 'repo').filter(arch=arch).order_by('-last_update')[:50]) + 'arch', 'repo').filter(arch=arch).order_by('-last_update')[:fetch]) pkgs.sort(key=attrgetter('last_update')) updates = [] ctr = 0 - while ctr < 15 and len(pkgs) > 0: + while ctr < number and len(pkgs) > 0: # not particularly happy with this logic, but it works. p = pkgs.pop() is_same = lambda q: p.is_same_version(q) and p.repo == q.repo diff --git a/settings.py b/settings.py index 8e916e36..1d26d9eb 100644 --- a/settings.py +++ b/settings.py @@ -4,6 +4,7 @@ import os ## Set the debug values DEBUG = False TEMPLATE_DEBUG = DEBUG +DEBUG_TOOLBAR = False ## Notification admins ADMINS = () @@ -44,13 +45,6 @@ LOGIN_REDIRECT_URL = '/' # Set django's User stuff to use our profile model AUTH_PROFILE_MODULE = 'main.UserProfile' -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.load_template_source', - 'django.template.loaders.eggs.load_template_source', - 'django.template.loaders.app_directories.load_template_source', -) - # We add a processor to determine if the request is secure or not TEMPLATE_CONTEXT_PROCESSORS = ( 'django.contrib.auth.context_processors.auth', @@ -61,6 +55,18 @@ TEMPLATE_CONTEXT_PROCESSORS = ( 'main.context_processors.secure', ) +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates". + # Always use forward slashes, even on Windows. + '%s/templates' % DEPLOY_PATH, +) + +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.eggs.Loader', + 'django.template.loaders.app_directories.Loader', +) + # This bug is a real bummer: # http://code.djangoproject.com/ticket/14105 MIDDLEWARE_CLASSES = ( @@ -77,18 +83,6 @@ MIDDLEWARE_CLASSES = ( ROOT_URLCONF = 'urls' -TEMPLATE_DIRS = ( - # Put strings here, like "/home/html/django_templates". - # Always use forward slashes, even on Windows. - '%s/templates' % DEPLOY_PATH, -) - -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.eggs.Loader', - 'django.template.loaders.app_directories.Loader', -) - # Configure where sessions and messages should reside MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage' SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' @@ -121,4 +115,12 @@ if not TEMPLATE_DEBUG: ('django.template.loaders.cached.Loader', TEMPLATE_LOADERS), ) +# Enable the debug toolbar if requested +if DEBUG_TOOLBAR: + MIDDLEWARE_CLASSES = \ + [ 'debug_toolbar.middleware.DebugToolbarMiddleware' ] + \ + list(MIDDLEWARE_CLASSES) + + INSTALLED_APPS = list(INSTALLED_APPS) + [ 'debug_toolbar' ] + # vim: set ts=4 sw=4 et: diff --git a/templates/admin/index.html b/templates/admin/index.html index 6f7f98ee..93cf258d 100644 --- a/templates/admin/index.html +++ b/templates/admin/index.html @@ -14,10 +14,18 @@ <div class="module"> <table> <caption>Custom Admin Pages</caption> + {% if perms.auth.add_user %} <tr> <th scope="row"><a href="/devel/newuser/">Create New User</a></th> <td></td><td></td> </tr> + {% endif %} + {% if user.is_superuser %} + <tr> + <th scope="row"><a href="/devel/admin_log/">Admin Actions Log</a></th> + <td></td><td></td> + </tr> + {% endif %} </table> </div> diff --git a/templates/devel/admin_log.html b/templates/devel/admin_log.html new file mode 100644 index 00000000..0f22ba2b --- /dev/null +++ b/templates/devel/admin_log.html @@ -0,0 +1,62 @@ +{% extends "admin/base_site.html" %} +{% load i18n %} + +{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% load adminmedia %}{% admin_media_prefix %}css/dashboard.css" />{% endblock %} + +{% block breadcrumbs %}<div class="breadcrumbs"><a href="/admin/">{% trans 'Home' %}</a>{% if title %} › {{ title }}{% endif %}</div>{% endblock %} + +{% block content %} +<div id="content-main"> + <div class="module"> +{% load log %} +{% if log_user %} +{% get_admin_log 100 as admin_log for_user log_user %} +{% else %} +{% get_admin_log 100 as admin_log %} +{% endif %} +{% if not admin_log %} +<p>{% trans 'None available' %}</p> +{% else %} +<table id="change-history"> + <thead> + <tr> + <th scope="col">{% trans 'Date/time' %}</th> + <th scope="col">{% trans 'User' %}</th> + <th>Type</th> + <th>Object</th> + <th scope="col">{% trans 'Action' %}</th> + </tr> + </thead> + <tbody> + {% for entry in admin_log %} + <tr> + <th scope="row">{{ entry.action_time|date:"DATETIME_FORMAT" }}</th> + {% if log_user %} + <td>{{ entry.user.username }}{% if entry.user.get_full_name %} ({{ entry.user.get_full_name }}){% endif %}</td> + {% else %} + <td><a href="{{ entry.user.username }}/">{{ entry.user.username }}</a>{% if entry.user.get_full_name %} ({{ entry.user.get_full_name }}){% endif %}</td> + {% endif %} + <td> + {% if entry.content_type %} + <span>{% filter capfirst %}{% trans entry.content_type.name %}{% endfilter %}</span> + {% else %} + <span>{% trans 'Unknown content' %}</span> + {% endif %} + </td> + <td> + <span class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}"></span> + {% if entry.is_deletion %} + {{ entry.object_repr }} + {% else %} + <a href="/admin/{{ entry.get_admin_url }}">{{ entry.object_repr }}</a> + {% endif %} + </td> + <td>{{ entry.change_message }}</td> + </tr> + {% endfor %} + </tbody> +</table> +{% endif %} + </div> +</div> +{% endblock %} diff --git a/templates/devel/index.html b/templates/devel/index.html index b681a96e..260e0cb9 100644 --- a/templates/devel/index.html +++ b/templates/devel/index.html @@ -27,8 +27,8 @@ <td>{{ pkg.repo.name }}</td> <td>{{ pkg.pkgver }}</td> <td>{{ pkg.arch.name }}</td> - <td>{{ pkg.flag_date }}</td> - <td>{{ pkg.last_update }}</td> + <td>{{ pkg.flag_date|date }}</td> + <td>{{ pkg.last_update|date }}</td> </tr> {% empty %} <tr class="empty"><td colspan="4"><em>No flagged packages to display</em></td></tr> @@ -80,7 +80,7 @@ <tr class="{% cycle 'odd' 'even' %}"> <td><a href="{{ todo.get_absolute_url }}" title="View todo list: {{ todo.name }}">{{ todo.name }}</a></td> - <td>{{ todo.date_added }}</td> + <td>{{ todo.date_added|date }}</td> <td class="wrap">{{ todo.description|safe }}</td> </tr> {% empty %} @@ -166,6 +166,15 @@ <th># Packages</th> <th># Flagged</th> </tr> + <tr class="even"> + <td><em>Orphan</em></td> + <td><a href="/packages/?maintainer=orphan" + title="View all orphan packages"> + <strong>{{ orphan.package_count }}</strong> packages</a></td> + <td><a href="/packages/?maintainer=orphan&flagged=Flagged" + title="View all flagged orphan packages"> + <strong>{{ orphan.flagged_count }}</strong> packages</a></td> + </tr> </thead> <tbody> {% for maint in maintainers %} diff --git a/templates/feeds/packages_title.html b/templates/feeds/packages_title.html index 910c6207..5c54ba65 100644 --- a/templates/feeds/packages_title.html +++ b/templates/feeds/packages_title.html @@ -1 +1 @@ -{{ obj.pkgname }} {{ obj.pkgver }}-{{ obj.pkgrel }} {{ obj.arch.name }} +{{ obj.pkgname }} {{ obj.full_version }} {{ obj.arch.name }} diff --git a/templates/packages/details.html b/templates/packages/details.html index 4fae6c68..8cda7ca3 100644 --- a/templates/packages/details.html +++ b/templates/packages/details.html @@ -1,14 +1,14 @@ {% extends "base.html" %} {% load cache %} -{% block title %}Parabola - {{ pkg.pkgname }} {{ pkg.pkgver }}-{{ pkg.pkgrel }} - Package Details{% endblock %} +{% block title %}Parabola - {{ pkg.pkgname }} {{ pkg.full_version }} - Package Details{% endblock %} {% block navbarclass %}anb-packages{% endblock %} {% load package_extras %} {% block content %} <div id="pkgdetails" class="box"> - <h2>Package Details: {{ pkg.pkgname }} {{ pkg.pkgver }}-{{ pkg.pkgrel }}</h2> + <h2>Package Details: {{ pkg.pkgname }} {{ pkg.full_version }}</h2> <div id="detailslinks" class="listing"> @@ -23,7 +23,7 @@ {% with pkg.in_testing as tp %}{% if tp %} <li><span class="flagged">Version <a href="{{ tp.get_absolute_url }}" - title="Testing package details for {{ tp.pkgname }}">{{ tp.pkgver }}-{{ tp.pkgrel }}</a> + title="Testing package details for {{ tp.pkgname }}">{{ tp.full_version }}</a> in testing</span></li> {% endif %}{% endwith %} {% if perms.main.change_package %} @@ -58,7 +58,7 @@ <ul> {% for o in others %} <li><a href="{{ o.get_absolute_url }}" - title="Package details for {{ o.pkgname }}">{{ o.pkgname }} {{ o.pkgver }}-{{ o.pkgrel }} [{{ o.repo.name|lower }}] ({{ o.arch.name }})</a></li> + title="Package details for {{ o.pkgname }}">{{ o.pkgname }} {{ o.full_version }} [{{ o.repo.name|lower }}] ({{ o.arch.name }})</a></li> {% endfor %} </ul> </div> diff --git a/templates/packages/differences.html b/templates/packages/differences.html index ba2d9bf5..69c39756 100644 --- a/templates/packages/differences.html +++ b/templates/packages/differences.html @@ -45,12 +45,12 @@ {% if diff.pkg_a %} <td><a href="{{ diff.pkg_a.get_absolute_url }}" title="View package details for {{ diff.pkg_a.pkgname }}"> - <span{% if diff.pkg_a.flag_date %} class="flagged"{% endif %}>{{ diff.pkg_a.pkgver }}-{{ diff.pkg_a.pkgrel }}</span></a></td> + <span{% if diff.pkg_a.flag_date %} class="flagged"{% endif %}>{{ diff.pkg_a.full_version }}</span></a></td> {% else %}<td>-</td>{% endif %} {% if diff.pkg_b %} <td><a href="{{ diff.pkg_b.get_absolute_url }}" title="View package details for {{ diff.pkg_b.pkgname }}"> - <span{% if diff.pkg_b.flag_date %} class="flagged"{% endif %}>{{ diff.pkg_b.pkgver }}-{{ diff.pkg_b.pkgrel }}</span></a></td> + <span{% if diff.pkg_b.flag_date %} class="flagged"{% endif %}>{{ diff.pkg_b.full_version }}</span></a></td> {% else %}<td>-</td>{% endif %} </tr> {% endfor %} diff --git a/templates/packages/files.html b/templates/packages/files.html index cd6b4c46..149154a6 100644 --- a/templates/packages/files.html +++ b/templates/packages/files.html @@ -1,11 +1,11 @@ {% extends "base.html" %} -{% block title %}Parabola - {{ pkg.pkgname }} {{ pkg.pkgver }}-{{ pkg.pkgrel }} - Package File List{% endblock %} +{% block title %}Parabola - {{ pkg.pkgname }} {{ pkg.full_version }} - Package File List{% endblock %} {% block navbarclass %}anb-packages{% endblock %} {% block content %} <div id="pkgdetails" class="box"> - <h2>Package File List: {{ pkg.pkgname }} {{ pkg.pkgver }}-{{ pkg.pkgrel }}</h2> + <h2>Package File List: {{ pkg.pkgname }} {{ pkg.full_version }}</h2> <div id="metadata"> <p><a href="{{ pkg.get_absolute_url }}">Back to Package</a></p> {% include "packages/files-list.html" %} diff --git a/templates/packages/group_details.html b/templates/packages/group_details.html index ee4f61d3..2a5a6149 100644 --- a/templates/packages/group_details.html +++ b/templates/packages/group_details.html @@ -24,9 +24,9 @@ <td><a href="{{ pkg.get_absolute_url }}" title="Package details for {{ pkg.pkgname }}">{{ pkg.pkgname }}</a></td> {% if pkg.flag_date %} - <td><span class="flagged">{{ pkg.pkgver }}-{{ pkg.pkgrel }}</span></td> + <td><span class="flagged">{{ pkg.full_version }}</span></td> {% else %} - <td>{{ pkg.pkgver }}-{{ pkg.pkgrel }}</td> + <td>{{ pkg.full_version }}</td> {% endif %} <td class="wrap">{{ pkg.pkgdesc }}</td> <td>{{ pkg.last_update|date }}</td> diff --git a/templates/packages/search.html b/templates/packages/search.html index ead009d0..ae9e55f2 100644 --- a/templates/packages/search.html +++ b/templates/packages/search.html @@ -14,28 +14,36 @@ <h3>Package Search</h3> - <form id="pkg-search" method="get" action="/packages/"> - <p><input type="hidden" name="sort" value='{{sort}}' /></p> - <fieldset> - <legend>Enter search criteria</legend> - <div><label for="id_arch" title="Limit results a specific CPU architecture"> - Arch</label>{{ search_form.arch }}</div> - <div><label for="id_repo" title="Limit results to a specific respository"> - Repository</label>{{ search_form.repo }}</div> - <div><label for="id_q" title="Enter keywords as desired"> - Keywords</label>{{ search_form.q }}</div> - <div><label for="id_maintainer" title="Limit results to a specific maintainer"> - Maintainer</label>{{ search_form.maintainer}}</div> - <div><label for="id_last_update" title="Limit results to a date after the date entered"> - Last Updated After</label>{{ search_form.last_update }}</div> - <div><label for="id_flagged" title="Limit results based on out-of-date status"> - Flagged</label>{{ search_form.flagged }}</div> - <div><label for="id_limit" title="Select the number of results to display per page"> - Per Page</label>{{ search_form.limit }}</div> - <div ><label> </label><input title="Search for packages using this criteria" - type="submit" value="Search" /></div> - </fieldset> - </form> + <form id="pkg-search" method="get" action="/packages/"> + <p><input type="hidden" name="sort" value='{{sort}}' /></p> + {{ search_form.non_field_errors }} + <fieldset> + <legend>Enter search criteria</legend> + <div>{{ search_form.arch.errors }} + <label for="id_arch" title="Limit results a specific CPU architecture"> + Arch</label>{{ search_form.arch }}</div> + <div>{{ search_form.repo.errors }} + <label for="id_repo" title="Limit results to a specific respository"> + Repository</label>{{ search_form.repo }}</div> + <div>{{ search_form.q.errors }} + <label for="id_q" title="Enter keywords as desired"> + Keywords</label>{{ search_form.q }}</div> + <div>{{ search_form.maintainer.errors }} + <label for="id_maintainer" title="Limit results to a specific maintainer"> + Maintainer</label>{{ search_form.maintainer}}</div> + <div>{{ search_form.last_update.errors }} + <label for="id_last_update" title="Limit results to a date after the date entered"> + Last Updated After</label>{{ search_form.last_update }}</div> + <div>{{ search_form.flagged.errors }} + <label for="id_flagged" title="Limit results based on out-of-date status"> + Flagged</label>{{ search_form.flagged }}</div> + <div>{{ search_form.limit.errors }} + <label for="id_limit" title="Select the number of results to display per page"> + Per Page</label>{{ search_form.limit }}</div> + <div ><label> </label><input title="Search for packages using this criteria" + type="submit" value="Search" /></div> + </fieldset> + </form> </div><!-- #pkglist-search --> @@ -50,13 +58,13 @@ <p class="pkglist-nav"> {% if page_obj.has_previous %} - <a class="prev" href="/packages/{{page_obj.previous_page_number}}/{{current_query}}" + <a class="prev" href="/packages/{{page_obj.previous_page_number}}/?{{current_query}}" title="Go to previous page">< Prev</a> {% else %} <span class="prev">< Prev</span> {% endif %} {% if page_obj.has_next %} - <a class="next" href="/packages/{{page_obj.next_page_number}}/{{current_query}}" + <a class="next" href="/packages/{{page_obj.next_page_number}}/?{{current_query}}" title="Go to next page">Next ></a> {% else %} <span class="next">Next ></span> @@ -74,17 +82,17 @@ {% if perms.main.change_package %} <th> </th> {% endif %} - <th><a href="/packages/{% buildsortqs "arch" %}" + <th><a href="/packages/?{% buildsortqs "arch" %}" title="Sort packages by architecture">Arch</a></th> - <th><a href="/packages/{% buildsortqs "repo" %}" + <th><a href="/packages/?{% buildsortqs "repo" %}" title="Sort packages by repository">Repo</a></th> - <th><a href="/packages/{% buildsortqs "pkgname" %}" + <th><a href="/packages/?{% buildsortqs "pkgname" %}" title="Sort packages by package name">Name</a></th> <th>Version</th> <th>Description</th> - <th><a href="/packages/{% buildsortqs "-last_update" %}" + <th><a href="/packages/?{% buildsortqs "-last_update" %}" title="Sort packages by last update">Last Updated</a></th> - <th><a href="/packages/{% buildsortqs "-flag_date" %}" + <th><a href="/packages/?{% buildsortqs "-flag_date" %}" title="Sort packages by when marked-out of-date">Flag Date</a></th> </tr> </thead> @@ -99,9 +107,9 @@ <td><a href="{{ pkg.get_absolute_url }}" title="Package details for {{ pkg.pkgname }}">{{ pkg.pkgname }}</a></td> {% if pkg.flag_date %} - <td><span class="flagged">{{ pkg.pkgver }}-{{ pkg.pkgrel }}</span></td> + <td><span class="flagged">{{ pkg.full_version }}</span></td> {% else %} - <td>{{ pkg.pkgver }}-{{ pkg.pkgrel }}</td> + <td>{{ pkg.full_version }}</td> {% endif %} <td class="wrap">{{ pkg.pkgdesc }}</td> <td>{{ pkg.last_update|date }}</td> @@ -118,13 +126,13 @@ <p class="pkglist-nav"> {% if page_obj.has_previous %} - <a class="prev" href="/packages/{{page_obj.previous_page_number}}/{{current_query}}" + <a class="prev" href="/packages/{{page_obj.previous_page_number}}/?{{current_query}}" title="Go to previous page">< Prev</a> {% else %} <span class="prev">< Prev</span> {% endif %} {% if page_obj.has_next %} - <a class="next" href="/packages/{{page_obj.next_page_number}}/{{current_query}}" + <a class="next" href="/packages/{{page_obj.next_page_number}}/?{{current_query}}" title="Go to next page">Next ></a> {% else %} <span class="next">Next ></span> diff --git a/templates/packages/signoffs.html b/templates/packages/signoffs.html index a9cce4a8..157843ac 100644 --- a/templates/packages/signoffs.html +++ b/templates/packages/signoffs.html @@ -26,7 +26,7 @@ <td>{{ pkg.arch.name }}</td> <td><a href="{{ pkg.get_absolute_url }}" title="View package details for {{ pkg.pkgname }}">{{ pkg.pkgname }}</a></td> - <td>{{ pkg.pkgver }}-{{ pkg.pkgrel }}</td> + <td>{{ pkg.full_version }}</td> <td>{{ pkg.last_update }}</td> <td>{{ target }}</td> <td class="signoff-{{pkg.approved_for_signoff|yesno}}"> diff --git a/templates/public/index.html b/templates/public/index.html index ff11994f..3432ccad 100644 --- a/templates/public/index.html +++ b/templates/public/index.html @@ -75,7 +75,7 @@ {% for update in pkg_updates %} {% with update|first as fpkg %} <tr> - <td class="pkg-name"><span class="{{ fpkg.repo|lower }}">{{ fpkg.pkgname }} {{ fpkg.pkgver }}-{{ fpkg.pkgrel }}</span></td> + <td class="pkg-name"><span class="{{ fpkg.repo|lower }}">{{ fpkg.pkgname }} {{ fpkg.full_version }}</span></td> <td class="pkg-arch"> {% for pkg in update %}<a href="{{ pkg.get_absolute_url }}" title="Details for {{ pkg.pkgname }} [{{ pkg.repo|lower }}]">{{ pkg.arch }}</a>{% if not forloop.last %}/{% endif %}{% endfor %} diff --git a/templates/todolists/list.html b/templates/todolists/list.html index a58fedc8..38d491d9 100644 --- a/templates/todolists/list.html +++ b/templates/todolists/list.html @@ -30,9 +30,9 @@ <tr class="{% cycle 'odd' 'even' %}"> <td><a href="{{ list.get_absolute_url }}" title="View todo list: {{ list.name }}">{{ list.name }}</a></td> - <td>{{ list.date_added }}</td> + <td>{{ list.date_added|date }}</td> <td>{{ list.creator.get_full_name }}</td> - <td class="wrap">{{ list.description|safe }}</td> + <td class="wrap">{{ list.description|urlize }}</td> <td>{{ list.pkg_count }}</td> <td>{{ list.incomplete_count }}</td> <td>{% ifequal list.incomplete_count 0 %}<span class="complete">Complete</span> diff --git a/templates/todolists/public_list.html b/templates/todolists/public_list.html index 5b957692..ceb001de 100644 --- a/templates/todolists/public_list.html +++ b/templates/todolists/public_list.html @@ -22,13 +22,14 @@ </div> </div> {% if todo_lists %} -<div id="public_todo_lists"> +<div id="public-todo-lists"> {% for list in todo_lists %} <div class="box"> - <a name="{{ list.id }}"></a> - <h4>{{ list.name }}</h4> - <div class="todo_list"> - <div>{{ list.description|safe|linebreaks }}</div> + <div class="todo-list"> + <a name="{{ list.id }}"></a> + <h4>{{ list.name }}</h4> + <p class="todo-info">{{ list.date_added|date }} - {{ list.creator.get_full_name }}</p> + <div>{{ list.description|urlize|linebreaks }}</div> <table id="todo-pkglist-{{ list.id }}" class="results todo-table"> <thead> <tr> diff --git a/templates/todolists/view.html b/templates/todolists/view.html index 477e0180..d4f5a08d 100644 --- a/templates/todolists/view.html +++ b/templates/todolists/view.html @@ -17,7 +17,9 @@ {% endif %} </ul> - <p>{{list.description|safe|linebreaks}}</p> + <p class="todo-info">{{ list.date_added|date }} - {{ list.creator.get_full_name }}</p> + + <div>{{list.description|urlize|linebreaks}}</div> <table id="dev-todo-pkglist" class="results todo-table"> <thead> |