diff --git a/debian/debian_config.py b/debian/debian_config.py index 8698793..c6a4ecc 100644 --- a/debian/debian_config.py +++ b/debian/debian_config.py @@ -23,4 +23,6 @@ METADATAS_DIR = os.path.join(VAR_DIR, 'metadatas') SECRET_KEY = file('/etc/%s/secret' % PROJECT_NAME).read() +ORGANIZATIONS_DIR = os.path.join(VAR_DIR, 'organizations') + execfile(os.path.join(ETC_DIR, 'settings.py')) diff --git a/debian/nginx-example.conf b/debian/nginx-example.conf index 37a51a8..16a3832 100644 --- a/debian/nginx-example.conf +++ b/debian/nginx-example.conf @@ -9,10 +9,11 @@ server { access_log /var/log/nginx/u-auth.example.org-access.log combined; error_log /var/log/nginx/u-auth.example.org-error.log; - location ~ ^/static/(.+)$ { + location ~ ^(.*)/static/(.+)$ { root /; - try_files /var/lib/u-auth/static/$1 - /var/lib/u-auth/collectstatic/$1 + try_files /var/lib/u-auth/organizations/$1/static/$2 + /var/lib/u-auth/static/$2 + /var/lib/u-auth/collectstatic/$2 =404; } diff --git a/debian/u-auth.dirs b/debian/u-auth.dirs index 0e857be..a998fe0 100644 --- a/debian/u-auth.dirs +++ b/debian/u-auth.dirs @@ -1,5 +1,6 @@ /etc/u-auth /usr/lib/u-auth +/var/lib/u-auth/organizations /var/lib/u-auth/collectstatic /var/lib/u-auth/static /var/lib/u-auth/templates diff --git a/uauth/context_processors.py b/uauth/context_processors.py new file mode 100644 index 0000000..a5ad489 --- /dev/null +++ b/uauth/context_processors.py @@ -0,0 +1,14 @@ +import os + +from django.conf import settings +from django.template.loader import get_template, TemplateDoesNotExist + +def theme_base(request): + if request.session.get('organization'): + try: + base = get_template('base.html', [os.path.join(settings.ORGANIZATIONS_DIR, + request.session['organization'], 'templates')]) + except TemplateDoesNotExist: + base = get_template('uauth/base.html') + + return {'theme_base': base} diff --git a/uauth/organization/forms.py b/uauth/organization/forms.py index 6bfb5b2..2c77f71 100644 --- a/uauth/organization/forms.py +++ b/uauth/organization/forms.py @@ -31,3 +31,10 @@ class LocalAccountCreateForm(LocalAccountForm): class UsersImportForm(forms.Form): users_file = forms.FileField(_('Users file')) + + +class TemplateForm(forms.Form): + template_file = forms.FileField(_('Template file')) + +class StaticForm(forms.Form): + static_file = forms.FileField(_('Static file')) diff --git a/uauth/organization/templates/organization/manage.html b/uauth/organization/templates/organization/manage.html index e47c5d3..6435f23 100644 --- a/uauth/organization/templates/organization/manage.html +++ b/uauth/organization/templates/organization/manage.html @@ -4,5 +4,6 @@ {% block content %} {% endblock %} diff --git a/uauth/organization/templates/organization/theme.html b/uauth/organization/templates/organization/theme.html new file mode 100644 index 0000000..7b5e0de --- /dev/null +++ b/uauth/organization/templates/organization/theme.html @@ -0,0 +1,45 @@ +{% extends "organization/base.html" %} +{% load i18n %} + +{% block page-title %} +{% trans 'Theme management' %} +{% endblock %} + +{% block appbar %} +

{% trans "Theme" %}

+{% trans "Upload static" %} +{% trans "Upload template" %} + +

{% trans "Templates" %}

+
+ + + + + + {% for template in templates %} + + {% empty %} + + {% endfor %} + +
{% trans "Filename" %}
{{ template }}
{% trans "No templates uploaded yet" %}
+
+ +

{% trans "Statics" %}

+
+ + + + + + {% for static in statics %} + + {% empty %} + + {% endfor %} + +
{% trans "Filename" %}
{{ static }}
{% trans "No statics uploaded yet" %}
+
+{% endblock %} + diff --git a/uauth/organization/templates/organization/upload.html b/uauth/organization/templates/organization/upload.html new file mode 100644 index 0000000..8577479 --- /dev/null +++ b/uauth/organization/templates/organization/upload.html @@ -0,0 +1,21 @@ +{% extends "organization/base.html" %} +{% load i18n %} + +{% block more-user-links %} +{{ block.super }} + {% trans 'Theme' %} +{% endblock %} + +{% block appbar %} +

{% trans "Theme" %}

+{% trans "Upload static" %} +{% trans "Upload template" %} +{% endblock %} + +{% block content %} +
+ {% csrf_token %} + {{ form.as_p }} +

+

+{% endblock %} diff --git a/uauth/organization/urls.py b/uauth/organization/urls.py index a169b26..7dd2f02 100644 --- a/uauth/organization/urls.py +++ b/uauth/organization/urls.py @@ -9,4 +9,9 @@ urlpatterns = patterns('', url(r'^users/import$', import_users, name='import-users'), url(r'^users/(?P[\w]+)/$', view_user, name='view-user'), url(r'^users/(?P[\w]+)/edit$', edit_user, name='edit-user'), + url(r'^theme/?$', theme, name='manage-theme'), + url(r'^theme/template/upload$', template_upload, name='template-upload'), + url(r'^theme/template/delete$', template_delete, name='template-delete'), + url(r'^theme/static/upload$', static_upload, name='static-upload'), + url(r'^theme/static/delete$', static_delete, name='static-delete'), ) diff --git a/uauth/organization/views.py b/uauth/organization/views.py index fb66fd2..911a7d8 100644 --- a/uauth/organization/views.py +++ b/uauth/organization/views.py @@ -1,6 +1,8 @@ +import os import csv import datetime +from django.conf import settings from django.utils.translation import ugettext as _ from django.core.urlresolvers import reverse_lazy from django.shortcuts import render, redirect @@ -15,7 +17,7 @@ from django_tables2 import RequestConfig from .utils import create_user, create_or_update_users from .models import LocalAccount, Organization -from .forms import LocalAccountCreateForm, LocalAccountForm, UsersImportForm +from .forms import * from .tables import AccountTable @@ -153,3 +155,111 @@ class ImportUsersView(OrganizationMixin, TemplateView): return self.render_to_response(context) import_users = ImportUsersView.as_view() + + +class ThemeView(OrganizationMixin, TemplateView): + template_name = 'organization/theme.html' + + def get_success_url(self): + return reverse_lazy('manage-theme', kwargs={'organization_slug': self.kwargs['organization_slug']}) + + def get_context_data(self, **kwargs): + ctx = super(ThemeView, self).get_context_data(**kwargs) + organization = ctx['organization'] + templates_dir = os.path.join(settings.ORGANIZATIONS_DIR, + organization.slug, 'templates') + statics_dir = os.path.join(settings.ORGANIZATIONS_DIR, + organization.slug, 'static') + ctx['templates'] = [] + ctx['statics'] = [] + if os.path.exists(templates_dir): + ctx['templates'] = os.listdir(templates_dir) + if os.path.exists(statics_dir): + ctx['statics'] = os.listdir(statics_dir) + ctx['templates_dir'] = templates_dir + ctx['statics_dir'] = statics_dir + return ctx + +theme = ThemeView.as_view() + +class UploadMixin(object): + template_name = "organization/upload.html" + + def get_context_data(self, **kwargs): + ctx = super(UploadMixin, self).get_context_data(**kwargs) + ctx['form'] = self.form_class() + return ctx + + def post(self, request, *args, **kwargs): + form = self.form_class(request.POST, request.FILES) + context = self.get_context_data(**kwargs) + context['form'] = form + organization = context['organization'] + destination_dir = os.path.join(settings.ORGANIZATIONS_DIR, + organization.slug, self.upload_dir) + if form.is_valid(): + data = form.cleaned_data[self.filename_param] + if not os.path.exists(destination_dir): + os.makedirs(destination_dir) + try: + with open(os.path.join(destination_dir, data.name), 'w') as template: + template.write(data.read()) + messages.info(request, _('File "%s" successfully uploaded') % data.name) + except OSError: + messages.error(request, _('An error occured while uploading file "%s"') % data.name) + return redirect(self.get_success_url()) + else: + return self.render_to_response(context) + + +class TemplateUpload(UploadMixin, ThemeView): + form_class = TemplateForm + filename_param = 'template_file' + upload_dir = 'templates' + +template_upload = TemplateUpload.as_view() + + +class TemplateDelete(ThemeView): + + def get(self, request, *args, **kwargs): + ctx = self.get_context_data(**kwargs) + template = request.GET.get('template') + if os.path.exists(os.path.join(ctx['templates_dir'], template)): + try: + os.remove(os.path.join(ctx['templates_dir'], template)) + messages.info(request, _('Template %s successfully removed') % template) + except IOError: + messages.error(request, _('An error occured while removing file %s') % template) + else: + messages.error(request, _('Unknown template %s') % template) + return redirect(self.get_success_url()) + + +template_delete = TemplateDelete.as_view() + + +class StaticUpload(UploadMixin, ThemeView): + form_class = StaticForm + filename_param = 'static_file' + upload_dir = 'static' + +static_upload = StaticUpload.as_view() + + +class StaticDelete(ThemeView): + + def get(self, request, *args, **kwargs): + ctx = self.get_context_data(**kwargs) + static = request.GET.get('static') + if os.path.exists(os.path.join(ctx['statics_dir'], static)): + try: + os.remove(os.path.join(ctx['statics_dir'], static)) + messages.info(request, _('Static file %s successfully removed') % static) + except IOError: + messages.error(request, _('An error occured while removing file %s') % static) + else: + messages.error(request, _('Unknown static %s') % static) + return redirect(self.get_success_url()) + +static_delete = StaticDelete.as_view() diff --git a/uauth/settings.py b/uauth/settings.py index 4ff8a09..39b5ff5 100644 --- a/uauth/settings.py +++ b/uauth/settings.py @@ -54,7 +54,9 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.clickjacking.XFrameOptionsMiddleware', ) -TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + ('django.core.context_processors.request',) +TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + \ + ('django.core.context_processors.request', + 'uauth.context_processors.theme_base',) ROOT_URLCONF = 'uauth.urls' @@ -97,6 +99,8 @@ LDAP_CONF = { 'dn': 'ou=radius,dc=entrouvert,dc=org', } +ORGANIZATIONS_DIR = os.path.join(BASE_DIR, 'organizations') + AUTHENTICATION_BACKENDS = global_settings.AUTHENTICATION_BACKENDS + ( 'mellon.backends.SAMLBackend', 'uauth.backends.LocalAccountPasswordBackend', diff --git a/uauth/static/css/icons/icon-ressources-hover.png b/uauth/static/css/icons/icon-ressources-hover.png new file mode 100644 index 0000000..09d6614 Binary files /dev/null and b/uauth/static/css/icons/icon-ressources-hover.png differ diff --git a/uauth/static/css/icons/icon-ressources.png b/uauth/static/css/icons/icon-ressources.png new file mode 100644 index 0000000..d48f51b Binary files /dev/null and b/uauth/static/css/icons/icon-ressources.png differ diff --git a/uauth/static/css/style.css b/uauth/static/css/style.css index 12bb306..24d40ce 100644 --- a/uauth/static/css/style.css +++ b/uauth/static/css/style.css @@ -74,4 +74,5 @@ div.example { } /* icons */ -li.users a {background-image: url(icons/icon-personnes.png);} \ No newline at end of file +li.users a {background-image: url(icons/icon-personnes.png);} +li.theme a {background-image: url(icons/icon-ressources.png);} \ No newline at end of file diff --git a/uauth/templates/uauth/organization.html b/uauth/templates/uauth/organization.html index dbdf0b1..a02269d 100644 --- a/uauth/templates/uauth/organization.html +++ b/uauth/templates/uauth/organization.html @@ -1,4 +1,4 @@ -{% extends "uauth/base.html" %} +{% extends theme_base %} {% load i18n %} {% block more-user-links %} diff --git a/uauth/views.py b/uauth/views.py index e9450be..726c61f 100644 --- a/uauth/views.py +++ b/uauth/views.py @@ -77,6 +77,7 @@ class OrganizationPageView(LoginMixin, FormView): context = super(OrganizationPageView, self).get_context_data(**kwargs) idps = get_idp_list() organization = Organization.objects.get(slug=self.kwargs['organization_slug']) + self.request.session['organization'] = organization.slug self.request.session[organization.slug] = self.request.GET.urlencode() relay = signing.dumps({'organization': organization.slug}) context.update({'idps': idps,