Are you sure you want to delete these records?
diff --git a/br/templates/backend/user_create.html b/br/templates/backend/user_create.html
new file mode 100644
index 0000000..c072bcb
--- /dev/null
+++ b/br/templates/backend/user_create.html
@@ -0,0 +1,124 @@
+{% extends 'backend/_layout.html' %}
+{% block content %}
+
+
+{% endblock content %}
\ No newline at end of file
diff --git a/br/templates/backend/user_edit.html b/br/templates/backend/user_edit.html
new file mode 100644
index 0000000..9e95771
--- /dev/null
+++ b/br/templates/backend/user_edit.html
@@ -0,0 +1,124 @@
+{% extends 'backend/_layout.html' %}
+{% block content %}
+
+
+{% endblock content %}
\ No newline at end of file
diff --git a/br/templates/backend/user_list.html b/br/templates/backend/user_list.html
new file mode 100644
index 0000000..644a404
--- /dev/null
+++ b/br/templates/backend/user_list.html
@@ -0,0 +1,160 @@
+{% extends 'backend/_layout.html' %}
+{% load staticfiles %}
+{% block content %}
+
+{% if messages %}
+{% for message in messages %}
+
+
+
+ {{ message|safe }}
+
+
+
+
+{% endfor %}
+{% endif %}
+
+
+
+
+
+
+
+
+
+ |
+ |
+ Full name |
+ Username |
+ Email |
+ Active? |
+
+
+
+
+
+
+
+
+
+
+
+{% include 'backend/pagination.html' %}
+
+
+
+
+
+
Are you sure you want to delete these users?
+
This cannot be undone!
+
+
+
+
+
+{% endblock content %}
+{% block scripts %}
+
+
+{% endblock scripts %}
\ No newline at end of file
diff --git a/br/views.py b/br/views.py
index 8337cc9..cf63f4e 100644
--- a/br/views.py
+++ b/br/views.py
@@ -354,6 +354,7 @@ def get_context_data(self, **kwargs):
context = super(ReportListView, self).get_context_data(**kwargs)
context['filter_form'] = self.filter_set.form
context['page_title'] = self.page_title
+ context['delete_form'] = ReportDeleteForm()
return context
def get_queryset(self):
diff --git a/profiles/forms.py b/profiles/forms.py
index e0b1918..b683b8d 100644
--- a/profiles/forms.py
+++ b/profiles/forms.py
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
from django import forms
+from django.contrib.auth.models import User
+from django.core.exceptions import ObjectDoesNotExist
from locations.models import Location
from profiles.models import Profile
@@ -7,8 +9,88 @@
class ProfileAdminForm(forms.ModelForm):
locations = forms.ModelMultipleChoiceField(
- queryset=Location.objects.filter(type__name=u'State'))
+ queryset=Location.objects.filter(type__name=u'State'))
class Meta:
model = Profile
fields = (u'user', u'locations')
+
+
+class UserForm(forms.ModelForm):
+ locations = forms.ModelMultipleChoiceField(
+ queryset=Location.objects.filter(type__name=u'State'), required=False)
+ can_add_locations = forms.BooleanField(required=False)
+ can_change_br_reports = forms.BooleanField(required=False)
+ can_change_dr_reports = forms.BooleanField(required=False)
+ can_change_mnchw_reports = forms.BooleanField(required=False)
+ can_change_reporters = forms.BooleanField(required=False)
+ can_change_users = forms.BooleanField(required=False)
+ password = forms.CharField(required=False, widget=forms.PasswordInput())
+ password_confirm = forms.CharField(required=False, widget=forms.PasswordInput())
+
+ class Meta:
+ model = User
+ fields = (
+ 'username', 'email', 'first_name', 'last_name', 'is_active',
+ 'is_superuser'
+ )
+
+ def __init__(self, *args, **kwargs):
+ user = kwargs.get('instance')
+ if user is not None:
+ initial_data = kwargs.get('initial', {}).copy()
+
+ # set up locations for subscription
+ try:
+ profile = user.profile
+ except ObjectDoesNotExist:
+ profile = None
+
+ if profile is not None:
+ initial_data['locations'] = profile.locations
+
+ # set up permissions
+ initial_data['can_add_locations'] = user.has_perm('locations.add_location')
+ initial_data['can_change_br_reports'] = user.has_perm('br.change_birthregistration')
+ initial_data['can_change_dr_reports'] = user.has_perm('dr.change_deathreport')
+ initial_data['can_change_mnchw_reports'] = user.has_perm('ipd.change_report')
+ initial_data['can_change_reporters'] = user.has_perm('reporters.change_reporter')
+ initial_data['can_change_users'] = user.has_perm('auth.change_user')
+
+ kwargs['initial'] = initial_data
+
+ super(UserForm, self).__init__(*args, **kwargs)
+
+ def clean(self):
+ cleaned_data = super(UserForm, self).clean()
+ password = cleaned_data.get('password')
+ password_confirm = cleaned_data.get('password_confirm')
+ if password != password_confirm:
+ self.add_error('password', 'Passwords do not match')
+
+ return cleaned_data
+
+
+class UserCreateForm(UserForm):
+ password = forms.CharField(widget=forms.PasswordInput())
+ password_confirm = forms.CharField(widget=forms.PasswordInput())
+
+
+class UserDeleteForm(forms.Form):
+ users = forms.ModelMultipleChoiceField(
+ queryset=User.objects.all(),
+ required=False, widget=forms.CheckboxSelectMultiple
+ )
+
+ def __init__(self, *args, **kwargs):
+ self.initiator = kwargs.pop('user', None)
+ super(UserDeleteForm, self).__init__(*args, **kwargs)
+
+ def clean(self):
+ cleaned_data = super(UserDeleteForm, self).clean()
+ users = cleaned_data.get('users')
+ if self.initiator is not None:
+ if users.filter(pk=self.initiator.pk).exists():
+ self.add_error('users', 'Cannot delete the current user')
+
+ return cleaned_data
diff --git a/profiles/urls.py b/profiles/urls.py
new file mode 100644
index 0000000..a3d48b5
--- /dev/null
+++ b/profiles/urls.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+from django.conf.urls import url
+
+from profiles.views import (
+ UserCreateView, UserListView, UserUpdateView, user_delete)
+
+urlpatterns = [
+ url(r'^$', UserListView.as_view(), name='users_list'),
+ url(r'^new/?$', UserCreateView.as_view(), name='user_create'),
+ url(r'^delete/?$', user_delete, name='user_delete'),
+ url(r'^(?P
\d+)/?$', UserUpdateView.as_view(), name='user_edit'),
+]
diff --git a/profiles/views.py b/profiles/views.py
index 91ea44a..0967769 100644
--- a/profiles/views.py
+++ b/profiles/views.py
@@ -1,3 +1,167 @@
-from django.shortcuts import render
+# -*- coding: utf-8 -*-
+from braces.views import LoginRequiredMixin, PermissionRequiredMixin
+from django.conf import settings
+from django.contrib import messages
+from django.contrib.auth.models import Permission, User
+from django.contrib.contenttypes.models import ContentType
+from django.core.urlresolvers import reverse
+from django.http import (
+ HttpResponseForbidden, HttpResponseNotAllowed, HttpResponseRedirect)
+from django.utils.http import is_safe_url
+from django.views.generic import CreateView, ListView, UpdateView
-# Create your views here.
+from profiles.forms import UserCreateForm, UserDeleteForm, UserForm
+from profiles.models import Profile
+
+PROTECTED_VIEW_PERMISSION = 'auth.change_user'
+
+
+def update_permissions(user, form_data):
+ from br.models import BirthRegistration
+ from dr.models import DeathReport
+ from ipd.models import Report
+ from locations.models import Location
+ from reporters.models import Reporter
+
+ br_report_content_type = ContentType.objects.get_for_model(
+ BirthRegistration)
+ dr_report_content_type = ContentType.objects.get_for_model(DeathReport)
+ location_content_type = ContentType.objects.get_for_model(Location)
+ mnchw_report_content_type = ContentType.objects.get_for_model(Report)
+ reporter_content_type = ContentType.objects.get_for_model(Reporter)
+ user_content_type = ContentType.objects.get_for_model(User)
+
+ if form_data.get('can_add_locations'):
+ permission = Permission.objects.get(
+ codename='add_location',
+ content_type=location_content_type,
+ )
+ user.user_permissions.add(permission)
+
+ if form_data.get('can_change_br_reports'):
+ permission = Permission.objects.get(
+ codename='change_birthregistration',
+ content_type=br_report_content_type,
+ )
+ user.user_permissions.add(permission)
+
+ if form_data.get('can_change_dr_reports'):
+ permission = Permission.objects.get(
+ codename='change_deathreport',
+ content_type=dr_report_content_type,
+ )
+ user.user_permissions.add(permission)
+
+ if form_data.get('can_change_mnchw_reports'):
+ permission = Permission.objects.get(
+ codename='change_report',
+ content_type=mnchw_report_content_type,
+ )
+ user.user_permissions.add(permission)
+
+ if form_data.get('can_change_reporters'):
+ permission = Permission.objects.get(
+ codename='change_reporter',
+ content_type=reporter_content_type,
+ )
+ user.user_permissions.add(permission)
+
+ if form_data.get('can_change_users'):
+ permission = Permission.objects.get(
+ codename='change_user',
+ content_type=user_content_type,
+ )
+ user.user_permissions.add(permission)
+
+ user.save()
+
+
+class UserListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
+ context_object_name = 'users'
+ model = User
+ ordering = ('-pk',)
+ page_title = 'Users'
+ paginate_by = settings.PAGE_SIZE
+ permission_required = PROTECTED_VIEW_PERMISSION
+ template_name = 'backend/user_list.html'
+
+ def get_context_data(self, **kwargs):
+ context = super(UserListView, self).get_context_data(**kwargs)
+ context['page_title'] = self.page_title
+ context['delete_form'] = UserDeleteForm()
+ return context
+
+
+class UserCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
+ form_class = UserCreateForm
+ model = User
+ page_title = 'Create User'
+ permission_required = PROTECTED_VIEW_PERMISSION
+ template_name = 'backend/user_create.html'
+
+ def form_valid(self, form):
+ user = form.save()
+ cleaned_data = form.cleaned_data
+ user.set_password(cleaned_data.get('password'))
+
+ if not user.is_superuser:
+ update_permissions(user, cleaned_data)
+
+ return HttpResponseRedirect(reverse('users:users_list'))
+
+ def get_context_data(self, **kwargs):
+ context = super(UserCreateView, self).get_context_data(**kwargs)
+ context['page_title'] = self.page_title
+ return context
+
+
+class UserUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
+ form_class = UserForm
+ model = User
+ page_title = 'Edit User'
+ permission_required = PROTECTED_VIEW_PERMISSION
+ template_name = 'backend/user_edit.html'
+
+ def form_valid(self, form):
+ user = form.save()
+ cleaned_data = form.cleaned_data
+
+ if cleaned_data.get('password'):
+ user.set_password(cleaned_data.get('password'))
+
+ if not user.is_superuser:
+ update_permissions(user, cleaned_data)
+
+ return HttpResponseRedirect(reverse('users:users_list'))
+
+ def get_context_data(self, **kwargs):
+ context = super(UserUpdateView, self).get_context_data(**kwargs)
+ context['page_title'] = self.page_title
+ return context
+
+
+def user_delete(request):
+ if request.method != 'POST':
+ return HttpResponseNotAllowed(['POST'])
+
+ if not request.user.is_authenticated:
+ return HttpResponseForbidden()
+
+ form = UserDeleteForm(request.POST, user=request.user)
+ redirect_path = request.META.get(
+ 'HTTP_REFERER', reverse('users:users_list'))
+
+ if form.is_valid():
+ users = form.cleaned_data.get('users')
+ users.delete()
+
+ messages.add_message(
+ request,
+ messages.SUCCESS,
+ 'Success! The selected users were deleted.'
+ )
+
+ if not is_safe_url(url=redirect_path, host=request.get_host()):
+ redirect_path = reverse('users:users_list')
+
+ return HttpResponseRedirect(redirect_path)
diff --git a/unicefng/templates/common/usermenu.html b/unicefng/templates/common/usermenu.html
index a6c4e29..31a29f6 100644
--- a/unicefng/templates/common/usermenu.html
+++ b/unicefng/templates/common/usermenu.html
@@ -1,18 +1,28 @@
{% load static %}
{% if user.is_authenticated %}
-{% if perms.br.change_birthregistration %}BR reports{% endif %}
-{% if perms.br.change_birthregistration %}BR centers{% endif %}
BR help
+{% if perms.br.change_birthregistration %}
+BR reports
+BR centers
-{% if perms.dr.change_deathreport %}DR reports{% endif %}
-{% if perms.dr.change_deathreport %}DR manual{% endif %}
+{% endif %}
+{% if perms.dr.change_deathreport %}
+DR reports
+DR manual
-{% if perms.ipd.change_report %}MNCHW campaigns{% endif %}
+{% endif %}
+{% if perms.ipd.change_report %}
+MNCHW campaigns
+{% endif %}
{% if perms.reporters.change_reporter %}
Reporters
{% endif %}
+{% if perms.auth.change_user %}
+Users
+
+{% endif %}
Messages
{% endif %}
\ No newline at end of file
diff --git a/unicefng/urls.py b/unicefng/urls.py
index 0789435..e8053fe 100644
--- a/unicefng/urls.py
+++ b/unicefng/urls.py
@@ -27,10 +27,11 @@
url(r'incoming/', HttpBackendView.as_view(backend_name='polling')),
url(r'^messages/', include('messagebox.urls', namespace='messaging')),
url(r'^reporters/', include('reporters.urls', namespace=u'reporters')),
+ url(r'^users/', include('profiles.urls', namespace='users')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# authentication urls
urlpatterns += [
- url(r'^accounts/login/$', auth_views.login, {'template_name': 'login.html'}, name="user-login"),
+ url(r'^accounts/login/$', auth_views.login, {'template_name': 'backend/login.html'}, name="user-login"),
url(r'^accounts/logout/$', auth_views.logout_then_login, name="user-logout")
]