diff --git a/circle/circle/settings/base.py b/circle/circle/settings/base.py index ccc7704..b3a12ce 100644 --- a/circle/circle/settings/base.py +++ b/circle/circle/settings/base.py @@ -192,6 +192,7 @@ MIDDLEWARE_CLASSES = ( # Default Django middleware. 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', diff --git a/circle/dashboard/forms.py b/circle/dashboard/forms.py index ad3db5f..4e575cf 100644 --- a/circle/dashboard/forms.py +++ b/circle/dashboard/forms.py @@ -22,6 +22,7 @@ from storage.models import Disk, DataStore from vm.models import ( InstanceTemplate, Lease, InterfaceTemplate, Node, Trait, Instance ) +from .models import Profile VLANS = Vlan.objects.all() DISKS = Disk.objects.exclude(type="qcow2-snap") @@ -991,3 +992,21 @@ class TraitForm(forms.ModelForm): class Meta: model = Trait fields = ['name'] + + +class MyProfileForm(forms.ModelForm): + + class Meta: + fields = ('preferred_language', ) + model = Profile + + @property + def helper(self): + helper = FormHelper() + helper.layout = Layout('preferred_language', ) + helper.add_input(Submit("submit", _("Save"))) + return helper + + def save(self, *args, **kwargs): + value = super(MyProfileForm, self).save(*args, **kwargs) + return value diff --git a/circle/dashboard/models.py b/circle/dashboard/models.py index 2bc02ea..79358ea 100644 --- a/circle/dashboard/models.py +++ b/circle/dashboard/models.py @@ -4,6 +4,7 @@ from logging import getLogger from django.conf import settings from django.contrib.auth.models import User, Group from django.contrib.auth.signals import user_logged_in +from django.core.urlresolvers import reverse from django.db.models import ( Model, ForeignKey, OneToOneField, CharField, IntegerField, TextField, DateTimeField, @@ -69,6 +70,9 @@ class Profile(Model): return Notification.send(self.user, subject, template, context, valid_until) + def get_absolute_url(self): + return reverse("dashboard.views.profile") + class GroupProfile(AclBase): ACL_LEVELS = ( diff --git a/circle/dashboard/templates/dashboard/base.html b/circle/dashboard/templates/dashboard/base.html index e5ccec2..e36fcc8 100644 --- a/circle/dashboard/templates/dashboard/base.html +++ b/circle/dashboard/templates/dashboard/base.html @@ -36,12 +36,15 @@ {% block navbar-ul %} {% endblock %} </ul> - <a class="navbar-brand pull-right" href="/network/" style="color: white; font-size: 10px;">Network</a> - <a class="navbar-brand pull-right" href="/admin/" style="color: white; font-size: 10px;">Admin</a> {% if user.is_authenticated %} - <a class="navbar-brand pull-right" href="{% url "logout" %}?next={% url "login" %}" style="color: white; font-size: 10px;">Log out {{user}}</a> + <a class="navbar-brand pull-right" href="{% url "logout" %}?next={% url "login" %}" style="color: white; font-size: 10px;"><i class="icon-signout icon-sign-out"></i> {% trans "Log out" %}</a> + <a class="navbar-brand pull-right" href="{% url "dashboard.views.profile" %}" title="{% trans "User profile" %}" style="color: white; font-size: 10px;"><i class="icon-user "></i> {{user}}</a> + <a class="navbar-brand pull-right" href="/network/" style="color: white; font-size: 10px;"><i class="icon-globe"></i> {% trans "Network" %}</a> + {% if user.is_superuser %} + <a class="navbar-brand pull-right" href="/admin/" style="color: white; font-size: 10px;"><i class="icon-cogs"></i> {% trans "Admin" %}</a> + {% endif %} {% else %} - <a class="navbar-brand pull-right" href="{% url "login" %}?next={% url "dashboard.index" %}" style="color: white; font-size: 10px;">Login</a> + <a class="navbar-brand pull-right" href="{% url "login" %}?next={% url "dashboard.index" %}" style="color: white; font-size: 10px;"><i class="icon-sign-in"></i> {% trans "Log in " %}</a> {% endif %} </div><!-- .collapse .navbar-collapse --> </div><!-- navbar navbar-inverse navbar-fixed-top --> diff --git a/circle/dashboard/templates/dashboard/profile_form.html b/circle/dashboard/templates/dashboard/profile_form.html new file mode 100644 index 0000000..649b073 --- /dev/null +++ b/circle/dashboard/templates/dashboard/profile_form.html @@ -0,0 +1,20 @@ +{% extends "dashboard/base.html" %} +{% load i18n %} +{% load crispy_forms_tags %} +{% block content %} + +<div class="row"> + <div class="col-md-12"> + <div class="panel panel-default"> + <div class="panel-heading"> + <a class="pull-right btn btn-default btn-xs" href="{% url "dashboard.index" %}">{% trans "Back" %}</a> + <h3 class="no-margin"><i class="icon-desktop"></i> {% trans "My profile" %}</h3> + </div> + <div class="panel-body"> + {% crispy form %} + </div> + </div> + </div> +</div> + +{% endblock %} diff --git a/circle/dashboard/urls.py b/circle/dashboard/urls.py index 09924f9..77e0201 100644 --- a/circle/dashboard/urls.py +++ b/circle/dashboard/urls.py @@ -4,10 +4,10 @@ from vm.models import Instance from .views import ( AclUpdateView, DiskAddView, FavouriteView, GroupAclUpdateView, GroupDelete, GroupDetailView, GroupList, GroupUserDelete, IndexView, LeaseCreate, - LeaseDelete, LeaseDetail, NodeAddTraitView, NodeCreate, NodeDelete, - NodeDetailView, NodeGraphView, NodeList, NodeStatus, NotificationView, - PortDelete, TemplateAclUpdateView, TemplateCreate, TemplateDelete, - TemplateDetail, TemplateList, TransferOwnershipConfirmView, + LeaseDelete, LeaseDetail, MyPreferencesView, NodeAddTraitView, NodeCreate, + NodeDelete, NodeDetailView, NodeGraphView, NodeList, NodeStatus, + NotificationView, PortDelete, TemplateAclUpdateView, TemplateCreate, + TemplateDelete, TemplateDetail, TemplateList, TransferOwnershipConfirmView, TransferOwnershipView, vm_activity, VmCreate, VmDelete, VmDetailView, VmDetailVncTokenView, VmGraphView, VmList, VmMassDelete, VmMigrateView, VmRenewView, @@ -97,4 +97,6 @@ urlpatterns = patterns( url(r'^disk/add/$', DiskAddView.as_view(), name="dashboard.views.disk-add"), + url(r'^profile/$', MyPreferencesView.as_view(), + name="dashboard.views.profile"), ) diff --git a/circle/dashboard/views.py b/circle/dashboard/views.py index cbe1de8..68b4fcc 100644 --- a/circle/dashboard/views.py +++ b/circle/dashboard/views.py @@ -34,8 +34,8 @@ from braces.views import ( ) from .forms import ( - CircleAuthenticationForm, DiskAddForm, HostForm, LeaseForm, NodeForm, - TemplateForm, TraitForm, VmCustomizeForm, + CircleAuthenticationForm, DiskAddForm, HostForm, LeaseForm, MyProfileForm, + NodeForm, TemplateForm, TraitForm, VmCustomizeForm, ) from .tables import (NodeListTable, NodeVmListTable, TemplateListTable, LeaseListTable, GroupListTable,) @@ -1999,8 +1999,10 @@ def circle_login(request): extra_context = { 'saml2': hasattr(settings, "SAML_CONFIG") } - return login(request, authentication_form=authentication_form, - extra_context=extra_context) + response = login(request, authentication_form=authentication_form, + extra_context=extra_context) + set_language_cookie(request, response) + return response class DiskAddView(TemplateView): @@ -2042,3 +2044,33 @@ class DiskAddView(TemplateView): r = obj.get_absolute_url() r = "%s#resources" % r return redirect(r) + + +class MyPreferencesView(UpdateView): + + model = Profile + form_class = MyProfileForm + + def get_object(self, queryset=None): + if self.request.user.is_anonymous(): + raise PermissionDenied() + try: + return self.request.user.profile + except Profile.DoesNotExist: + raise Http404(_("You don't have a profile.")) + + def form_valid(self, form): + response = super(MyPreferencesView, self).form_valid(form) + set_language_cookie(self.request, response) + return response + + +def set_language_cookie(request, response, lang=None): + if lang is None: + try: + lang = request.user.profile.preferred_language + except: + return + + cname = getattr(settings, 'LANGUAGE_COOKIE_NAME', 'django_language') + response.set_cookie(cname, lang, 365 * 86400)