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" %}
+
+
+{% trans "Statics" %}
+
+{% 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 %}
+
+{% 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,