summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--devel/management/commands/reporead.py69
-rw-r--r--devel/urls.py2
-rw-r--r--devel/views.py31
-rw-r--r--main/migrations/0043_auto__add_field_package_epoch.py162
-rw-r--r--main/migrations/0044_auto__chg_field_todolist_date_added.py156
-rw-r--r--main/models.py12
-rw-r--r--media/archweb.css9
-rw-r--r--packages/templatetags/package_extras.py19
-rw-r--r--packages/utils.py2
-rw-r--r--packages/views.py59
-rw-r--r--public/utils.py9
-rw-r--r--settings.py40
-rw-r--r--templates/admin/index.html8
-rw-r--r--templates/devel/admin_log.html62
-rw-r--r--templates/devel/index.html15
-rw-r--r--templates/feeds/packages_title.html2
-rw-r--r--templates/packages/details.html8
-rw-r--r--templates/packages/differences.html4
-rw-r--r--templates/packages/files.html4
-rw-r--r--templates/packages/group_details.html4
-rw-r--r--templates/packages/search.html74
-rw-r--r--templates/packages/signoffs.html2
-rw-r--r--templates/public/index.html2
-rw-r--r--templates/todolists/list.html4
-rw-r--r--templates/todolists/public_list.html11
-rw-r--r--templates/todolists/view.html4
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 %} &rsaquo; {{ 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&amp;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>&nbsp;</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>&nbsp;</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">&lt; Prev</a>
{% else %}
<span class="prev">&lt; 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 &gt;</a>
{% else %}
<span class="next">Next &gt;</span>
@@ -74,17 +82,17 @@
{% if perms.main.change_package %}
<th>&nbsp;</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">&lt; Prev</a>
{% else %}
<span class="prev">&lt; 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 &gt;</a>
{% else %}
<span class="next">Next &gt;</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>