\w+)/$',
ActivationView.as_view(
template_name='registration/activate.html'
), name='registration_activate'),
url(r'^register/$',
RegistrationView.as_view(
template_name='registration/registration_form.html'
),
name='registration_register'),
url(r'^register/complete/$',
TemplateView.as_view(
template_name='registration/registration_complete.html'
), name='registration_complete'),
url(r'^register/closed/$',
TemplateView.as_view(
template_name='registration/registration_closed.html'
), name='registration_disallowed'),
url(r'', include('registration.auth_urls')),
)
PK }H6v wafer/registration/views.pyimport urllib
from django.contrib.auth import login
from django.contrib.auth.views import redirect_to_login
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.conf import settings
from django.http import Http404, HttpResponseRedirect
from wafer.registration.sso import SSOError, debian_sso, github_sso
def redirect_profile(request):
'''
The default destination from logging in, redirect to the actual profile URL
'''
if request.user.is_authenticated():
return HttpResponseRedirect(reverse('wafer_user_profile',
args=(request.user.username,)))
else:
return redirect_to_login(next=reverse(redirect_profile))
def github_login(request):
if 'github' not in settings.WAFER_SSO:
raise Http404()
if 'code' not in request.GET:
return HttpResponseRedirect(
'https://github.com/login/oauth/authorize?' + urllib.urlencode({
'client_id': settings.WAFER_GITHUB_CLIENT_ID,
'redirect_uri': request.build_absolute_uri(
reverse(github_login)),
'scope': 'user:email',
'state': request.META['CSRF_COOKIE'],
}))
try:
if request.GET['state'] != request.META['CSRF_COOKIE']:
raise SSOError('Incorrect state')
user = github_sso(request.GET['code'])
except SSOError as e:
messages.error(request, u'%s' % e)
return HttpResponseRedirect(reverse('auth_login'))
login(request, user)
return redirect_profile(request)
def debian_login(request):
if 'debian' not in settings.WAFER_SSO:
raise Http404()
try:
user = debian_sso(request.META)
except SSOError as e:
messages.error(request, u'%s' % e)
return HttpResponseRedirect(reverse('auth_login'))
login(request, user)
return redirect_profile(request)
PK }H wafer/registration/forms.pyfrom django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
class RegistrationFormHelper(FormHelper):
form_action = reverse('registration_register')
def __init__(self, *args, **kwargs):
super(RegistrationFormHelper, self).__init__(*args, **kwargs)
self.add_input(Submit('submit', _('Sign up')))
class LoginFormHelper(FormHelper):
form_action = reverse('django.contrib.auth.views.login')
def __init__(self, *args, **kwargs):
super(LoginFormHelper, self).__init__(*args, **kwargs)
# TODO: next field
self.add_input(Submit('submit', _('Log in')))
PK v|G wafer/registration/apps.py# Needed due to django 1.7 changed app name restrictions
from django.apps import AppConfig
class RegistrationConfig(AppConfig):
label = 'wafer.registration'
name = 'wafer.registration'
PK OIG
B B wafer/registration/__init__.pydefault_app_config = 'wafer.registration.apps.RegistrationConfig'
PK (Hp wafer/registration/sso.py# coding: utf-8
import logging
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
from django.db import IntegrityError
import requests
from wafer.kv.models import KeyValue
MAX_APPEND = 20
log = logging.getLogger(__name__)
class SSOError(Exception):
pass
def sso(user, desired_username, name, email, profile_fields=None):
"""
Create a user, if the provided `user` is None, from the parameters.
Then log the user in, and return it.
"""
if not user:
user = _create_desired_user(desired_username)
_configure_user(user, name, email, profile_fields)
if not user.is_active:
raise SSOError('Account disabled')
# login() expects the logging in backend to be set on the user.
# We are bypassing login, so fake it.
user.backend = settings.AUTHENTICATION_BACKENDS[0]
return user
def _create_desired_user(desired_username):
for append in xrange(MAX_APPEND):
username = desired_username
if append:
username += str(append)
try:
return get_user_model().objects.create(username=username)
except IntegrityError:
continue
log.warning('Ran out of possible usernames for %s', desired_username)
raise SSOError('Ran out of possible usernames for %s' % desired_username)
def _configure_user(user, name, email, profile_fields):
if name:
user.first_name, user.last_name = name
for attr in ('first_name', 'last_name'):
max_length = get_user_model()._meta.get_field(attr).max_length
if len(getattr(user, attr)) > max_length:
setattr(user, attr, getattr(user, attr)[:max_length - 1] + u'…')
user.email = email
user.save()
profile = user.userprofile
if profile_fields:
for k, v in profile_fields.items():
setattr(profile, k, v)
profile.save()
def github_sso(code):
r = requests.post('https://github.com/login/oauth/access_token', data={
'client_id': settings.WAFER_GITHUB_CLIENT_ID,
'client_secret': settings.WAFER_GITHUB_CLIENT_SECRET,
'code': code,
})
if r.status_code != 200:
log.warning('Response %s from api.github.com', r.status_code)
raise SSOError('Invalid code')
token = r.content
r = requests.get('https://api.github.com/user?%s' % token)
if r.status_code != 200:
log.warning('Response %s from api.github.com', r.status_code)
raise SSOError('Failed response from GitHub')
gh = r.json()
try:
login = gh['login']
name = gh['name'].partition(' ')[::2]
except KeyError as e:
log.warning('Error creating account from github information: %s', e)
raise SSOError('GitHub profile missing required content')
email = gh.get('email', None)
if not email: # No public e-mail address
r = requests.get('https://api.github.com/user/emails?%s' % token)
if r.status_code != 200:
log.warning('Response %s from api.github.com', r.status_code)
raise SSOError('Failed response from GitHub')
try:
email = r.json()[0]['email']
except (KeyError, IndexError) as e:
log.warning('Error extracting github email address: %s', e)
raise SSOError('Failed to obtain email address from GitHub')
profile_fields = {
'github_username': login,
}
if 'blog' in gh:
profile_fields['blog'] = gh['blog']
try:
user = get_user_model().objects.get(userprofile__github_username=login)
except MultipleObjectsReturned:
log.warning('Multiple accounts have GitHub username %s', login)
raise SSOError('Multiple accounts have GitHub username %s' % login)
except ObjectDoesNotExist:
user = None
user = sso(user=user, desired_username=login, name=name, email=email,
profile_fields=profile_fields)
return user
def debian_sso(meta):
authentication_status = meta.get('SSL_CLIENT_VERIFY', None)
if authentication_status != "SUCCESS":
raise SSOError('Requires authentication via Client Certificate. '
'Obtain one from https://sso.debian.org/spkac/')
email = meta['SSL_CLIENT_S_DN_CN']
group = Group.objects.get_by_natural_key('Registration')
user = None
for kv in KeyValue.objects.filter(
group=group, key='debian_sso_email', value=email,
userprofile__isnull=False):
if kv.userprofile_set.count() > 1:
message = 'Multiple accounts have Debian SSOed with address %s'
log.warning(message, email)
raise SSOError(message % email)
user = kv.userprofile_set.first().user
break
username = email.split('@', 1)[0]
name = ('Unknown User', username)
if not user:
r = requests.get('https://nm.debian.org/api/people',
params={'uid': username},
headers={'Api-Key': settings.WAFER_DEBIAN_NM_API_KEY})
if r.status_code != 200:
log.warning('Response %s from nm.debian.org', r.status_code)
raise SSOError('Failed to query nm.debian.org')
if 'r' not in r.json():
log.warning('Error parsing nm.debian.org response: %r', r.json())
raise SSOError('Failed to parse nm.debian.org respnose')
# The API performs substring queries, so we need to find the correct
# entry in the response.
for person in r.json()['r']:
if person['uid'] == username:
first_name = person['cn']
if person['mn']:
first_name += u' ' + person['mn']
last_name = person['sn']
name = (first_name, last_name)
break
user = sso(user=user, desired_username=username, name=name, email=email)
user.userprofile.kv.get_or_create(group=group, key='debian_sso_email',
defaults={'value': email})
return user
PK OIG$ B wafer/registration/templates/registration/activation_complete.html{% extends 'wafer/base.html' %}
{% load i18n %}
{% block content %}
{% trans 'Activation complete' %}
{% url 'auth_login' as login_url %}
{% blocktrans %}
You are registered!
Please log in.
{% endblocktrans %}
{% endblock %}
PK OIGҽx} 5 wafer/registration/templates/registration/logout.html{% extends 'wafer/base.html' %}
{% load i18n %}
{% block content %}
{% trans 'Logged out' %}
{% blocktrans %}
You've been logged out.
{% endblocktrans %}
{% endblock %}
PK }He9 @ wafer/registration/templates/registration/registration_form.html{% extends 'wafer/base.html' %}
{% load i18n %}
{% load crispy_forms_tags %}
{% load wafer_crispy %}
{% block content %}
{% trans 'Sign up' %}
{% wafer_form_helper 'wafer.registration.forms.RegistrationFormHelper' as form_helper %}
{% crispy form form_helper %}
{% endblock %}
PK OIGx;\ \ D wafer/registration/templates/registration/registration_complete.html{% extends 'wafer/base.html' %}
{% load i18n %}
{% block content %}
{% trans 'Registration almost complete' %}
{% blocktrans %}
Thank you for registering for {{ WAFER_CONFERENCE_NAME }}.
We have sent you an e-mail.
Please follow the instructions in it to complete your registration.
{% endblocktrans %}
{% endblock %}
PK OIG@/ > wafer/registration/templates/registration/activation_email.txt{% load i18n %}
{% spaceless %}
{% url 'registration_activate' activation_key as activation_url %}
{% url 'index' as index_url %}
{% endspaceless %}{% blocktrans with WAFER_CONFERENCE_NAME=site.name domain=site.domain %}Hi,
TL;DR Click https://{{ domain }}{{ activation_url }}
Thank you for signing up for {{ WAFER_CONFERENCE_NAME }}.
To verify your e-mail address, please follow the link above, and complete your
registration process. You have {{ expiration_days }} days left to do so.
We hope to see you soon.
Thanks,
The {{ WAFER_CONFERENCE_NAME }} website.
--
{{ WAFER_CONFERENCE_NAME }}
http://{{ domain }}{{ index_url }}
{% endblocktrans %}
PK v|G'O֦) ) 4 wafer/registration/templates/registration/login.html{% extends 'wafer/base.html' %}
{% load i18n %}
{% load crispy_forms_tags %}
{% load wafer_crispy %}
{% block content %}
{% trans 'Log in' %}
{% wafer_form_helper 'wafer.registration.forms.LoginFormHelper' as form_helper %}
{% crispy form form_helper %}
{% trans 'Sign up' %}
{% url 'registration_register' as signup_url %}
{% blocktrans %}
Not registered? Please
sign up.
{% endblocktrans %}
{% if WAFER_SSO %}
{% trans 'Shared/Social Log in and Sign up' %}
{% if 'github' in WAFER_SSO %}
{% url 'wafer.registration.views.github_login' as github_sso_url %}
- GitHub
{% endif %}
{% if 'debian' in WAFER_SSO %}
{% url 'wafer.registration.views.debian_login' as debian_sso_url %}
- Debian SSO
{% endif %}
{% endif %}
{% endblock %}
PK OIGY® F wafer/registration/templates/registration/activation_email_subject.txt{% load i18n %}
{% blocktrans with WAFER_CONFERENCE_NAME=site.name %}
Activate your {{ WAFER_CONFERENCE_NAME }} account [{{ expiration_days }} days left]
{% endblocktrans %}
PK OIG8ׁ 7 wafer/registration/templates/registration/activate.html{% extends 'wafer/base.html' %}
{% load i18n %}
{% block content %}
{% trans 'Oops, Registration failed' %}
{% url 'registration_register' as register_url %}
{% url 'wafer_page' 'contact' as contact_url %}
{% blocktrans %}
We seem to have had some difficulty with our registration.
Please try again
or contact our admins.
{% endblocktrans %}
{% endblock %}
PK v|G' @ wafer/registration/templates/registration/registration_base.html{% extends 'wafer/base.html' %}
PK OIG + wafer/registration/templatetags/__init__.pyPK OIGx / wafer/registration/templatetags/wafer_crispy.pyfrom django import template
import sys
register = template.Library()
@register.assignment_tag
def wafer_form_helper(helper_name):
'''
Find the specified Crispy FormHelper and instantiate it.
Handy when you are crispyifying other apps' forms.
'''
module, class_name = helper_name.rsplit('.', 1)
if module not in sys.modules:
__import__(module)
mod = sys.modules[module]
class_ = getattr(mod, class_name)
return class_()
PK OIG ) wafer/registration/migrations/__init__.pyPK }H wafer/compare/__init__.pyPK yItM M wafer/compare/admin.py# hack'ish support for comparing a django reversion
# history object with the current state
from diff_match_patch import diff_match_patch
import datetime
from reversion.admin import VersionAdmin
from reversion.models import Version
from django.conf.urls import url
from django.shortcuts import get_object_or_404, render
from django.contrib.admin.utils import unquote, quote
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from django.utils.encoding import force_text
from django.contrib.admin import SimpleListFilter
from django.contrib.contenttypes.models import ContentType
from markitup.fields import Markup
class DateModifiedFilter(SimpleListFilter):
title = _('Last Modified')
parameter_name = 'moddate'
def lookups(self, request, model_admin):
return (
('today', _('Today')),
('yesterday', _('Since yesterday')),
('7 days', _('In the last 7 days')),
('30 days', _('In the last 30 days')),
)
def queryset(self, request, queryset):
date = None
if self.value() == 'today':
date = datetime.date.today()
elif self.value() == 'yesterday':
date = datetime.date.today() - datetime.timedelta(days=1)
elif self.value() == '7 days':
date = datetime.date.today() - datetime.timedelta(days=7)
elif self.value() == '30 days':
date = datetime.date.today() - datetime.timedelta(days=30)
if not date:
return queryset
content_type = ContentType.objects.get_for_model(queryset.model)
revisions = Version.objects.filter(content_type=content_type, revision__date_created__gte=date)
return queryset.filter(pk__in=[x.object_id for x in revisions])
def get_date(revision):
return revision.revision.date_created.strftime("%Y-%m-%d %H:%m:%S")
def get_author(revision):
if revision.revision.user:
return revision.revision.user.username
return 'Unknown'
def make_diff(current, revision):
"""Create the difference between the current revision and a previous version"""
the_diff = []
dmp = diff_match_patch()
for field in (set(current.field_dict.keys()) | set(revision.field_dict.keys())):
# These exclusions really should be configurable
if field == 'id' or field.endswith('_rendered'):
continue
# KeyError's may happen if the database structure changes
# between the creation of revisions. This isn't ideal,
# but should not be a fatal error.
# Log this?
missing_field = False
try:
cur_val = current.field_dict[field] or ""
except KeyError:
cur_val = "No such field in latest version\n"
missing_field = True
try:
old_val = revision.field_dict[field] or ""
except KeyError:
old_val = "No such field in old version\n"
missing_field = True
if missing_field:
# Ensure that the complete texts are marked as changed
# so new entries containing any of the marker words
# don't show up as differences
diffs = [(dmp.DIFF_DELETE, old_val), (dmp.DIFF_INSERT, cur_val)]
patch = dmp.diff_prettyHtml(diffs)
elif isinstance(cur_val, Markup):
# we roll our own diff here, so we can compare of the raw
# markdown, rather than the rendered result.
if cur_val.raw == old_val.raw:
continue
diffs = dmp.diff_main(old_val.raw, cur_val.raw)
patch = dmp.diff_prettyHtml(diffs)
elif cur_val == old_val:
continue
else:
# Compare the actual field values
diffs = dmp.diff_main(force_text(old_val), force_text(cur_val))
patch = dmp.diff_prettyHtml(diffs)
the_diff.append((field, patch))
the_diff.sort()
return the_diff
class CompareVersionAdmin(VersionAdmin):
compare_template = "admin/wafer.compare/compare.html"
compare_list_template = "admin/wafer.compare/compare_list.html"
# Add a compare button next to the History button.
change_form_template = "admin/wafer.compare/change_form.html"
def get_urls(self):
urls = super(CompareVersionAdmin, self).get_urls()
opts = self.model._meta
compare_urls = [
url("^([^/]+)/([^/]+)/compare/$", self.admin_site.admin_view(self.compare_view),
name='%s_%s_compare' % (opts.app_label, opts.model_name)),
url("^([^/]+)/comparelist/$", self.admin_site.admin_view(self.comparelist_view),
name='%s_%s_comparelist' % (opts.app_label, opts.model_name)),
]
return compare_urls + urls
def compare_view(self, request, object_id, version_id, extra_context=None):
"""Actually compare two versions."""
opts = self.model._meta
object_id = unquote(object_id)
# get_for_object's ordering means this is always the latest revision.
# The reversion we want to compare to
current = Version.objects.get_for_object_reference(self.model, object_id)[0]
revision = Version.objects.get_for_object_reference(self.model, object_id).filter(id=version_id)[0]
the_diff = make_diff(current, revision)
context = {
"title": _("Comparing current %(model)s with revision created %(date)s") % {
'model': current,
'date' : get_date(revision),
},
"opts": opts,
"compare_list_url": reverse("%s:%s_%s_comparelist" % (self.admin_site.name, opts.app_label, opts.model_name),
args=(quote(object_id),)),
"diff_list": the_diff,
}
extra_context = extra_context or {}
context.update(extra_context)
return render(request, self.compare_template or self._get_template_list("compare.html"),
context)
def comparelist_view(self, request, object_id, extra_context=None):
"""Allow selecting versions to compare."""
opts = self.model._meta
object_id = unquote(object_id)
current = get_object_or_404(self.model, pk=object_id)
# As done by reversion's history_view
action_list = [
{
"revision": version.revision,
"url": reverse("%s:%s_%s_compare" % (self.admin_site.name, opts.app_label, opts.model_name), args=(quote(version.object_id), version.id)),
} for version in self._reversion_order_version_queryset(Version.objects.get_for_object_reference(
self.model,
object_id).select_related("revision__user"))]
context = {"action_list": action_list,
"opts": opts,
"object_id": quote(object_id),
"original": current,
}
extra_context = extra_context or {}
context.update(extra_context)
return render(request, self.compare_list_template or self._get_template_list("compare_list.html"),
context)
PK }H8, , 8 wafer/compare/templates/admin/wafer.compare/compare.html{% extends "admin/base_site.html" %}
{% load i18n l10n admin_urls %}
{% block breadcrumbs %}
Compare ›
{% blocktrans with opts.verbose_name_plural|escape as name %}{{name}}{% endblocktrans %}
{% endblock %}
{% block content %}
{% for field, diff in diff_list %}
{{ field }}
{{ diff | safe }}
{% empty %}
{% trans 'No differences found' %}
{% endfor %}
{% endblock %}
PK }H{9t t <