Merge branch 'feature/set_sftp_password'
* feature/set_sftp_password: plug users and hosting packages together implement CustomerHostingPackageDetails view introduce new settings for groups and upload server implement osusers.forms.ChangeOsUserPasswordForm refactor dashboard.views.UserDashboardView generate documentation for gvacommon.viewmixins implement viewmixins.StaffOrSelfLoginRequiredMixin
This commit is contained in:
commit
f55886f1fe
20 changed files with 379 additions and 31 deletions
|
@ -1,6 +1,8 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
* :feature:`-` add frontend functionality to set an os users' sftp password
|
||||
(needs gvaldap >= 0.4.0 on the LDAP side)
|
||||
* :support:`-` remove unused dashboard.views.LogoutView and the corresponding
|
||||
URL in dashboard.urls
|
||||
* :feature:`-` add new task stub to set an ldap user's password
|
||||
|
|
|
@ -13,3 +13,11 @@ provides some functionality that is common to all gnuviechadmin subprojects.
|
|||
.. automodule:: gvacommon.celeryrouters
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
:py:mod:`viewmixins <gvacommon.viewmixins>`
|
||||
-------------------------------------------
|
||||
|
||||
.. automodule:: gvacommon.viewmixins
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
|
|
@ -22,3 +22,17 @@
|
|||
|
||||
.. automodule:: hostingpackages.models
|
||||
:members:
|
||||
|
||||
|
||||
:py:mod:`views <hostingpackages.views>`
|
||||
---------------------------------------
|
||||
|
||||
.. automodule:: hostingpackages.views
|
||||
:members:
|
||||
|
||||
|
||||
:py:mod:`urls <hostingpackages.urls>`
|
||||
-------------------------------------
|
||||
|
||||
.. automodule:: hostingpackages.urls
|
||||
:members:
|
||||
|
|
|
@ -18,8 +18,28 @@
|
|||
:members:
|
||||
|
||||
|
||||
:py:mod:`forms <osusers.forms>`
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: osusers.forms
|
||||
:members:
|
||||
|
||||
|
||||
:py:mod:`models <osusers.models>`
|
||||
---------------------------------
|
||||
|
||||
.. automodule:: osusers.models
|
||||
:members:
|
||||
|
||||
|
||||
:py:mod:`urls <osusers.urls>`
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: osusers.urls
|
||||
|
||||
|
||||
:py:mod:`views <osusers.views>`
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: osusers.views
|
||||
:members:
|
||||
|
|
|
@ -4,15 +4,13 @@ This module defines the views for the gnuviechadmin customer dashboard.
|
|||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.views.generic import (
|
||||
DetailView,
|
||||
TemplateView,
|
||||
)
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from braces.views import LoginRequiredMixin
|
||||
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
||||
|
||||
from hostingpackages.models import CustomerHostingPackage
|
||||
|
||||
|
@ -25,7 +23,7 @@ class IndexView(TemplateView):
|
|||
template_name = 'dashboard/index.html'
|
||||
|
||||
|
||||
class UserDashboardView(LoginRequiredMixin, DetailView):
|
||||
class UserDashboardView(StaffOrSelfLoginRequiredMixin, DetailView):
|
||||
"""
|
||||
This is the user dashboard view.
|
||||
|
||||
|
@ -35,18 +33,16 @@ class UserDashboardView(LoginRequiredMixin, DetailView):
|
|||
slug_field = 'username'
|
||||
template_name = 'dashboard/user_dashboard.html'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if (request.user.is_staff or request.user == self.get_object()):
|
||||
return super(UserDashboardView, self).dispatch(
|
||||
request, *args, **kwargs
|
||||
)
|
||||
return HttpResponseForbidden(
|
||||
_('You are not allowed to view this page.')
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserDashboardView, self).get_context_data(**kwargs)
|
||||
context['hosting_packages'] = CustomerHostingPackage.objects.filter(
|
||||
customer=self.object
|
||||
)
|
||||
return context
|
||||
|
||||
def get_customer_object(self):
|
||||
"""
|
||||
Returns the customer object.
|
||||
|
||||
"""
|
||||
return self.get_object()
|
||||
|
|
|
@ -352,5 +352,8 @@ OSUSER_MINGID = int(get_env_variable('GVA_MIN_OS_GID'))
|
|||
OSUSER_USERNAME_PREFIX = get_env_variable('GVA_OSUSER_PREFIX')
|
||||
OSUSER_HOME_BASEPATH = get_env_variable('GVA_OSUSER_HOME_BASEPATH')
|
||||
OSUSER_DEFAULT_SHELL = get_env_variable('GVA_OSUSER_DEFAULT_SHELL')
|
||||
OSUSER_DEFAULT_GROUPS = ['sftponly']
|
||||
OSUSER_SFTP_GROUP = 'sftponly'
|
||||
OSUSER_SSH_GROUP = 'sshusers'
|
||||
OSUSER_DEFAULT_GROUPS = [OSUSER_SFTP_GROUP]
|
||||
OSUSER_UPLOAD_SERVER = get_env_variable('GVA_OSUSER_UPLOADSERVER')
|
||||
########## END CUSTOM APP CONFIGURATION
|
||||
|
|
|
@ -11,6 +11,7 @@ urlpatterns = patterns(
|
|||
url(r'', include('dashboard.urls')),
|
||||
url(r'^accounts/', include('allauth.urls')),
|
||||
url(r'^hosting/', include('hostingpackages.urls')),
|
||||
url(r'^osuser/', include('osusers.urls')),
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
)
|
||||
|
||||
|
|
42
gnuviechadmin/gvacommon/viewmixins.py
Normal file
42
gnuviechadmin/gvacommon/viewmixins.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
"""
|
||||
This module defines mixins for gnuviechadmin views.
|
||||
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from braces.views import LoginRequiredMixin
|
||||
|
||||
|
||||
class StaffOrSelfLoginRequiredMixin(LoginRequiredMixin):
|
||||
"""
|
||||
Mixin that makes sure that a user is logged in and matches the current
|
||||
customer or is a staff user.
|
||||
|
||||
"""
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if (
|
||||
request.user.is_staff or
|
||||
request.user == self.get_customer_object()
|
||||
):
|
||||
return super(StaffOrSelfLoginRequiredMixin, self).dispatch(
|
||||
request, *args, **kwargs
|
||||
)
|
||||
return HttpResponseForbidden(
|
||||
_('You are not allowed to view this page.')
|
||||
)
|
||||
|
||||
def get_customer_object(self):
|
||||
"""
|
||||
Views based on this mixin have to implement this method to return
|
||||
the customer that must be an object of the same class as the
|
||||
django.contrib.auth user type.
|
||||
|
||||
:return: customer
|
||||
:rtype: settings.AUTH_USER_MODEL
|
||||
|
||||
"""
|
||||
raise NotImplemented("subclass has to implement get_customer_object")
|
|
@ -5,6 +5,7 @@ This module contains the hosting package models.
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import transaction
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
@ -218,6 +219,12 @@ class CustomerHostingPackage(HostingPackageBase):
|
|||
name=self.name, customer=self.customer
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('hosting_package_details', kwargs={
|
||||
'user': self.customer.username,
|
||||
'pk': self.id,
|
||||
})
|
||||
|
||||
def copy_template_attributes(self):
|
||||
"""
|
||||
Copy the attributes of the hosting package's template to the package.
|
||||
|
|
|
@ -1,12 +1,22 @@
|
|||
"""
|
||||
This module defines the URL patterns for hosting package related views.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
from .views import CreateHostingPackage
|
||||
from .views import (
|
||||
CreateHostingPackage,
|
||||
CustomerHostingPackageDetails,
|
||||
)
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^(?P<user>[\w0-9@.+-_]+)/create$', CreateHostingPackage.as_view(),
|
||||
name='create_hosting_package'),
|
||||
url(r'^(?P<user>[\w0-9@.+-_]+)/hostingpackage/(?P<pk>\d+)/$',
|
||||
CustomerHostingPackageDetails.as_view(),
|
||||
name='hosting_package_details'),
|
||||
)
|
||||
|
|
|
@ -4,18 +4,22 @@ This module defines views related to hosting packages.
|
|||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.shortcuts import redirect
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.generic import DetailView
|
||||
from django.views.generic.edit import CreateView
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from braces.views import (
|
||||
LoginRequiredMixin,
|
||||
StaffuserRequiredMixin,
|
||||
)
|
||||
|
||||
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
||||
|
||||
from .forms import CreateHostingPackageForm
|
||||
from .models import CustomerHostingPackage
|
||||
|
||||
|
@ -37,13 +41,12 @@ class CreateHostingPackage(
|
|||
kwargs.update(self.kwargs)
|
||||
return kwargs
|
||||
|
||||
def _get_customer(self):
|
||||
def get_customer_object(self):
|
||||
return get_user_model().objects.get(username=self.kwargs['user'])
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CreateHostingPackage, self).get_context_data(**kwargs)
|
||||
context['customer'] = self._get_customer()
|
||||
context['customer'] = self.get_customer_object()
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
|
@ -52,7 +55,7 @@ class CreateHostingPackage(
|
|||
|
||||
def form_valid(self, form):
|
||||
hostingpackage = form.save(commit=False)
|
||||
hostingpackage.customer = self._get_customer()
|
||||
hostingpackage.customer = self.get_customer_object()
|
||||
hostingpackage.save()
|
||||
messages.success(
|
||||
self.request,
|
||||
|
@ -60,3 +63,24 @@ class CreateHostingPackage(
|
|||
name=hostingpackage.name)
|
||||
)
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
|
||||
class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
|
||||
"""
|
||||
This view is for showing details of a customer hosting package.
|
||||
|
||||
"""
|
||||
model = CustomerHostingPackage
|
||||
context_object_name = 'hostingpackage'
|
||||
|
||||
def get_customer_object(self):
|
||||
return get_user_model().objects.get(username=self.kwargs['user'])
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CustomerHostingPackageDetails, self).get_context_data(
|
||||
**kwargs)
|
||||
context.update({
|
||||
'customer': self.get_customer_object(),
|
||||
'uploadserver': settings.OSUSER_UPLOAD_SERVER,
|
||||
})
|
||||
return context
|
||||
|
|
|
@ -6,6 +6,9 @@ from django import forms
|
|||
from django.utils.translation import ugettext as _
|
||||
from django.contrib import admin
|
||||
|
||||
from .forms import (
|
||||
PASSWORD_MISMATCH_ERROR
|
||||
)
|
||||
from .models import (
|
||||
AdditionalGroup,
|
||||
Group,
|
||||
|
@ -13,11 +16,6 @@ from .models import (
|
|||
User,
|
||||
)
|
||||
|
||||
PASSWORD_MISMATCH_ERROR = _("Passwords don't match")
|
||||
"""
|
||||
Error message for non matching passwords.
|
||||
"""
|
||||
|
||||
|
||||
class AdditionalGroupInline(admin.TabularInline):
|
||||
"""
|
||||
|
|
71
gnuviechadmin/osusers/forms.py
Normal file
71
gnuviechadmin/osusers/forms.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
"""
|
||||
This module defines operating system user related forms.
|
||||
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django import forms
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Submit
|
||||
|
||||
from .models import User
|
||||
|
||||
PASSWORD_MISMATCH_ERROR = _("Passwords don't match")
|
||||
"""
|
||||
Error message for non matching passwords.
|
||||
"""
|
||||
|
||||
|
||||
class ChangeOsUserPasswordForm(forms.ModelForm):
|
||||
"""
|
||||
A form for setting an OS user's password.
|
||||
|
||||
"""
|
||||
password1 = forms.CharField(
|
||||
label=_('Password'), widget=forms.PasswordInput,
|
||||
required=False,
|
||||
)
|
||||
password2 = forms.CharField(
|
||||
label=_('Password (again)'), widget=forms.PasswordInput,
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = []
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.helper = FormHelper()
|
||||
super(ChangeOsUserPasswordForm, self).__init__(*args, **kwargs)
|
||||
self.helper.form_action = reverse(
|
||||
'set_osuser_password', kwargs={'slug': self.instance.username})
|
||||
self.helper.add_input(Submit('submit', _('Set password')))
|
||||
|
||||
def clean_password2(self):
|
||||
"""
|
||||
Check that the two password entries match.
|
||||
|
||||
:return: the validated password
|
||||
:rtype: str or None
|
||||
|
||||
"""
|
||||
password1 = self.cleaned_data.get('password1')
|
||||
password2 = self.cleaned_data.get('password2')
|
||||
if password1 and password2 and password1 != password2:
|
||||
raise forms.ValidationError(PASSWORD_MISMATCH_ERROR)
|
||||
return password2
|
||||
|
||||
def save(self, commit=True):
|
||||
"""
|
||||
Save the provided password in hashed format.
|
||||
|
||||
:param boolean commit: whether to save the created user
|
||||
:return: user instance
|
||||
:rtype: :py:class:`osusers.models.User`
|
||||
|
||||
"""
|
||||
self.instance.set_password(self.cleaned_data['password1'])
|
||||
return super(ChangeOsUserPasswordForm, self).save(commit=commit)
|
|
@ -29,6 +29,7 @@ from ldaptasks.tasks import (
|
|||
delete_ldap_group,
|
||||
delete_ldap_user,
|
||||
remove_ldap_user_from_group,
|
||||
set_ldap_user_password,
|
||||
)
|
||||
|
||||
from fileservertasks.tasks import (
|
||||
|
@ -245,6 +246,15 @@ class User(TimeStampedModel, models.Model):
|
|||
"""
|
||||
if hasattr(self, 'shadow'):
|
||||
self.shadow.set_password(password)
|
||||
success = set_ldap_user_password.delay(
|
||||
self.username, password).get()
|
||||
if success:
|
||||
_LOGGER.info(
|
||||
"successfully set LDAP password for %s", self.username)
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"setting the LDAP password for %s failed", self.username)
|
||||
return success
|
||||
else:
|
||||
self.shadow = Shadow.objects.create_shadow(
|
||||
user=self, password=password
|
||||
|
@ -253,7 +263,9 @@ class User(TimeStampedModel, models.Model):
|
|||
self.username, self.uid, self.group.gid, self.gecos,
|
||||
self.homedir, self.shell, password
|
||||
).get()
|
||||
logging.info("set LDAP password for %s", dn)
|
||||
_LOGGER.info("set LDAP password for %s", dn)
|
||||
return True
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def save(self, *args, **kwargs):
|
||||
|
|
16
gnuviechadmin/osusers/urls.py
Normal file
16
gnuviechadmin/osusers/urls.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
This module defines the URL patterns for operating system user related views.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
from .views import SetOsUserPassword
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^(?P<slug>[\w0-9@.+-_]+)/setpassword$', SetOsUserPassword.as_view(),
|
||||
name='set_osuser_password'),
|
||||
)
|
45
gnuviechadmin/osusers/views.py
Normal file
45
gnuviechadmin/osusers/views.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
"""
|
||||
This module defines the views for gnuviechadmin operating system user handling.
|
||||
|
||||
"""
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from django.shortcuts import redirect
|
||||
from django.views.generic import UpdateView
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.contrib import messages
|
||||
|
||||
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
||||
|
||||
from .forms import ChangeOsUserPasswordForm
|
||||
from .models import User
|
||||
|
||||
|
||||
class SetOsUserPassword(StaffOrSelfLoginRequiredMixin, UpdateView):
|
||||
"""
|
||||
This view is used for setting a new operating system user password.
|
||||
|
||||
"""
|
||||
model = User
|
||||
slug_field = 'username'
|
||||
template_name_suffix = '_setpassword'
|
||||
context_object_name = 'osuser'
|
||||
form_class = ChangeOsUserPasswordForm
|
||||
|
||||
def get_customer_object(self):
|
||||
return self.get_object().customer
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super(SetOsUserPassword, self).get_context_data(
|
||||
*args, **kwargs)
|
||||
context['customer'] = self.get_customer_object()
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
osuser = form.save()
|
||||
messages.success(
|
||||
self.request,
|
||||
_("New password for {username} has been set successfully.").format(
|
||||
username=osuser.username
|
||||
))
|
||||
return redirect(osuser.customerhostingpackage)
|
|
@ -22,7 +22,7 @@
|
|||
<tbody>
|
||||
{% for package in hosting_packages %}
|
||||
<tr>
|
||||
<th>{{ package.name }}</th>
|
||||
<th><a href="{{ package.get_absolute_url }}" title="{% blocktrans with packagename=package.name %}Show details for {{ packagename }}{% endblocktrans %}">{{ package.name }}</a></th>
|
||||
<th>
|
||||
{% with diskspace=package.get_disk_space %}
|
||||
<span title="{% blocktrans %}The reserved disk space for your hosting package is {{ diskspace }} bytes.{% endblocktrans %}">{{ diskspace|filesizeformat }}</span>
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
{% extends "hostingpackages/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{{ block.super }} - {% spaceless %}
|
||||
{% if user == customer %}
|
||||
{% blocktrans with package=hostingpackage.name %}Details for your Hosting Package {{ package }}{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Details for Hosting Package {{ package }} of {{ full_name }}{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% endspaceless %}{% endblock title %}
|
||||
|
||||
{% block page_title %}{% blocktrans with package=hostingpackage.name %}Details of Hosting Package {{ package }}{% endblocktrans %}{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-6 col-xs-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{% trans "Hosting Package Information" %}<div class="pull-right"><a class="panel-title" href="#" title="{% trans "Edit Hosting Package Information" %}"><i class="glyphicon glyphicon-cog"></i></a></div>
|
||||
</div>
|
||||
<dl class="panel-body dl-horizontal">
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ hostingpackage.name }}</dd>
|
||||
<dt>{% trans "Description" %}</dt>
|
||||
<dd>{{ hostingpackage.description|default:"-" }}</dd>
|
||||
<dt>{% trans "Disk space" %}</dt>
|
||||
{% with diskspace=hostingpackage.get_disk_space %}
|
||||
<dd title="{% blocktrans %}The reserved disk space for your hosting package is {{ diskspace }} bytes.{% endblocktrans %}">{{ diskspace|filesizeformat }}</dd>
|
||||
{% endwith %}
|
||||
<dt>{% trans "Mailboxes" %}</dt>
|
||||
<dd>{% blocktrans with num=hostingpackage.get_used_mailboxes total=hostingpackage.get_mailboxes %}{{ num }} of {{ total }} in use{% endblocktrans %}</dd>
|
||||
<dt>{% if hostingpackage.osuser.is_sftp_user %}{% trans "SFTP username" %}{% else %}{% trans "SSH/SFTP username" %}{% endif %}</dt>
|
||||
<dd>{{ hostingpackage.osuser.username }}</dd>
|
||||
<dt>{% trans "Upload server" %}</dt>
|
||||
<dd>{{ uploadserver }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-6 col-xs-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{% trans "Hosting Package Actions" %}</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item"><a href="{% url "set_osuser_password" slug=hostingpackage.osuser.username %}">{% if hostingpackage.osuser.is_sftp %}{% trans "Set SFTP password" %}{% else %}{% trans "Set SSH/SFTP password" %}{% endif %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
1
gnuviechadmin/templates/osusers/base.html
Normal file
1
gnuviechadmin/templates/osusers/base.html
Normal file
|
@ -0,0 +1 @@
|
|||
{% extends "base.html" %}
|
30
gnuviechadmin/templates/osusers/user_setpassword.html
Normal file
30
gnuviechadmin/templates/osusers/user_setpassword.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
{% extends "osusers/base.html" %}
|
||||
{% load i18n crispy_forms_tags %}
|
||||
{% block title %}{{ block.super }} - {% spaceless %}
|
||||
{% if customer == user %}
|
||||
{% blocktrans with osuser=osuser.username %}Set new password for user {{ osuser }}{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans with osuser=osuser.username full_name=customer.get_full_name %}Set new password for user {{ osuser }} of customer {{ full_name }}{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% endspaceless %}{% endblock title %}
|
||||
|
||||
{% block page_title %}{% spaceless %}
|
||||
{% if customer == user %}
|
||||
{% blocktrans with osuser=osuser.username %}Set new password for user {{ osuser }}{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans with osuser=osuser.username full_name=customer.get_full_name %}Set new password for user {{ osuser }} of customer {{ full_name }}{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% endspaceless %}{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
{% crispy form %}
|
||||
{% endblock content %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('input[type=password]').val('');
|
||||
$('input[type=password]').first().focus();
|
||||
});
|
||||
</script>
|
||||
{% endblock extra_js %}
|
Loading…
Reference in a new issue