From 37e278e43ece42375352c07d9f04991ef58d992c Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C5=81ukasz=20Rekucki?= Date: Sat, 17 Apr 2010 23:59:47 +0200 Subject: [PATCH] Profile edit and password change views. Better login/logout templates. Some polish translations. --- provider/cas_provider/models.py | 8 +-- provider/cas_provider/views.py | 57 ++++++++--------- provider/setup.py | 2 +- src/accounts/__init__.py | 0 src/accounts/forms.py | 26 ++++++++ .../locale/pl_PL/LC_MESSAGES/django.mo | Bin 0 -> 825 bytes .../locale/pl_PL/LC_MESSAGES/django.po | 49 +++++++++++++++ src/accounts/models.py | 3 + src/accounts/templates/account/profile.html | 41 +++++++++++++ src/accounts/templatetags/__init__.py | 0 src/accounts/templatetags/gravatar.py | 44 +++++++++++++ src/accounts/tests.py | 23 +++++++ src/accounts/urls.py | 8 +++ src/accounts/views.py | 41 +++++++++++++ src/cas/manage.py | 12 ++-- src/cas/media/static/css/main.css | 58 ++++++++++++++++++ src/cas/media/static/redmine_logo.png | Bin 0 -> 8827 bytes src/cas/settings.py | 12 ++-- .../templates/{cas_base.html => base.html} | 5 +- src/cas/templates/cas/login.html | 32 +++++----- src/cas/templates/cas/logout.html | 23 ++++--- src/cas/templates/horizontal_footer.html | 9 +++ src/cas/urls.py | 14 ++++- 23 files changed, 390 insertions(+), 77 deletions(-) create mode 100644 src/accounts/__init__.py create mode 100644 src/accounts/forms.py create mode 100644 src/accounts/locale/pl_PL/LC_MESSAGES/django.mo create mode 100644 src/accounts/locale/pl_PL/LC_MESSAGES/django.po create mode 100644 src/accounts/models.py create mode 100644 src/accounts/templates/account/profile.html create mode 100644 src/accounts/templatetags/__init__.py create mode 100644 src/accounts/templatetags/gravatar.py create mode 100644 src/accounts/tests.py create mode 100644 src/accounts/urls.py create mode 100644 src/accounts/views.py mode change 100644 => 100755 src/cas/manage.py create mode 100644 src/cas/media/static/css/main.css create mode 100644 src/cas/media/static/redmine_logo.png rename src/cas/templates/{cas_base.html => base.html} (73%) create mode 100644 src/cas/templates/horizontal_footer.html diff --git a/provider/cas_provider/models.py b/provider/cas_provider/models.py index 5d09912..6e7ebaf 100644 --- a/provider/cas_provider/models.py +++ b/provider/cas_provider/models.py @@ -10,14 +10,14 @@ class ServiceTicket(models.Model): service = models.URLField(verify_exists=False) ticket = models.CharField(max_length=256) created = models.DateTimeField(auto_now=True) - + def __unicode__(self): return "%s (%s) - %s" % (self.user.username, self.service, self.created) - + class LoginTicket(models.Model): ticket = models.CharField(max_length=32) created = models.DateTimeField(auto_now=True) - + def __unicode__(self): return "%s - %s" % (self.ticket, self.created) @@ -30,7 +30,7 @@ def auth_success_response(user): if settings.CAS_CUSTOM_ATTRIBUTES_CALLBACK: callback = get_callable(settings.CAS_CUSTOM_ATTRIBUTES_CALLBACK) attrs = callback(user) - + response = ElementRoot(CAS + 'serviceResponse') auth_success = etree.SubElement(response, CAS + 'authenticationSuccess') username = etree.SubElement(auth_success, CAS + 'user') diff --git a/provider/cas_provider/views.py b/provider/cas_provider/views.py index 2733eb3..e2466b8 100644 --- a/provider/cas_provider/views.py +++ b/provider/cas_provider/views.py @@ -4,6 +4,7 @@ from django.template import RequestContext from django.contrib.auth.models import User from django.contrib.auth import authenticate from django.contrib.auth import login as auth_login, logout as auth_logout +from django.utils.translation import ugettext_lazy as _ from cas_provider.forms import LoginForm from cas_provider.models import ServiceTicket, LoginTicket, auth_success_response @@ -15,14 +16,13 @@ try: from urlparse import parse_qs as url_parse_qs except ImportError: from cgi import parse_qs as url_parse_qs - import logging -logger = logging.getLogger("fnp.cas.provider") +logger = logging.getLogger("cas.provider") __all__ = ['login', 'validate', 'service_validate', 'logout'] -def _add_query_param(url, param, value): +def _add_query_param(url, param, value): parsed = urlparse.urlparse(url) query = url_parse_qs(parsed.query) query[param] = [unicode(value, 'utf-8')] @@ -33,12 +33,10 @@ def _add_query_param(url, param, value): return parsed.geturl() -def login(request, template_name = 'cas/login.html', success_redirect = '/accounts/'): +def login(request, template_name='cas/login.html', success_redirect='/accounts/'): service = request.GET.get('service', None) - + if request.user.is_authenticated(): - logger.info("User %s passed auth, service is %s", request.user, service) - if service is not None: ticket = create_service_ticket(request.user, service) target = _add_query_param(service, 'ticket', ticket.ticket) @@ -47,53 +45,46 @@ def login(request, template_name = 'cas/login.html', success_redirect = '/accoun else: logger.info("Redirecting to default: %s", success_redirect) return HttpResponseRedirect(success_redirect) - + errors = [] if request.method == 'POST': username = request.POST.get('username', None) password = request.POST.get('password', None) service = request.POST.get('service', None) lt = request.POST.get('lt', None) - + logger.debug("User %s logging in", username) - logger.info("Login submit: serivce = %s, Lticket=%s",service, lt) try: - login_ticket = LoginTicket.objects.get(ticket = lt) + login_ticket = LoginTicket.objects.get(ticket=lt) except: - errors.append('Login ticket expired. Please try again.') + errors.append(_(u'Login ticket expired. Please try again.')) else: login_ticket.delete() - logger.debug("Auth") - user = authenticate(username = username, password = password) + user = authenticate(username=username, password=password) if user is not None: if user.is_active: - logger.debug("AuthLogin") auth_login(request, user) - if service is not None: + if service is not None: ticket = create_service_ticket(user, service) - logger.info("Service=%s, ticket=%s", service, ticket) target = _add_query_param(service, 'ticket', ticket.ticket) - logger.info("Redirecting to %s", target) return HttpResponseRedirect(target) else: - logger.info("Redirecting to default: %s", success_redirect) return HttpResponseRedirect(success_redirect) else: - errors.append('This account is disabled.') + errors.append(_(u'This account is disabled.')) else: - errors.append('Incorrect username and/or password.') - - logger.debug("LOGIN GET, service = %s", service) + errors.append(_(u'Incorrect username and/or password.')) + form = LoginForm(service) - return render_to_response(template_name, {'form': form, 'errors': errors}, context_instance = RequestContext(request)) + return render_to_response(template_name, {'form': form, 'errors': errors}, context_instance=RequestContext(request)) def validate(request): service = request.GET.get('service', None) ticket_string = request.GET.get('ticket', None) if service is not None and ticket_string is not None: try: - ticket = ServiceTicket.objects.get(ticket = ticket_string) + ticket = ServiceTicket.objects.get(ticket=ticket_string) username = ticket.user.username ticket.delete() return HttpResponse("yes\n%s\n" % username) @@ -105,24 +96,24 @@ def service_validate(request): service = request.GET.get('service', None) ticket_string = request.GET.get('ticket', None) if service is None or ticket_string is None: - return HttpResponse(''' + return HttpResponse(r''' Not all required parameters were sent. - ''', mimetype = 'text/xml') + ''', mimetype='application/xml') try: - ticket = ServiceTicket.objects.get(ticket = ticket_string) + ticket = ServiceTicket.objects.get(ticket=ticket_string) ticket.delete() - return HttpResponse(auth_success_response(ticket.user), mimetype = 'text/xml') + return HttpResponse(auth_success_response(ticket.user), mimetype='text/xml') except ServiceTicket.DoesNotExist: - return HttpResponse(''' + return HttpResponse(r''' The provided ticket is invalid. - ''', mimetype = 'text/xml') + ''', mimetype='application/xml') -def logout(request, template_name = 'cas/logout.html'): +def logout(request, template_name='cas/logout.html'): url = request.GET.get('url', None) auth_logout(request) - return render_to_response(template_name, {'url': url}, context_instance = RequestContext(request)) + return render_to_response(template_name, {'url': url}, context_instance=RequestContext(request)) diff --git a/provider/setup.py b/provider/setup.py index 489f8d2..8a67235 100644 --- a/provider/setup.py +++ b/provider/setup.py @@ -1,5 +1,5 @@ from setuptools import setup, find_packages - + setup( name='django-cas-provider', version='0.2', diff --git a/src/accounts/__init__.py b/src/accounts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/accounts/forms.py b/src/accounts/forms.py new file mode 100644 index 0000000..5dcb842 --- /dev/null +++ b/src/accounts/forms.py @@ -0,0 +1,26 @@ +from django import forms +from django.contrib.auth.models import User as DjangoUser +from django.utils.translation import ugettext_lazy as _ + +class UserBasicForm(forms.ModelForm): + + class Meta: + model = DjangoUser + fields = ('first_name', 'last_name', 'email',) + + +class UserPasswordForm(forms.Form): + + new_password = forms.CharField(widget=forms.PasswordInput(), + label=_("Your new password")) + verifier = forms.CharField(widget=forms.PasswordInput(), + label=_("Repeated password")) + + def clean(self): + if 'verifier' not in self.cleaned_data or 'new_password' not in self.cleaned_data: + return self.cleaned_data + + if self.cleaned_data['verifier'] != self.cleaned_data['new_password']: + raise forms.ValidationError(_("Passwords do not match!")) + + return self.cleaned_data diff --git a/src/accounts/locale/pl_PL/LC_MESSAGES/django.mo b/src/accounts/locale/pl_PL/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..59955aea57d967fff3545284703851b908cae52d GIT binary patch literal 825 zcmZ8f%Wl&^6g9L^hkPwSocBo8osv^oPJ$Te=*+o3KKFXYKl96HM>`f}3T;t+e0{DUVNL2AZiM?=WqZx>Q6A5~}q` zs&KBAJmoR7>RBB+SH8K{5K0izK%bgUchzdJgi*u7MbM!fC=hJ)UnQ9ZE7@ZmLnZ^?eHa1#;y)**ae%| zo1{>Lg6(V;*X#}3Mk8skAy30I-=XI=NKhkX7xrsTP+2>wRlS@~wIMAV72E~o_F;-d=d zcCI9aVFG)+PjFbn-kIk~Ou2ZO9;?#KylS_0NpayzXE|kxc%maiE+PktC*0($veagB zmIwv|efeP!aqFnh+2u#*Qq*#$riYv=D(LhXwf3(pz#)+%b8)W5OmO@W_~Z=I%{Zdr fnD!>0R;_wwkk}^+ZjJD|@WMy`R4&eYkahYC!yDbl literal 0 HcmV?d00001 diff --git a/src/accounts/locale/pl_PL/LC_MESSAGES/django.po b/src/accounts/locale/pl_PL/LC_MESSAGES/django.po new file mode 100644 index 0000000..8bb2e63 --- /dev/null +++ b/src/accounts/locale/pl_PL/LC_MESSAGES/django.po @@ -0,0 +1,49 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-04-18 10:48+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:15 +msgid "Your new password" +msgstr "Twoje nowe hasło" + +#: forms.py:17 +msgid "Repeated password" +msgstr "Powtórzone hasło" + +#: forms.py:24 +msgid "Passwords do not match!" +msgstr "Hasło i powtórzenie się nie zgadzają!" + +#: templates/account/profile.html:12 +msgid "Your profile" +msgstr "Twój profil" + +#: templates/account/profile.html:16 +msgid "Change profile" +msgstr "Zmień profil" + +#: templates/account/profile.html:20 +msgid "Password change" +msgstr "Zmiana hasła" + +#: templates/account/profile.html:24 +msgid "Change password" +msgstr "Zmień hasło" + +#: templates/account/profile.html:30 +msgid "Availble services" +msgstr "Dostępne usługi" diff --git a/src/accounts/models.py b/src/accounts/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/src/accounts/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/src/accounts/templates/account/profile.html b/src/accounts/templates/account/profile.html new file mode 100644 index 0000000..15e5eaf --- /dev/null +++ b/src/accounts/templates/account/profile.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} +{% load gravatar i18n %} + +{% block content %} + + +
+ {% if messages %} +

{% for message in messages %}{{ message }}{% endfor %}

+ {% endif %} + +

{% trans "Your profile" %}

+
+ + {{ basic_form.as_table }} + +
+
+ +

{% trans "Password change" %}

+
+ + {{ pass_form.as_table }} + +
+
+
+ +
+

{% trans "Availble services" %}

+ + + + + + +
+{% endblock %} \ No newline at end of file diff --git a/src/accounts/templatetags/__init__.py b/src/accounts/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/accounts/templatetags/gravatar.py b/src/accounts/templatetags/gravatar.py new file mode 100644 index 0000000..a6db9aa --- /dev/null +++ b/src/accounts/templatetags/gravatar.py @@ -0,0 +1,44 @@ +from django import template +from django.template.defaultfilters import stringfilter +import hashlib +import urllib + +register = template.Library() + +DEFAULTS = dict(size=80, rating='g', default='monsterid') + +class GravatarNode(template.Node): + + def __init__(self, email, size): + self.email = template.Variable(email) + self.size = size + + def render(self, context): + try: + email = self.email.resolve(context) + except template.VariableDoesNotExist: + return '' + + gravatar_url = "http://www.gravatar.com/avatar/" + hashlib.md5(email).hexdigest() + "?" + gravatar_url += urllib.urlencode({'default': 'wavatar', 'size': str(self.size)}) + + return gravatar_url + +@register.tag +def gravatar(parser, token): + try: + _tag_name, email, size = token.split_contents() + except ValueError: + raise template.TemplateSyntaxError, "%r tag requires two args" % token.contents.split()[0] + + return GravatarNode(email, int(size)) + + + + +@register.filter(name='md5') +@stringfilter +def md5_hash(value): + h = hashlib.md5() + h.update(value) + return h.hexdigest() diff --git a/src/accounts/tests.py b/src/accounts/tests.py new file mode 100644 index 0000000..2247054 --- /dev/null +++ b/src/accounts/tests.py @@ -0,0 +1,23 @@ +""" +This file demonstrates two different styles of tests (one doctest and one +unittest). These will both pass when you run "manage.py test". + +Replace these with more appropriate tests for your application. +""" + +from django.test import TestCase + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.failUnlessEqual(1 + 1, 2) + +__test__ = {"doctest": """ +Another way to test that 1 + 1 is equal to 2. + +>>> 1 + 1 == 2 +True +"""} + diff --git a/src/accounts/urls.py b/src/accounts/urls.py new file mode 100644 index 0000000..86e81fa --- /dev/null +++ b/src/accounts/urls.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from django.conf.urls.defaults import * + +urlpatterns = patterns('accounts.views', + url(r'^$', 'account_profile'), + url(r'^change_profile$', 'account_change_basic_profile'), + url(r'^change_password$', 'account_change_password'), +) diff --git a/src/accounts/views.py b/src/accounts/views.py new file mode 100644 index 0000000..b00dd20 --- /dev/null +++ b/src/accounts/views.py @@ -0,0 +1,41 @@ +# Create your views here. +from django import http +from django.utils.translation import ugettext as __ +from django.views.generic.simple import direct_to_template +from django.views.decorators.http import require_POST +from django.contrib.auth.decorators import login_required +from accounts.forms import UserBasicForm, UserPasswordForm + +@login_required +def account_profile(request, basic_form=None, pass_form=None): + return direct_to_template(request, "account/profile.html", { + "basic_form": basic_form or UserBasicForm(instance=request.user), + "pass_form": pass_form or UserPasswordForm(), + }) + + +@require_POST +@login_required +def account_change_basic_profile(request): + form = UserBasicForm(request.POST, instance=request.user) + + if form.is_valid(): + form.save() + request.user.message_set.create(message=__("Profile has been changed.")) + return http.HttpResponseRedirect('/accounts/') + + return account_profile(request, basic_form=form) + +@require_POST +@login_required +def account_change_password(request): + form = UserPasswordForm(request.POST) + + if form.is_valid(): + request.user.set_password(form.cleaned_data['new_password']) + request.user.save() + + request.user.message_set.create(message=__("Password has been changed.")) + return http.HttpResponseRedirect('/accounts/') + + return account_profile(request, pass_form=form) diff --git a/src/cas/manage.py b/src/cas/manage.py old mode 100644 new mode 100755 index 741587d..43896be --- a/src/cas/manage.py +++ b/src/cas/manage.py @@ -4,20 +4,20 @@ from django.core.management import execute_manager from os import path import sys -PROJECT_ROOT = path.realpath(path.dirname(__file__)) -sys.path.insert(0, path.abspath(path.join(PROJECT_ROOT, "..", "provider"))) -print sys.path +PROJECT_ROOT = path.realpath(path.dirname(__file__)) +sys.path = [ + path.abspath(path.join(PROJECT_ROOT, '..')), + path.abspath(path.join(PROJECT_ROOT, '..', '..', "provider")), +] + sys.path try: import settings # Assumed to be in the same directory. except ImportError: import traceback - traceback.print_exc(file =sys.stderr) + traceback.print_exc(file=sys.stderr) sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) sys.exit(1) if __name__ == "__main__": # Append lib and apps directories to PYTHONPATH - - execute_manager(settings) diff --git a/src/cas/media/static/css/main.css b/src/cas/media/static/css/main.css new file mode 100644 index 0000000..b2c7b75 --- /dev/null +++ b/src/cas/media/static/css/main.css @@ -0,0 +1,58 @@ +body { + background-color: #eee; +} + +#content_wrap { + background-color: #555; + margin: 0px 5%; + padding: 5px; +} + +#content { + background-color: white; + border-radius: 10px; + margin: 5px; + padding: 0.2em 1em; + overflow: hidden; +} + +#details { + margin: 1em; + float: left; + max-width: 75%; + +} + +#details form table th { + text-align: left; +} + +#details form { + margin-bottom: 2em; +} + +.user_avatar { + float: left; + margin: 1em; + max-width: 20%; + text-align: center; +} + +.user_avatar img { + border: 1px solid black; +} + +#services-list { + width: 250px; + float: right; + margin: 1em 2em; +} + +#services-list img { + width: 250px; + margin: 1em 0em; +} + +img.small_logo { + width: 250px; +} diff --git a/src/cas/media/static/redmine_logo.png b/src/cas/media/static/redmine_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bdfabb1ca8e51b7c914b198e19a99c12c67981af GIT binary patch literal 8827 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 literal 0 HcmV?d00001 diff --git a/src/cas/settings.py b/src/cas/settings.py index acc5988..3ed14b0 100644 --- a/src/cas/settings.py +++ b/src/cas/settings.py @@ -6,7 +6,9 @@ PROJECT_ROOT = path.realpath(path.dirname(__file__)) DEBUG = True TEMPLATE_DEBUG = DEBUG -ADMINS = [] +ADMINS = [ + "lrekucki@gmail.com", +] MANAGERS = ADMINS @@ -37,13 +39,11 @@ USE_I18N = True # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" MEDIA_ROOT = PROJECT_ROOT + '/media/' -STATIC_ROOT = PROJECT_ROOT + '/static/' # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash if there is a path component (optional in other cases). # Examples: "http://media.lawrence.com", "http://example.com/media/" MEDIA_URL = '/media/' -STATIC_URL = '/static/' # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a # trailing slash. @@ -61,6 +61,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( "django.core.context_processors.auth", "django.core.context_processors.debug", "django.core.context_processors.i18n", + 'django.core.context_processors.media', "django.core.context_processors.request", ) @@ -75,7 +76,7 @@ MIDDLEWARE_CLASSES = ( ROOT_URLCONF = 'cas.urls' TEMPLATE_DIRS = ( - PROJECT_ROOT + '/templates', + PROJECT_ROOT + '/templates', ) INSTALLED_APPS = ( @@ -85,8 +86,9 @@ INSTALLED_APPS = ( 'django.contrib.sites', 'django.contrib.admin', 'django.contrib.admindocs', - + 'cas_provider', + 'accounts', ) # django-cas-provider settings diff --git a/src/cas/templates/cas_base.html b/src/cas/templates/base.html similarity index 73% rename from src/cas/templates/cas_base.html rename to src/cas/templates/base.html index a49c721..1872c06 100644 --- a/src/cas/templates/cas_base.html +++ b/src/cas/templates/base.html @@ -4,10 +4,11 @@ {% block title %}Fundacja Nowoczesna Polska - CAS{% block subtitle %}{% endblock subtitle %}{% endblock title%} + {% block extrahead %} {% endblock %} - -
{% block content %} {% endblock %}
+ +
{% block content %} {% endblock %}
diff --git a/src/cas/templates/cas/login.html b/src/cas/templates/cas/login.html index e51cd8d..7a51cac 100644 --- a/src/cas/templates/cas/login.html +++ b/src/cas/templates/cas/login.html @@ -1,21 +1,21 @@ -{% extends "cas_base.html" %} +{% extends "base.html" %} +{% load i18n %} {% block content %} +
-
- Zaloguj się - {% if errors %} -
    - {% for error in errors %} -
  • {{ error|escape }}
  • - {% endfor %} -
- {% endif %} - - {{ form.as_table }} -
-

-
+

{% trans "Login" %}

+ + {% for error in errors %} +

{{ error }}

+ {% endfor %} + + + {{ form.as_table }} + +
+
+ + {% include "horizontal_footer.html" %} {% endblock %} - \ No newline at end of file diff --git a/src/cas/templates/cas/logout.html b/src/cas/templates/cas/logout.html index d2a4009..ce3db21 100644 --- a/src/cas/templates/cas/logout.html +++ b/src/cas/templates/cas/logout.html @@ -1,12 +1,21 @@ -{% extends "cas_base.html" %} - +{% extends "base.html" %} +{% load i18n %} + {% block title %} Logged out {% endblock %} - + {% block content %} -

Logged out

- -

You have successfully logged out. To ensure that you are logged out of all services, please close your browser.

- {% if url %}

Click here to return to {{ url }}

{% endif %} +

{% trans "Logged out" %}

+ +

{% blocktrans %}You have successfully logged out. To ensure that you are logged out of all services, please close your browser.{% endblocktrans %}

+ + {% if url %} +

{% blocktrans %}You can return to service you came from: {{ url }}{% endblocktrans %}

+ {% endif %} + + {% url cas_provider.views.login as login_url %} +

{% blocktrans %}You can also login again{% endblocktrans %}

+ + {% include "horizontal_footer.html" %} {% endblock %} \ No newline at end of file diff --git a/src/cas/templates/horizontal_footer.html b/src/cas/templates/horizontal_footer.html new file mode 100644 index 0000000..4d6ab86 --- /dev/null +++ b/src/cas/templates/horizontal_footer.html @@ -0,0 +1,9 @@ +
+

+ + + + + + +

\ No newline at end of file diff --git a/src/cas/urls.py b/src/cas/urls.py index f1aeca6..4a285fd 100644 --- a/src/cas/urls.py +++ b/src/cas/urls.py @@ -1,17 +1,25 @@ # -*- coding: utf-8 -*- from django.conf.urls.defaults import * +from django.views.generic.simple import redirect_to from django.contrib import admin from django.conf import settings admin.autodiscover() urlpatterns = patterns('', + url(r'^$', redirect_to, {'url': '/accounts/'}), + + # django-cas-provider + url(r'^cas/', include('cas_provider.urls')), + # Admin panel url(r'^admin/doc/', include('django.contrib.admindocs.urls')), - url(r'^admin/(.*)', admin.site.root), + url(r'^admin/', include(admin.site.urls)), - # django-cas-provider - url(r'^', include('cas_provider.urls')), + url(r'^accounts/', include('accounts.urls')), + + url(r'^%s(?P.+)$' % settings.MEDIA_URL[1:], 'django.views.static.serve', + {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}), ) -- 2.20.1