summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2015-04-16 01:16:52 -0400
committerLuke Shumaker <lukeshu@sbcglobal.net>2015-04-16 01:16:52 -0400
commit7aabbe788bb4e26ee9c7cfc0e18692b487e11099 (patch)
treee1032beb86e35caafb164e694d2aa7e2e9664de2
parent046e6c8b6152a8142ed838e5c406cc04c18f2533 (diff)
parent155bf43a28e404f327a7bcff214c22e212627673 (diff)
Merge branch 'archweb-generic'
-rw-r--r--README.BRANDING5
-rw-r--r--README.md23
-rw-r--r--devel/fixtures/staff_groups.json58
-rw-r--r--devel/utils.py8
-rw-r--r--main/fixtures/groups.json29
-rw-r--r--main/templatetags/pgp.py19
-rw-r--r--main/utils.py15
-rw-r--r--mirrors/models.py1
-rw-r--r--mirrors/templatetags/jinja2.py53
-rw-r--r--mirrors/templatetags/mirror_status.py7
-rw-r--r--news/models.py7
-rw-r--r--news/urls.py3
-rw-r--r--news/views.py6
-rw-r--r--packages/templatetags/jinja2.py1
-rw-r--r--packages/views/search.py17
-rw-r--r--public/views.py37
-rw-r--r--releng/models.py6
-rw-r--r--requirements.txt4
-rw-r--r--requirements_prod.txt4
-rw-r--r--sitemaps.py29
-rw-r--r--sitestatic/archweb.css11
-rw-r--r--sitestatic/archweb.js4
-rw-r--r--sitestatic/download.pngbin0 -> 1155 bytes
-rw-r--r--sitestatic/magnet.pngbin0 -> 2382 bytes
-rw-r--r--templates/devel/index.html15
-rw-r--r--templates/mirrors/error_table.html.jinja (renamed from templates/mirrors/error_table.html)8
-rw-r--r--templates/mirrors/mirror_details.html40
-rw-r--r--templates/mirrors/mirror_details_urls.html.jinja36
-rw-r--r--templates/mirrors/status.html6
-rw-r--r--templates/mirrors/status_table.html30
-rw-r--r--templates/mirrors/status_table.html.jinja28
-rw-r--r--templates/mirrors/url_details.html29
-rw-r--r--templates/mirrors/url_details_logs.html.jinja26
-rw-r--r--templates/news/list.html3
-rw-r--r--templates/public/keys.html4
-rw-r--r--templates/releng/release_detail.html15
-rw-r--r--templates/releng/release_list.html25
-rw-r--r--templates/sitemaps/news_sitemap.xml.jinja14
-rw-r--r--templates/sitemaps/sitemap.xml.jinja9
-rw-r--r--templates/todolists/list.html7
-rw-r--r--templates/todolists/paginator.html22
-rw-r--r--todolists/urls.py6
-rw-r--r--todolists/views.py13
-rw-r--r--urls.py10
-rw-r--r--visualize/static/visualize.js5
45 files changed, 485 insertions, 213 deletions
diff --git a/README.BRANDING b/README.BRANDING
index 55ef9d91..2dc186b0 100644
--- a/README.BRANDING
+++ b/README.BRANDING
@@ -3,7 +3,7 @@ Replacing logos is still a bit of work.
Here is a summary of the text files that need to be changed to
re-brand it, from the text side.
-Files used to configure branding/url stuff
+Files used to configure branding/URL stuff
------------------------------------------
* `settings.py`
@@ -14,6 +14,9 @@ Files used to configure branding/url stuff
Files with minor Arch stuff that's just easier to patch
-------------------------------------------------------
+`devel/fixtures/staff_groups.json`
+ * Mentions of Arch in "description" fields
+
`templates/packages/flaghelp.html`
* link to "arch-general" mailing list
diff --git a/README.md b/README.md
index b2a45c47..7754d961 100644
--- a/README.md
+++ b/README.md
@@ -43,15 +43,15 @@ packages, you will probably want the following:
1. Run `virtualenv2`.
- $ cd /path/to/archweb && virtualenv2 ./env/
+ cd /path/to/archweb && virtualenv2 ./env/
2. Activate the virtualenv.
- $ source ./env/bin/activate
+ source ./env/bin/activate
2. Install dependencies through `pip`.
- (archweb-env) $ pip install -r requirements.txt
+ pip install -r requirements.txt
3. Copy `local_settings.py.example` to `local_settings.py` and modify.
Make sure to uncomment the appropriate database section (either sqlite or
@@ -59,26 +59,29 @@ packages, you will probably want the following:
4. Sync the database to create it.
- (archweb-env) $ ./manage.py syncdb
+ ./manage.py syncdb
5. Migrate changes.
- (archweb-env) $ ./manage.py migrate
+ ./manage.py migrate
6. Load the fixtures to prepopulate some data. If you don't want some of the
provided data, adjust the file glob accordingly.
- (archweb-env) $ ./manage.py loaddata */fixtures/*.json
+ ./manage.py loaddata main/fixtures/*.json
+ ./manage.py loaddata devel/fixtures/*.json
+ ./manage.py loaddata mirrors/fixtures/*.json
+ ./manage.py loaddata releng/fixtures/*.json
7. Use the following commands to start a service instance
- (archweb-env) $ ./manage.py runserver
+ ./manage.py runserver
8. To optionally populate the database with real data:
- (archweb-env) $ wget ftp://ftp.archlinux.org/core/os/i686/core.db.tar.gz
- (archweb-env) $ ./manage.py reporead i686 core.db.tar.gz
- (archweb-env) $ ./manage.py syncisos
+ wget http://mirrors.kernel.org/archlinux/core/os/i686/core.db.tar.gz
+ ./manage.py reporead i686 core.db.tar.gz
+ ./manage.py syncisos
Alter architecture and repo to get x86\_64 and packages from other repos if
needed.
diff --git a/devel/fixtures/staff_groups.json b/devel/fixtures/staff_groups.json
new file mode 100644
index 00000000..f57baa26
--- /dev/null
+++ b/devel/fixtures/staff_groups.json
@@ -0,0 +1,58 @@
+[
+{
+ "fields": {
+ "group": [
+ "Hackers"
+ ],
+ "description": "This is a list of the current Parabola Hackers. They maintain the [libre] package repository and keep the [core], [extra], and [community] repositories clean of nonfree software, in addition to doing any other developer duties.",
+ "sort_order": 1,
+ "member_title": "Hacker",
+ "slug": "hackers",
+ "name": "Hackers"
+ },
+ "model": "devel.staffgroup",
+ "pk": 1
+},
+{
+ "fields": {
+ "group": [
+ "Retired Hackers"
+ ],
+ "description": "Below you can find a list of ex-hackers (aka project fellows). These folks helped make Parabola what it is today. Thanks!",
+ "sort_order": 11,
+ "member_title": "Fellow",
+ "slug": "hacker-fellows",
+ "name": "Hacker Fellows"
+ },
+ "model": "devel.staffgroup",
+ "pk": 3
+},
+{
+ "fields": {
+ "group": [
+ "Retired Trusted Users"
+ ],
+ "description": "Below you can find a list of ex-trusted users (aka fellows). These folks helped make Parabola what it is today. Thanks!",
+ "sort_order": 12,
+ "member_title": "Fellow",
+ "slug": "trusted-user-fellows",
+ "name": "Trusted User Fellows"
+ },
+ "model": "devel.staffgroup",
+ "pk": 4
+},
+{
+ "fields": {
+ "group": [
+ "Support Staff"
+ ],
+ "description": "These are the unheralded people that keep things running behind the scenes. Forum moderators, wiki admins, IRC moderators, mirror maintenance, and everything else that keeps a Linux distro running smoothly.",
+ "sort_order": 5,
+ "member_title": "Staff",
+ "slug": "support-staff",
+ "name": "Support Staff"
+ },
+ "model": "devel.staffgroup",
+ "pk": 5
+}
+]
diff --git a/devel/utils.py b/devel/utils.py
index eaa7d07e..3326987a 100644
--- a/devel/utils.py
+++ b/devel/utils.py
@@ -6,13 +6,16 @@ from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db import connection
from django.db.models import Count, Q
+from devel.models import UserProfile
from main.utils import cache_function
from main.models import Package
from packages.models import PackageRelation
@cache_function(283)
def get_annotated_maintainers():
- maintainers = User.objects.filter(is_active=True).order_by(
+ profile_ids = UserProfile.allowed_repos.through.objects.values('userprofile_id')
+ maintainers = User.objects.filter(
+ is_active=True, userprofile__id__in=profile_ids).order_by(
'first_name', 'last_name')
# annotate the maintainers with # of maintained and flagged packages
@@ -43,7 +46,8 @@ SELECT pr.user_id, COUNT(*), COUNT(p.flag_date)
m.flagged_count = flag_count.get(m.id, 0)
m.updated_count = update_count.get(m.id, 0)
- return maintainers
+ # force non-QS context, otherwise pickling doesn't work
+ return list(maintainers)
def ignore_does_not_exist(func):
diff --git a/main/fixtures/groups.json b/main/fixtures/groups.json
index 67cab6cf..e1ef0556 100644
--- a/main/fixtures/groups.json
+++ b/main/fixtures/groups.json
@@ -206,6 +206,11 @@
"user"
],
[
+ "change_staffgroup",
+ "devel",
+ "staffgroup"
+ ],
+ [
"add_userprofile",
"devel",
"userprofile"
@@ -471,5 +476,29 @@
},
"model": "auth.group",
"pk": 8
+},
+{
+ "fields": {
+ "name": "Retired Hackers",
+ "permissions": []
+ },
+ "model": "auth.group",
+ "pk": 9
+},
+{
+ "fields": {
+ "name": "Retired Trusted Users",
+ "permissions": []
+ },
+ "model": "auth.group",
+ "pk": 10
+},
+{
+ "fields": {
+ "name": "Support Staff",
+ "permissions": []
+ },
+ "model": "auth.group",
+ "pk": 11
}
]
diff --git a/main/templatetags/pgp.py b/main/templatetags/pgp.py
index 2417f688..455e8f9c 100644
--- a/main/templatetags/pgp.py
+++ b/main/templatetags/pgp.py
@@ -44,21 +44,14 @@ def pgp_key_link(key_id, link_text=None):
return '<a href="%s" title="PGP key search for %s">%s</a>' % values
-@cache_function(1741)
-def name_for_key(normalized):
- try:
- matching_key = DeveloperKey.objects.select_related(
- 'owner').get(key=normalized, owner_id__isnull=False)
- return matching_key.owner.get_full_name()
- except DeveloperKey.DoesNotExist:
- return None
-
-
@register.simple_tag
-def user_pgp_key_link(key_id):
+def user_pgp_key_link(dev_keys, key_id):
normalized = key_id[-16:]
- name = name_for_key(normalized)
- return pgp_key_link(key_id, name)
+ found = dev_keys.get(normalized, None)
+ if found:
+ return pgp_key_link(key_id, found.owner.get_full_name())
+ else:
+ return pgp_key_link(key_id, None)
@register.filter(needs_autoescape=True)
diff --git a/main/utils.py b/main/utils.py
index cf156566..f94f314d 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -4,6 +4,8 @@ except ImportError:
import pickle
import hashlib
+import markdown
+from markdown.extensions import Extension
from django.core.cache import cache
from django.db import connections, router
@@ -109,6 +111,19 @@ def database_vendor(model, mode='read'):
return connections[database].vendor
+class EscapeHtml(Extension):
+ def extendMarkdown(self, md, md_globals):
+ del md.preprocessors['html_block']
+ del md.inlinePatterns['html']
+
+
+def parse_markdown(text, allow_html=False):
+ if allow_html:
+ return markdown.markdown(text, enable_attributes=False)
+ ext = [EscapeHtml()]
+ return markdown.markdown(text, extensions=ext, enable_attributes=False)
+
+
def groupby_preserve_order(iterable, keyfunc):
'''Take an iterable and regroup using keyfunc to determine whether items
belong to the same group. The order of the iterable is preserved and
diff --git a/mirrors/models.py b/mirrors/models.py
index 820f3328..9743d177 100644
--- a/mirrors/models.py
+++ b/mirrors/models.py
@@ -165,6 +165,7 @@ class MirrorLog(models.Model):
is_success = models.BooleanField(default=True)
error = models.TextField(blank=True, default='')
+ @property
def delay(self):
if self.last_sync is None:
return None
diff --git a/mirrors/templatetags/jinja2.py b/mirrors/templatetags/jinja2.py
new file mode 100644
index 00000000..04e50238
--- /dev/null
+++ b/mirrors/templatetags/jinja2.py
@@ -0,0 +1,53 @@
+from datetime import timedelta
+from django_jinja import library
+from markupsafe import Markup
+
+
+@library.global_function
+def country_flag(country):
+ if not country:
+ return ''
+ html = '<span class="fam-flag fam-flag-%s" title="%s"></span> ' % (
+ unicode(country.code).lower(), unicode(country.name))
+ return Markup(html)
+
+
+@library.filter
+def duration(value):
+ if not value and type(value) != timedelta:
+ return u''
+ # does not take microseconds into account
+ total_secs = value.seconds + value.days * 24 * 3600
+ mins = total_secs // 60
+ hrs, mins = divmod(mins, 60)
+ return '%d:%02d' % (hrs, mins)
+
+
+@library.filter
+def hours(value):
+ if not value and type(value) != timedelta:
+ return u''
+ # does not take microseconds into account
+ total_secs = value.seconds + value.days * 24 * 3600
+ mins = total_secs // 60
+ hrs, mins = divmod(mins, 60)
+ if hrs == 1:
+ return '%d hour' % hrs
+ return '%d hours' % hrs
+
+
+@library.filter
+def floatvalue(value, arg=2):
+ if value is None:
+ return u''
+ return '%.*f' % (arg, value)
+
+
+@library.filter
+def percentage(value, arg=1):
+ if not value and type(value) != float:
+ return u''
+ new_val = value * 100.0
+ return '%.*f%%' % (arg, new_val)
+
+# vim: set ts=4 sw=4 et:
diff --git a/mirrors/templatetags/mirror_status.py b/mirrors/templatetags/mirror_status.py
index b3810d9a..c8004e4b 100644
--- a/mirrors/templatetags/mirror_status.py
+++ b/mirrors/templatetags/mirror_status.py
@@ -31,11 +31,4 @@ def floatvalue(value, arg=2):
return u''
return '%.*f' % (arg, value)
-@register.filter
-def percentage(value, arg=1):
- if not value and type(value) != float:
- return u''
- new_val = value * 100.0
- return '%.*f%%' % (arg, new_val)
-
# vim: set ts=4 sw=4 et:
diff --git a/news/models.py b/news/models.py
index 985c1088..a66da8d4 100644
--- a/news/models.py
+++ b/news/models.py
@@ -1,11 +1,11 @@
-import markdown
-
from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.utils.safestring import mark_safe
from django.utils.timezone import now
+from main.utils import parse_markdown
+
class News(models.Model):
slug = models.SlugField(max_length=255, unique=True)
@@ -22,8 +22,7 @@ class News(models.Model):
return '/news/%s/' % self.slug
def html(self):
- return mark_safe(markdown.markdown(
- self.content, safe_mode=self.safe_mode, enable_attributes=False))
+ return mark_safe(parse_markdown(self.content, not self.safe_mode))
def __unicode__(self):
return self.title
diff --git a/news/urls.py b/news/urls.py
index 0eec6d86..c13722d4 100644
--- a/news/urls.py
+++ b/news/urls.py
@@ -5,8 +5,7 @@ from .views import (NewsDetailView, NewsListView,
urlpatterns = patterns('news.views',
- (r'^$',
- NewsListView.as_view(), {}, 'news-list'),
+ (r'^$', NewsListView.as_view(), {}, 'news-list'),
(r'^preview/$', 'preview'),
# old news URLs, permanent redirect view so we don't break all links
diff --git a/news/views.py b/news/views.py
index ca4fdf97..274ba75d 100644
--- a/news/views.py
+++ b/news/views.py
@@ -1,5 +1,3 @@
-import markdown
-
from django import forms
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect
@@ -8,7 +6,7 @@ from django.views.generic import (DetailView, ListView,
CreateView, UpdateView, DeleteView)
from .models import News
-from main.utils import find_unique_slug
+from main.utils import find_unique_slug, parse_markdown
class NewsForm(forms.ModelForm):
@@ -62,7 +60,7 @@ def view_redirect(request, object_id):
@require_POST
def preview(request):
data = request.POST.get('data', '')
- markup = markdown.markdown(data, safe_mode=True, enable_attributes=False)
+ markup = parse_markdown(data)
return HttpResponse(markup)
# vim: set ts=4 sw=4 et:
diff --git a/packages/templatetags/jinja2.py b/packages/templatetags/jinja2.py
index a07f87bd..f0b42a09 100644
--- a/packages/templatetags/jinja2.py
+++ b/packages/templatetags/jinja2.py
@@ -88,5 +88,4 @@ def flag_unfree(package):
}
return link_encode(url, data)
-
# vim: set ts=4 sw=4 et:
diff --git a/packages/views/search.py b/packages/views/search.py
index 0b776d79..e4cd0423 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -6,6 +6,7 @@ from django.db.models import Q
from django.http import HttpResponse
from django.views.generic import ListView
+from devel.models import UserProfile
from main.models import Package, Arch, Repo
from main.utils import empty_response, make_choice
from ..models import PackageRelation
@@ -36,14 +37,16 @@ class PackageSearchForm(forms.Form):
self.fields['arch'].choices = make_choice(
[arch.name for arch in Arch.objects.all()])
self.fields['q'].widget.attrs.update({"size": "30"})
- maints = User.objects.filter(is_active=True).order_by(
+
+ profile_ids = UserProfile.allowed_repos.through.objects.values('userprofile_id')
+ people = User.objects.filter(
+ is_active=True, userprofile__id__in=profile_ids).order_by(
'first_name', 'last_name')
- self.fields['maintainer'].choices = \
- [('', 'All'), ('orphan', 'Orphan')] + \
- [(m.username, m.get_full_name()) for m in maints]
- self.fields['packager'].choices = \
- [('', 'All'), ('unknown', 'Unknown')] + \
- [(m.username, m.get_full_name()) for m in maints]
+ people = [('', 'All'), ('orphan', 'Orphan')] + \
+ [(p.username, p.get_full_name()) for p in people]
+
+ self.fields['maintainer'].choices = people
+ self.fields['packager'].choices = people
def exact_matches(self):
# only do exact match search if 'q' is sole parameter
diff --git a/public/views.py b/public/views.py
index 1b79e7c8..088ee4de 100644
--- a/public/views.py
+++ b/public/views.py
@@ -9,7 +9,7 @@ from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render
from django.views.decorators.cache import cache_control, cache_page
-from devel.models import MasterKey, PGPSignature, StaffGroup
+from devel.models import MasterKey, DeveloperKey, PGPSignature, StaffGroup, UserProfile
from main.models import Arch, Repo, Donor
from mirrors.models import MirrorUrl
from news.models import News
@@ -67,7 +67,9 @@ def feeds(request):
@cache_control(max_age=307)
def keys(request):
- users = User.objects.filter(is_active=True).select_related(
+ profile_ids = UserProfile.allowed_repos.through.objects.values('userprofile_id')
+ users = User.objects.filter(
+ is_active=True, userprofile__id__in=profile_ids).select_related(
'userprofile__pgp_key').order_by('first_name', 'last_name')
user_key_ids = frozenset(user.userprofile.pgp_key[-16:] for user in users
if user.userprofile.pgp_key)
@@ -90,34 +92,39 @@ def keys(request):
not_expired, revoked__isnull=True).values_list('signer', 'signee'))
restrict = Q(signer__in=user_key_ids) & Q(signee__in=user_key_ids)
- cross_signatures = PGPSignature.objects.filter(restrict,
+ cross_signatures = PGPSignature.objects.filter(
not_expired, revoked__isnull=True).order_by('created')
+ # filter in python so we aren't sending a crazy long query to the DB
+ cross_signatures = [s for s in cross_signatures
+ if s.signer in user_key_ids and s.signee in user_key_ids]
+
+ developer_keys = DeveloperKey.objects.select_related(
+ 'owner').filter(owner__isnull=False)
+ developer_keys = {key.key[-16:]: key for key in developer_keys}
context = {
'keys': master_keys,
'active_users': users,
'signatures': signatures,
'cross_signatures': cross_signatures,
+ 'developer_keys': developer_keys,
}
return render(request, 'public/keys.html', context)
@cache_page(1789)
def keys_json(request):
- node_list = []
+ profile_ids = UserProfile.allowed_repos.through.objects.values('userprofile_id')
+ users = User.objects.filter(
+ is_active=True, userprofile__id__in=profile_ids).select_related(
+ 'userprofile__pgp_key').order_by('first_name', 'last_name')
users = User.objects.filter(is_active=True).select_related('userprofile')
- node_list.extend({
- 'name': dev.get_full_name(),
- 'key': dev.userprofile.pgp_key,
- 'group': 'dev'
- } for dev in users.filter(groups__name='Developers'))
- node_list.extend({
- 'name': tu.get_full_name(),
- 'key': tu.userprofile.pgp_key,
- 'group': 'tu'
- } for tu in users.filter(groups__name='Trusted Users').exclude(
- groups__name='Developers'))
+ node_list = [{
+ 'name': user.get_full_name(),
+ 'key': user.userprofile.pgp_key,
+ 'group': 'packager'
+ } for user in users]
master_keys = MasterKey.objects.select_related('owner').filter(
revoked__isnull=True)
diff --git a/releng/models.py b/releng/models.py
index 2f9216bd..a4af81ab 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -2,7 +2,6 @@ from base64 import b64decode
from bencode import bdecode, bencode
from datetime import datetime
import hashlib
-import markdown
from pytz import utc
from django.conf import settings
@@ -12,7 +11,7 @@ from django.db.models.signals import pre_save
from django.utils.safestring import mark_safe
from main.fields import PositiveBigIntegerField
-from main.utils import set_created_field
+from main.utils import set_created_field, parse_markdown
class IsoOption(models.Model):
@@ -154,8 +153,7 @@ class Release(models.Model):
return "magnet:?%s" % '&'.join(['%s=%s' % (k, v) for k, v in query])
def info_html(self):
- return mark_safe(markdown.markdown(
- self.info, safe_mode=True, enable_attributes=False))
+ return mark_safe(parse_markdown(self.info))
def torrent(self):
try:
diff --git a/requirements.txt b/requirements.txt
index e1acd2e9..2a5dba96 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,9 +2,9 @@
Django==1.7.1
IPy==0.81
Jinja2==2.7.3
-Markdown==2.4.1
+Markdown==2.5.2
bencode==1.0
-django-jinja==1.0.4
+django-jinja==1.0.5
django_countries==3.0.1
jsmin==2.0.11
pgpdump==1.5
diff --git a/requirements_prod.txt b/requirements_prod.txt
index ef535eb8..6edfa17f 100644
--- a/requirements_prod.txt
+++ b/requirements_prod.txt
@@ -2,9 +2,9 @@
Django==1.7.1
IPy==0.81
Jinja2==2.7.3
-Markdown==2.4.1
+Markdown==2.5.2
bencode==1.0
-django-jinja==1.0.4
+django-jinja==1.0.5
django_countries==3.0.1
jsmin==2.0.11
pgpdump==1.5
diff --git a/sitemaps.py b/sitemaps.py
index eb4e05d9..03ad9254 100644
--- a/sitemaps.py
+++ b/sitemaps.py
@@ -8,6 +8,7 @@ from main.models import Package
from news.models import News
from packages.utils import get_group_info, get_split_packages_info
from releng.models import Release
+from todolists.models import Todolist
class PackagesSitemap(Sitemap):
@@ -98,6 +99,13 @@ class NewsSitemap(Sitemap):
return 'yearly'
+class RecentNewsSitemap(NewsSitemap):
+ def items(self):
+ now = datetime.utcnow().replace(tzinfo=utc)
+ cutoff = now - timedelta(days=30)
+ return super(RecentNewsSitemap, self).items().filter(postdate__gte=cutoff)
+
+
class ReleasesSitemap(Sitemap):
changefreq = "monthly"
@@ -105,7 +113,7 @@ class ReleasesSitemap(Sitemap):
return Release.objects.all().defer('info', 'torrent_data').order_by()
def lastmod(self, obj):
- return obj.created
+ return obj.last_modified
def priority(self, obj):
if obj.available:
@@ -113,6 +121,25 @@ class ReleasesSitemap(Sitemap):
return "0.2"
+class TodolistSitemap(Sitemap):
+ priority = "0.4"
+
+ def __init__(self):
+ now = datetime.utcnow().replace(tzinfo=utc)
+ self.two_weeks_ago = now - timedelta(days=14)
+
+ def items(self):
+ return Todolist.objects.all().defer('raw').order_by()
+
+ def lastmod(self, obj):
+ return obj.last_modified
+
+ def changefreq(self, obj):
+ if obj.last_modified > self.two_weeks_ago:
+ return 'weekly'
+ return 'monthly'
+
+
class BaseSitemap(Sitemap):
DEFAULT_PRIORITY = 0.7
diff --git a/sitestatic/archweb.css b/sitestatic/archweb.css
index 0a16687e..db053a52 100644
--- a/sitestatic/archweb.css
+++ b/sitestatic/archweb.css
@@ -631,6 +631,17 @@ div.news-article .article-info {
width: 75%;
}
+/* todolists: list */
+.todolist-nav {
+ float: right;
+ margin-top: -2.2em;
+}
+
+ .todolist-nav .prev,
+ .todolist-nav .next {
+ margin: 0 1em;
+ }
+
/* donate: donor list */
#donor-list ul {
width: 100%;
diff --git a/sitestatic/archweb.js b/sitestatic/archweb.js
index 43816c5b..dce9cd0c 100644
--- a/sitestatic/archweb.js
+++ b/sitestatic/archweb.js
@@ -303,12 +303,12 @@ function filter_packages() {
var cells = $(this).children('td');
/* all this just to get the split version out of the table cell */
- var ver_a = cells.eq(2).find('span').text().match(pat);
+ var ver_a = cells.eq(2).text().match(pat);
if (!ver_a) {
return true;
}
- var ver_b = cells.eq(3).find('span').text().match(pat);
+ var ver_b = cells.eq(3).text().match(pat);
if (!ver_b) {
return true;
}
diff --git a/sitestatic/download.png b/sitestatic/download.png
new file mode 100644
index 00000000..9ab858c2
--- /dev/null
+++ b/sitestatic/download.png
Binary files differ
diff --git a/sitestatic/magnet.png b/sitestatic/magnet.png
new file mode 100644
index 00000000..f67e69b9
--- /dev/null
+++ b/sitestatic/magnet.png
Binary files differ
diff --git a/templates/devel/index.html b/templates/devel/index.html
index 72f149a3..3a0fb9a9 100644
--- a/templates/devel/index.html
+++ b/templates/devel/index.html
@@ -157,8 +157,11 @@
</ul>
</div>{# #dev-dashboard #}
-<div id='stats-area'>
- <p>Enable Javascript to get more useful info here.</p>
+<div id="stats-area">
+ <div class="box">
+ <h2>Developer Stats</h2>
+ <p id="stats-message">Enable JavaScript to get more useful info here.</p>
+ </div>
</div>
{% endblock %}
@@ -167,8 +170,12 @@
<script type="text/javascript" src="{% static "archweb.js" %}"></script>
<script type="text/javascript">
$(document).ready(function() {
- $("#stats-area").html('<p>Loading stats…</p>');
- $("#stats-area").load('stats/', function() {
+ $("#stats-message").html('Loading developer stats…');
+ $("#stats-area").load('stats/', function(response, status, xhr) {
+ if (status === 'error' || status === 'timeout') {
+ $("#stats-message").html('Developer stats loading encountered an error. Sorry.');
+ return;
+ }
var settings = {
widgets: ['zebra'],
sortList: [[0,0]],
diff --git a/templates/mirrors/error_table.html b/templates/mirrors/error_table.html.jinja
index cd7265af..52f68135 100644
--- a/templates/mirrors/error_table.html
+++ b/templates/mirrors/error_table.html.jinja
@@ -1,5 +1,3 @@
-{% load cycle from future %}
-{% load flags mirror_status %}
<table id="errorlog_mirrors" class="results">
<thead>
<tr>
@@ -12,12 +10,12 @@
</tr>
</thead>
<tbody>
- {% for log in error_logs %}<tr class="{% cycle 'odd' 'even' %}">
+ {% for log in error_logs %}<tr class="{{ loop.cycle('odd', 'even') }}">
<td>{{ log.url__url }}</td>
<td>{{ log.url__protocol__protocol }}</td>
- <td class="country">{% country_flag log.country %}{{ log.country.name }}</td>
+ <td class="country">{{ country_flag(log.country) }}{{ log.country.name }}</td>
<td class="wrap">{{ log.error|linebreaksbr }}</td>
- <td>{{ log.last_occurred|date:'Y-m-d H:i' }}</td>
+ <td>{{ log.last_occurred|date('Y-m-d H:i') }}</td>
<td>{{ log.error_count }}</td>
</tr>{% endfor %}
</tbody>
diff --git a/templates/mirrors/mirror_details.html b/templates/mirrors/mirror_details.html
index ffaacfe6..3220195d 100644
--- a/templates/mirrors/mirror_details.html
+++ b/templates/mirrors/mirror_details.html
@@ -90,46 +90,10 @@
</table>
<h3>Available URLs</h3>
-
- <table id="available_urls" class="results">
- <thead>
- <tr>
- <th>Mirror URL</th>
- <th>Protocol</th>
- <th>Country</th>
- <th>IPv4</th>
- <th>IPv6</th>
- <th>Last Sync</th>
- <th>Completion %</th>
- <th>μ Delay (hh:mm)</th>
- <th>μ Duration (s)</th>
- <th>σ Duration (s)</th>
- <th>Score</th>
- <th>Details</th>
- </tr>
- </thead>
- <tbody>
- {% for m_url in urls %}
- <tr class="{% cycle 'odd' 'even' %}">
- <td>{% if m_url.protocol.is_download %}<a href="{{ m_url.url }}">{{ m_url.url }}</a>{% else %}{{ m_url.url }}{% endif %}</td>
- <td>{{ m_url.protocol }}</td>
- <td class="country">{% country_flag m_url.country %}{{ m_url.country.name }}</td>
- <td>{{ m_url.has_ipv4|yesno|capfirst }}</td>
- <td>{{ m_url.has_ipv6|yesno|capfirst }}</td>
- <td>{{ m_url.last_sync|date:'Y-m-d H:i'|default:'unknown' }}</td>
- <td>{{ m_url.completion_pct|percentage:1 }}</td>
- <td>{{ m_url.delay|duration|default:'unknown' }}</td>
- <td>{{ m_url.duration_avg|floatvalue:2 }}</td>
- <td>{{ m_url.duration_stddev|floatvalue:2 }}</td>
- <td>{{ m_url.score|floatvalue:1|default:'∞' }}</td>
- <td><a href="{{ m_url.id }}/">Details</a></td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
+ {% include "mirrors/mirror_details_urls.html.jinja" %}
<h3>Error Log</h3>
- {% include "mirrors/error_table.html" %}
+ {% include "mirrors/error_table.html.jinja" %}
</div>
<div class="box">
diff --git a/templates/mirrors/mirror_details_urls.html.jinja b/templates/mirrors/mirror_details_urls.html.jinja
new file mode 100644
index 00000000..7ab1548b
--- /dev/null
+++ b/templates/mirrors/mirror_details_urls.html.jinja
@@ -0,0 +1,36 @@
+<table id="available_urls" class="results">
+ <thead>
+ <tr>
+ <th>Mirror URL</th>
+ <th>Protocol</th>
+ <th>Country</th>
+ <th>IPv4</th>
+ <th>IPv6</th>
+ <th>Last Sync</th>
+ <th>Completion %</th>
+ <th>μ Delay (hh:mm)</th>
+ <th>μ Duration (s)</th>
+ <th>σ Duration (s)</th>
+ <th>Score</th>
+ <th>Details</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for m_url in urls %}
+ <tr class="{{ loop.cycle('odd', 'even') }}">
+ <td>{% if m_url.protocol.is_download %}<a href="{{ m_url.url }}">{{ m_url.url }}</a>{% else %}{{ m_url.url }}{% endif %}</td>
+ <td>{{ m_url.protocol }}</td>
+ <td class="country">{{ country_flag(m_url.country) }}{{ m_url.country.name }}</td>
+ <td>{{ m_url.has_ipv4|yesno|capfirst }}</td>
+ <td>{{ m_url.has_ipv6|yesno|capfirst }}</td>
+ <td>{{ m_url.last_sync|date('Y-m-d H:i')|default('unknown') }}</td>
+ <td>{{ m_url.completion_pct|percentage(1) }}</td>
+ <td>{{ m_url.delay|duration|default('unknown') }}</td>
+ <td>{{ m_url.duration_avg|floatvalue(2) }}</td>
+ <td>{{ m_url.duration_stddev|floatvalue(2) }}</td>
+ <td>{{ m_url.score|floatvalue(1)|default('∞') }}</td>
+ <td><a href="{{ m_url.id }}/">Details</a></td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
diff --git a/templates/mirrors/status.html b/templates/mirrors/status.html
index 250d9bad..f11d57ca 100644
--- a/templates/mirrors/status.html
+++ b/templates/mirrors/status.html
@@ -60,18 +60,18 @@
<a name="outofsync" id="outofsync"></a>
<h3>Out of Sync Mirrors</h3>
{% with urls=bad_urls table_id='outofsync_mirrors' %}
- {% include "mirrors/status_table.html" %}
+ {% include "mirrors/status_table.html.jinja" %}
{% endwith %}
<a name="successful" id="successful"></a>
<h3>Successfully Syncing Mirrors</h3>
{% with urls=good_urls table_id='successful_mirrors' %}
- {% include "mirrors/status_table.html" %}
+ {% include "mirrors/status_table.html.jinja" %}
{% endwith %}
<a name="errorlog" id="errorlog"></a>
<h3>Mirror Syncing Error Log</h3>
- {% include "mirrors/error_table.html" %}
+ {% include "mirrors/error_table.html.jinja" %}
</div>
{% endblock %}
diff --git a/templates/mirrors/status_table.html b/templates/mirrors/status_table.html
deleted file mode 100644
index 83538303..00000000
--- a/templates/mirrors/status_table.html
+++ /dev/null
@@ -1,30 +0,0 @@
-{% load cycle from future %}
-{% load flags mirror_status %}
-<table id="{{ table_id }}" class="results">
- <thead>
- <tr>
- <th>Mirror URL</th>
- <th>Protocol</th>
- <th>Country</th>
- <th>Completion %</th>
- <th>μ Delay (hh:mm)</th>
- <th>μ Duration (s)</th>
- <th>σ Duration (s)</th>
- <th>Mirror Score</th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- {% for m_url in urls %}<tr class="{% cycle 'odd' 'even' %}">
- <td>{{ m_url.url }}</td>
- <td>{{ m_url.protocol }}</td>
- <td class="country">{% country_flag m_url.country %}{{ m_url.country.name }}</td>
- <td>{{ m_url.completion_pct|percentage:1 }}</td>
- <td>{{ m_url.delay|duration|default:'unknown' }}</td>
- <td>{{ m_url.duration_avg|floatvalue:2 }}</td>
- <td>{{ m_url.duration_stddev|floatvalue:2 }}</td>
- <td>{{ m_url.score|floatvalue:1|default:'∞' }}</td>
- <td><a href="{{ m_url.get_absolute_url }}">details</a></td>
- </tr>{% endfor %}
- </tbody>
-</table>
diff --git a/templates/mirrors/status_table.html.jinja b/templates/mirrors/status_table.html.jinja
new file mode 100644
index 00000000..598a1af0
--- /dev/null
+++ b/templates/mirrors/status_table.html.jinja
@@ -0,0 +1,28 @@
+<table id="{{ table_id }}" class="results">
+ <thead>
+ <tr>
+ <th>Mirror URL</th>
+ <th>Protocol</th>
+ <th>Country</th>
+ <th>Completion %</th>
+ <th>μ Delay (hh:mm)</th>
+ <th>μ Duration (s)</th>
+ <th>σ Duration (s)</th>
+ <th>Mirror Score</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for m_url in urls %}<tr class="{{ loop.cycle('odd', 'even') }}">
+ <td>{{ m_url.url }}</td>
+ <td>{{ m_url.protocol }}</td>
+ <td class="country">{{ country_flag(m_url.country) }}{{ m_url.country.name }}</td>
+ <td>{{ m_url.completion_pct|percentage(1) }}</td>
+ <td>{{ m_url.delay|duration|default('unknown') }}</td>
+ <td>{{ m_url.duration_avg|floatvalue(2) }}</td>
+ <td>{{ m_url.duration_stddev|floatvalue(2) }}</td>
+ <td>{{ m_url.score|floatvalue(1)|default('∞') }}</td>
+ <td><a href="{{ m_url.get_absolute_url() }}">details</a></td>
+ </tr>{% endfor %}
+ </tbody>
+</table>
diff --git a/templates/mirrors/url_details.html b/templates/mirrors/url_details.html
index c47e0508..96fcc49d 100644
--- a/templates/mirrors/url_details.html
+++ b/templates/mirrors/url_details.html
@@ -1,5 +1,4 @@
{% extends "base.html" %}
-{% load cycle from future %}
{% load static from staticfiles %}
{% load mirror_status %}
{% load flags %}
@@ -58,33 +57,7 @@
</table>
<h3>Check Logs</h3>
-
- <table id="check_logs" class="results">
- <thead>
- <tr>
- <th>Check Time</th>
- <th>Check Location</th>
- <th>Check IP</th>
- <th>Last Sync</th>
- <th>Delay (hh:mm)</th>
- <th>Duration (s)</th>
- <th>Success?</th>
- <th>Error Message</th>
- </tr>
- </thead>
- <tbody>
- {% for log in logs %}<tr class="{% cycle 'odd' 'even' %}">
- <td>{{ log.check_time|date:'Y-m-d H:i' }}</td>
- <td class="country">{% country_flag log.location.country %}{{ log.location.country.name }}</td>
- <td>{{ log.location.source_ip }}</td>
- <td>{{ log.last_sync|date:'Y-m-d H:i' }}</td>
- <td>{{ log.delay|duration }}</td>
- <td>{{ log.duration|floatvalue }}</td>
- <td>{{ log.is_success|yesno|capfirst }}</td>
- <td class="wrap">{{ log.error|linebreaksbr }}</td>
- </tr>{% endfor %}
- </tbody>
- </table>
+ {% include "mirrors/url_details_logs.html.jinja" %}
</div>
{% endblock %}
diff --git a/templates/mirrors/url_details_logs.html.jinja b/templates/mirrors/url_details_logs.html.jinja
new file mode 100644
index 00000000..58f179d8
--- /dev/null
+++ b/templates/mirrors/url_details_logs.html.jinja
@@ -0,0 +1,26 @@
+<table id="check_logs" class="results">
+ <thead>
+ <tr>
+ <th>Check Time</th>
+ <th>Check Location</th>
+ <th>Check IP</th>
+ <th>Last Sync</th>
+ <th>Delay (hh:mm)</th>
+ <th>Duration (s)</th>
+ <th>Success?</th>
+ <th>Error Message</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for log in logs %}<tr class="{{ loop.cycle('odd', 'even') }}">
+ <td>{{ log.check_time|date('Y-m-d H:i') }}</td>
+ <td class="country">{{ country_flag(log.location.country) }}{{ log.location.country.name }}</td>
+ <td>{{ log.location.source_ip }}</td>
+ <td>{{ log.last_sync|date('Y-m-d H:i') }}</td>
+ <td>{{ log.delay|duration }}</td>
+ <td>{{ log.duration|floatvalue }}</td>
+ <td>{{ log.is_success|yesno|capfirst }}</td>
+ <td class="wrap">{{ log.error|linebreaksbr }}</td>
+ </tr>{% endfor %}
+ </tbody>
+</table>
diff --git a/templates/news/list.html b/templates/news/list.html
index 3295e333..8662a91b 100644
--- a/templates/news/list.html
+++ b/templates/news/list.html
@@ -10,7 +10,7 @@
{% block content %}
<div id="news-article-list" class="box">
- <h2>News Archives</h2>
+ <h2>{{ BRANDING_DISTRONAME }} News Archives</h2>
{% if perms.news.add_news %}
<ul class="admin-actions">
@@ -54,6 +54,5 @@
</table>
{% include "news/paginator.html" %}
-
</div>
{% endblock %}
diff --git a/templates/public/keys.html b/templates/public/keys.html
index 0818719c..f15ec1a9 100644
--- a/templates/public/keys.html
+++ b/templates/public/keys.html
@@ -123,8 +123,8 @@
<tbody>
{% for sig in cross_signatures %}
<tr>
- <td>{% user_pgp_key_link sig.signer %}</td>
- <td>{% user_pgp_key_link sig.signee %}</td>
+ <td>{% user_pgp_key_link developer_keys sig.signer %}</td>
+ <td>{% user_pgp_key_link developer_keys sig.signee %}</td>
<td>{{ sig.created }}</td>
<td>{{ sig.expires|default:"" }}</td>
</tr>
diff --git a/templates/releng/release_detail.html b/templates/releng/release_detail.html
index d04533b9..97017600 100644
--- a/templates/releng/release_detail.html
+++ b/templates/releng/release_detail.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load static %}
{% block title %}{{ BRANDING_DISTRONAME }} - Release: {{ release.version }}{% endblock %}
@@ -9,11 +10,15 @@
<ul>
<li><strong>Release Date:</strong> {{ release.release_date|date }}</li>
{% if release.kernel_version %}<li><strong>Kernel Version:</strong> {{ release.kernel_version }}</li>{% endif %}
- <li><strong>Available:</strong> {{ release.available|yesno }}</li>
- {% if release.torrent_data %}<li><strong>Download:</strong> <a
- href="{% url 'releng-release-torrent' release.version %}"
- title="Download torrent for {{ release.version }}">Torrent</a>,
- <a href="{{ release.magnet_uri }}">Magnet</a></li>{% endif %}
+ <li><strong>Available:</strong> {{ release.available|yesno|capfirst }}</li>
+ {% if release.torrent_data %}
+ <li><a href="{% url 'releng-release-torrent' release.version %}"
+ title="Download torrent for {{ release.version }}">
+ Download via Torrent <img width="12" height="12" src="{% static "download.png" %}" alt=""/></a></li>
+ <li><a href="{{ release.magnet_uri }}"
+ title="Get magnet link for {{ release.version }}">
+ Download via Magnet URI <img width="12" height="12" src="{% static "magnet.png" %}" alt=""/></a></li>
+ {% endif %}
{% if release.md5_sum %}<li><strong>MD5:</strong> {{ release.md5_sum }}</li>{% endif %}
{% if release.sha1_sum %}<li><strong>SHA1:</strong> {{ release.sha1_sum }}</li>{% endif %}
</ul>
diff --git a/templates/releng/release_list.html b/templates/releng/release_list.html
index af8b8a61..3f442d07 100644
--- a/templates/releng/release_list.html
+++ b/templates/releng/release_list.html
@@ -12,28 +12,41 @@
<div id="release-list" class="box">
<h2>Releases</h2>
+ <p>This is a list of ISO releases made by the Arch Linux release
+ engineering team. These are typically done on a monthly cadence, containing
+ the latest kernel and base packages from the package repositories. Click
+ the version of each release to read any additional notes or details about
+ each release.</p>
+ <p>Torrents and magnet URIs are available to download the releases.
+ Releases listed as not available may still be seeded by peers, but are no
+ longer registered via the official Arch Linux torrent tracker. We always
+ recommend running the latest available release.</p>
+
<table id="release-table" class="results">
<thead>
<tr>
+ <th style="width: 30px;"></th>
<th>Release Date</th>
<th>Version</th>
<th>Kernel Version</th>
<th>Available?</th>
- <th>Torrent</th>
- <th>Magnet</th>
<th>Download Size</th>
</tr>
</thead>
<tbody>
{% for item in release_list %}
<tr class="{% cycle 'odd' 'even' %}">
+ <td>{% if item.torrent_data %}
+ <a href="{% url 'releng-release-torrent' item.version %}"
+ title="Download torrent for {{ item.version }}"><img width="12" height="12" src="{% static "download.png" %}" alt="Torrent"/></a>
+ &nbsp;
+ <a href="{{ item.magnet_uri }}"
+ title="Get magnet link for {{ item.version }}"><img width="12" height="12" src="{% static "magnet.png" %}" alt="Magnet"/></a>
+ {% endif %}</td>
<td>{{ item.release_date|date }}</td>
<td><a href="{{ item.get_absolute_url }}" title="Release details for {{ item.version }}">{{ item.version }}</a></td>
<td>{{ item.kernel_version|default:"" }}</td>
<td class="available-{{ item.available|yesno }}">{{ item.available|yesno|capfirst }}</td>
- <td>{% if item.available %}<a href="{% url 'releng-release-torrent' item.version %}"
- title="Download torrent for {{ item.version }}">Torrent</a>{% endif %}</td>
- <td>{% if item.available %}<a href="{{ item.magnet_uri }}">Magnet</a>{% endif %}</td>
<td>{% if item.torrent_data %}{{ item.torrent.file_length|filesizeformat }}{% endif %}</td>
</tr>
{% endfor %}
@@ -50,7 +63,7 @@ $(document).ready(function() {
$(".results").tablesorter({
widgets: ['zebra'],
sortList: [[0,1], [1,1]],
- headers: { 4: { sorter: false }, 5: { sorter: false } }
+ headers: { 0: { sorter: false } }
});
});
</script>
diff --git a/templates/sitemaps/news_sitemap.xml.jinja b/templates/sitemaps/news_sitemap.xml.jinja
new file mode 100644
index 00000000..97dd17b5
--- /dev/null
+++ b/templates/sitemaps/news_sitemap.xml.jinja
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
+{% for url in urlset %}<url>
+<loc>{{ url.location }}</loc>
+{% if url.lastmod %}<lastmod>{{ url.lastmod|date("Y-m-d") }}</lastmod>{% endif %}
+{% if url.changefreq %}<changefreq>{{ url.changefreq }}</changefreq>{% endif %}
+{% if url.priority %}<priority>{{ url.priority }}</priority>{% endif %}
+<news:news>
+ <news:publication><news:name>Arch Linux News</news:name><news:language>en</news:language></news:publication>
+ {% if url.item.postdate %}<news:publication_date>{{ url.item.postdate|date("c") }}</news:publication_date>{% endif %}
+ {% if url.item.title %}<news:title>{{ url.item.title }}</news:title>{% endif %}
+</news:news>
+</url>{% endfor %}
+</urlset>
diff --git a/templates/sitemaps/sitemap.xml.jinja b/templates/sitemaps/sitemap.xml.jinja
new file mode 100644
index 00000000..0808a7de
--- /dev/null
+++ b/templates/sitemaps/sitemap.xml.jinja
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+{% for url in urlset %}<url>
+<loc>{{ url.location }}</loc>
+{% if url.lastmod %}<lastmod>{{ url.lastmod|date("Y-m-d") }}</lastmod>{% endif %}
+{% if url.changefreq %}<changefreq>{{ url.changefreq }}</changefreq>{% endif %}
+{% if url.priority %}<priority>{{ url.priority }}</priority>{% endif %}
+</url>{% endfor %}
+</urlset>
diff --git a/templates/todolists/list.html b/templates/todolists/list.html
index 0f1ccfd7..5cfd6a02 100644
--- a/templates/todolists/list.html
+++ b/templates/todolists/list.html
@@ -16,7 +16,10 @@
<p>Todo lists are used by the developers when a rebuild of a set of
packages is needed. This is common when a library has a version bump,
during a toolchain rebuild, or a general cleanup of packages in the
- repositories. The progress can be tracked here.</p>
+ repositories. The progress can be tracked here, and completed todo lists
+ can be browsed as well.</p>
+
+ {% include "todolists/paginator.html" %}
<table id="dev-todo-lists" class="results todo-table">
<thead>
@@ -46,6 +49,8 @@
{% endfor %}
</tbody>
</table>
+
+ {% include "todolists/paginator.html" %}
</div>
{% endblock %}
diff --git a/templates/todolists/paginator.html b/templates/todolists/paginator.html
new file mode 100644
index 00000000..3b077419
--- /dev/null
+++ b/templates/todolists/paginator.html
@@ -0,0 +1,22 @@
+{% if is_paginated %}
+<div class="pagination">
+ <p>{{ paginator.count }} todo lists, viewing page {{ page_obj.number }} of {{ paginator.num_pages }}.</p>
+ <p class="todolist-nav">
+ {% if page_obj.has_previous %}
+ <a class="prev" href="?page={{ page_obj.previous_page_number }}"
+ title="Go to previous page">&lt; Prev</a>
+ {% endif %}
+ {% for num in paginator.page_range %}
+ {% ifequal num page_obj.number %}
+ <span>{{ num }}</span>
+ {% else %}
+ <a href="?page={{ num }}" title="Go to page {{ num }}">{{ num }}</a>
+ {% endifequal %}
+ {% endfor %}
+ {% if page_obj.has_next %}
+ <a class="next" href="?page={{ page_obj.next_page_number }}"
+ title="Go to next page">Next &gt;</a>
+ {% endif %}
+ </p>
+</div>
+{% endif %}
diff --git a/todolists/urls.py b/todolists/urls.py
index 6617d7dd..ed065f50 100644
--- a/todolists/urls.py
+++ b/todolists/urls.py
@@ -1,11 +1,11 @@
from django.conf.urls import patterns
from django.contrib.auth.decorators import permission_required
-from .views import (view_redirect, view, todolist_list, add, edit, flag,
- list_pkgbases, DeleteTodolist)
+from .views import (view_redirect, view, add, edit, flag,
+ list_pkgbases, DeleteTodolist, TodolistListView)
urlpatterns = patterns('',
- (r'^$', todolist_list),
+ (r'^$', TodolistListView.as_view(), {}, 'todolist-list'),
# old todolists URLs, permanent redirect view so we don't break all links
(r'^(?P<old_id>\d+)/$', view_redirect),
diff --git a/todolists/views.py b/todolists/views.py
index cdbfa702..db6f20f0 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -8,7 +8,7 @@ from django.shortcuts import (get_list_or_404, get_object_or_404,
redirect, render)
from django.db import transaction
from django.views.decorators.cache import never_cache
-from django.views.generic import DeleteView
+from django.views.generic import DeleteView, ListView
from django.template import Context, loader
from django.utils.timezone import now
@@ -92,10 +92,13 @@ def list_pkgbases(request, slug, svn_root):
return HttpResponse('\n'.join(pkgbases), content_type='text/plain')
-def todolist_list(request):
- incomplete_only = request.user.is_anonymous()
- lists = get_annotated_todolists(incomplete_only)
- return render(request, 'todolists/list.html', {'lists': lists})
+class TodolistListView(ListView):
+ context_object_name = "lists"
+ template_name = "todolists/list.html"
+ paginate_by = 50
+
+ def get_queryset(self):
+ return get_annotated_todolists()
@never_cache
diff --git a/urls.py b/urls.py
index 81b45627..04820713 100644
--- a/urls.py
+++ b/urls.py
@@ -16,8 +16,11 @@ our_sitemaps = {
'package-groups': sitemaps.PackageGroupsSitemap,
'split-packages': sitemaps.SplitPackagesSitemap,
'releases': sitemaps.ReleasesSitemap,
+ 'todolists': sitemaps.TodolistSitemap,
}
+news_sitemaps = { 'news': sitemaps.RecentNewsSitemap }
+
urlpatterns = []
# Public pages
@@ -78,7 +81,12 @@ urlpatterns += patterns('',
{'sitemaps': our_sitemaps, 'sitemap_url_name': 'sitemaps'}),
(r'^sitemap-(?P<section>.+)\.xml$',
cache_page(1831)(sitemap_views.sitemap),
- {'sitemaps': our_sitemaps}, 'sitemaps'),
+ {'sitemaps': our_sitemaps, 'template_name': 'sitemaps/sitemap.xml.jinja'},
+ 'sitemaps'),
+ (r'^news-sitemap\.xml$',
+ cache_page(1831)(sitemap_views.sitemap),
+ {'sitemaps': news_sitemaps, 'template_name': 'sitemaps/news_sitemap.xml.jinja'},
+ 'news-sitemap'),
)
# Authentication / Admin
diff --git a/visualize/static/visualize.js b/visualize/static/visualize.js
index 6f254f29..32e2a304 100644
--- a/visualize/static/visualize.js
+++ b/visualize/static/visualize.js
@@ -195,7 +195,7 @@ function developer_keys(chart_id, data_url) {
}
});
jQuery.map(json.nodes, function(d, i) {
- if (d.group === "dev" || d.group === "tu") {
+ if (d.group === "packager") {
d.approved = d.master_sigs >= 3;
} else {
d.approved = null;
@@ -220,8 +220,7 @@ function developer_keys(chart_id, data_url) {
return r * 1.6 - 0.75;
case "cacert":
return r * 1.4 - 0.75;
- case "dev":
- case "tu":
+ case "packager":
default:
return r - 0.75;
}