Skip to content

Commit

Permalink
Add user management
Browse files Browse the repository at this point in the history
- Add user model, views and templates
- Add organization model, views and templates
- Create, edit and delete superusers and staff (fixes #44)
- Create, edit and delete region users (fixes #59)
- Show region switcher based on user's permissions (fixes #56)
- Show link to network management if user is superuser or staff (fixes #66)
  • Loading branch information
timobrembeck committed Jun 12, 2019
1 parent 17380d7 commit ab09e86
Show file tree
Hide file tree
Showing 35 changed files with 1,346 additions and 28 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ docs/_build/
.idea
.directory
.directory
cms-django.iml

# Media Library
backend/media/*

# XLIFF files folder
**/xliffs/

4 changes: 2 additions & 2 deletions backend/cms/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@


from django.contrib import admin

from .models import Site, Language, LanguageTreeNode, Page, PageTranslation
from .models import Site, Language, LanguageTreeNode, Page, PageTranslation, Organization

admin.site.register(Site)
admin.site.register(Language)
admin.site.register(LanguageTreeNode)
admin.site.register(Page)
admin.site.register(PageTranslation)
admin.site.register(Organization)
5 changes: 5 additions & 0 deletions backend/cms/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,9 @@

from .media_models import Document
from .media_models import DocumentForm

from .configuration import Configuration

from .organization import Organization

from .user_profile import UserProfile
6 changes: 3 additions & 3 deletions backend/cms/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from datetime import datetime, time, date

from dateutil.rrule import weekday, rrule
from django.contrib.auth.models import User
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator, MaxValueValidator
Expand Down Expand Up @@ -113,7 +113,7 @@ def get_list_view(cls):

event_translations = EventTranslation.objects.filter(
language='de'
).select_related('user')
).select_related('creator')
events = cls.objects.all().prefetch_related(
models.Prefetch('event_translations', queryset=event_translations)
).filter(event_translations__language='de')
Expand Down Expand Up @@ -178,4 +178,4 @@ class EventTranslation(models.Model):
event = models.ForeignKey(Event, related_name='event_translations', on_delete=models.CASCADE)
created_date = models.DateTimeField(default=timezone.now)
last_updated = models.DateTimeField(auto_now=True)
creator = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
creator = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)
15 changes: 15 additions & 0 deletions backend/cms/models/organization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.db import models
from django.utils import timezone


class Organization(models.Model):

name = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
thumbnail = models.CharField(max_length=250, blank=True)

created_date = models.DateTimeField(default=timezone.now)
last_updated = models.DateTimeField(auto_now=True)

def __str__(self):
return self.name
5 changes: 2 additions & 3 deletions backend/cms/models/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.exceptions import ObjectDoesNotExist
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
Expand Down Expand Up @@ -161,7 +160,7 @@ class PageTranslation(models.Model):
version = models.PositiveIntegerField(default=0)
public = models.BooleanField(default=False)
minor_edit = models.BooleanField(default=False)
creator = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
creator = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)
created_date = models.DateTimeField(default=timezone.now)
last_updated = models.DateTimeField(auto_now=True)

Expand Down
6 changes: 3 additions & 3 deletions backend/cms/models/poi.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Model for Point of Interests
"""
from django.contrib.auth.models import User
from django.db import models
from django.conf import settings
from django.utils import timezone

from .site import Site
Expand Down Expand Up @@ -35,7 +35,7 @@ def get_list_view(cls):

poi_translations = POITranslation.objects.filter(
language='de'
).select_related('user')
).select_related('creator')
pois = cls.objects.all().prefetch_related(
models.Prefetch('poi_translations', queryset=poi_translations)
).filter(poi_translations__language='de')
Expand Down Expand Up @@ -66,4 +66,4 @@ class POITranslation(models.Model):
public = models.BooleanField(default=False)
created_date = models.DateTimeField(default=timezone.now)
last_updated = models.DateTimeField(auto_now=True)
creator = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
creator = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)
22 changes: 22 additions & 0 deletions backend/cms/models/user_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from django.db import models
from django.conf import settings

from .site import Site
from .organization import Organization


class UserProfile(models.Model):

user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='profile', on_delete=models.CASCADE)
regions = models.ManyToManyField(Site, blank=True)
organization = models.ForeignKey(Organization, null=True, blank=True, on_delete=models.SET_NULL)

@property
def roles(self):
return self.user.groups.all()

def __str__(self):
return self.user.username

class Meta():
default_permissions = ()
40 changes: 26 additions & 14 deletions backend/cms/templates/_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,19 @@
{% endif %}
</div>
<div id="instance-selector-list" class="absolute shadow rounded-b">
{% for other_region in sites %}
<a href="{% url 'dashboard' site_slug=other_region.slug %}" class="block px-4 py-3 text-grey-darkest hover:bg-grey">
{{ other_region.name }}
</a>
{% for other_region in user.profile.regions.all %}
{% if not other_region == site %}
<a href="{% url 'dashboard' site_slug=other_region.slug %}" class="block px-4 py-3 text-grey-darkest hover:bg-grey">
{{ other_region.name }}
</a>
{% endif %}
{% endfor %}
{% if site %}
<a href="{% url 'admin_dashboard' %}" class="block px-4 py-3 text-grey-darkest hover:bg-grey rounded-b">
{% trans 'Network Management' %}
</a>
{% if user.is_superuser or user.is_staff %}
{% if site %}
<a href="{% url 'admin_dashboard' %}" class="block px-4 py-3 text-grey-darkest hover:bg-grey rounded-b">
{% trans 'Network Management' %}
</a>
{% endif %}
{% endif %}
</div>
</div>
Expand Down Expand Up @@ -126,9 +130,9 @@
<i data-feather="calendar" class="absolute"></i>
{% trans 'Events' %}
</a>
<a href="" class="relative block text-grey-light hover:text-grey-darker hover:bg-integreat">
<a href="{% url 'region_users' site_slug=site.slug %}" class="relative block text-grey-light hover:text-grey-darker hover:bg-integreat {% if current_menu_item == 'users' %}active{% endif %}">
<i data-feather="users" class="absolute"></i>
{% trans 'User' %}
{% trans 'Users' %}
</a>
<a href="" class="relative block text-grey-light hover:text-grey-darker hover:bg-integreat">
<i data-feather="thumbs-up" class="absolute"></i>
Expand Down Expand Up @@ -167,10 +171,18 @@
<i data-feather="flag" class="absolute"></i>
{% trans 'Languages' %}
</a>
<a href="" class="relative block text-grey-light hover:text-grey-darker hover:bg-integreat">
<i data-feather="users" class="absolute"></i>
{% trans 'User' %}
</a>
<a href="{% url 'users' %}" class="relative block text-grey-light hover:text-grey-darker hover:bg-integreat {% if current_menu_item == 'users' %}active{% endif %}">
<i data-feather="users" class="absolute"></i>
{% trans 'Users' %}
</a>
<a href="{% url 'roles' %}" class="relative block text-grey-light hover:text-grey-darker hover:bg-integreat">
<i data-feather="key" class="absolute"></i>
{% trans 'Roles' %}
</a>
<a href="{% url 'organizations' %}" class="relative block text-grey-light hover:text-grey-darker hover:bg-integreat">
<i data-feather="umbrella" class="absolute"></i>
{% trans 'Organizations' %}
</a>
<a href="" class="relative block text-grey-light hover:text-grey-darker hover:bg-integreat">
<i data-feather="thumbs-up" class="absolute"></i>
{% trans 'Feedback' %}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% load i18n %}
<div id="confirm_delete_user" class="confirmation-popup flex-col justify-center max-w-sm fixed pin-t pin-b pin-r pin-l hidden">
<div class="content bg-grey-light w-full p-4 shadow-md rounded">
<h2>{% trans 'Please confirm that you really want to delete this user' %}</h2>
<p class="mt-4 mb-6">
{% trans 'This cannot be reversed.' %}
</p>
<a href="{% url 'delete_region_user' site_slug=site.slug user_id=user_form.instance.id %}" class="bg-red hover:bg-red-dark text-white font-bold py-3 px-4 mr-2 rounded focus:outline-none focus:shadow-outline">
{% trans 'Yes, delete this user now' %}
</a>
<button class="cursor-pointer bg-blue hover:bg-blue-dark text-white font-bold py-3 px-4 rounded" onclick="close_confirmation_popup(event)">
{% trans 'Cancel' %}
</button>
</div>
</div>
15 changes: 15 additions & 0 deletions backend/cms/templates/general/confirmation_popups/delete_user.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% load i18n %}
<div id="confirm_delete_user" class="confirmation-popup flex-col justify-center max-w-sm fixed pin-t pin-b pin-r pin-l hidden">
<div class="content bg-grey-light w-full p-4 shadow-md rounded">
<h2>{% trans 'Please confirm that you really want to delete this user' %}</h2>
<p class="mt-4 mb-6">
{% trans 'This cannot be reversed.' %}
</p>
<a href="{% url 'delete_user' user_id=user_form.instance.id %}" class="bg-red hover:bg-red-dark text-white font-bold py-3 px-4 mr-2 rounded focus:outline-none focus:shadow-outline">
{% trans 'Yes, delete this user now' %}
</a>
<button class="cursor-pointer bg-blue hover:bg-blue-dark text-white font-bold py-3 px-4 rounded" onclick="close_confirmation_popup(event)">
{% trans 'Cancel' %}
</button>
</div>
</div>
41 changes: 41 additions & 0 deletions backend/cms/templates/organizations/list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{% extends "_base.html" %}
{% load i18n %}
{% block content %}
{% load static %}
<div class="table-header">
<div class="flex flex-wrap">
<form class="table-search relative w-1/2">
<i data-feather="search" class="absolute"></i>
<input type="search" placeholder="{% trans 'Search' %}" class="h-full pl-10 pr-4 rounded shadow">
</form>
<div class="w-1/2 flex flex-wrap justify-end">
<a href="{% url 'new_organization' %}" class="bg-grey-dark hover:bg-integreat hover:text-grey-darkest text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
{% trans 'Create organization' %}
</a>
</div>
</div>
</div>

<div class="table-listing">
<table class="w-full mt-4 rounded border border-solid border-grey-light shadow bg-white">
<thead>
<tr class="border-b border-solid border-grey-light">
<th class="text-sm text-left uppercase py-3 pl-4 pr-2">{% trans 'Name' %}</th>
<th class="text-sm text-left uppercase py-3 px-2">{% trans 'Slug' %}</th>
<th class="text-sm text-left uppercase py-3 px-2">{% trans 'Thumbnail' %}</th>
</tr>
</thead>
<tbody>
{% for organization in organizations %}
{% include "organizations/list_row.html" %}
{% empty %}
<tr>
<td colspan="6" class="px-2 py-3">
{% trans 'No organizations available yet.' %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
13 changes: 13 additions & 0 deletions backend/cms/templates/organizations/list_row.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<tr class="border-t border-solid border-grey-lighter hover:bg-grey-lightest">
<td>
<a href="{% url 'edit_organization' organization_id=organization.id %}" class="block py-3 px-2 text-grey-darkest">
{{ organization.name }}
</a>
</td>
<td>
{{ organization.slug }}
</td>
<td>
{{ organization.thumbnail }}
</td>
</tr>
77 changes: 77 additions & 0 deletions backend/cms/templates/organizations/organization.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{% extends "_base.html" %}
{% load i18n %}
{% block content %}
{% load static %}
{% load widget_tweaks %}
<form method="post">
{% csrf_token %}
<div class="flex flex-wrap mb-4">
<div class="w-2/5 flex flex-wrap flex-col justify-center">
<h2 class="heading font-normal">
{% if form.initial %}
{% with form.name.value as organization_name %}
{% blocktrans %}Edit organization "{{ organization_name }}"{% endblocktrans %}
{% endwith %}
{% else %}
{% trans 'Create new organization' %}
{% endif %}
</h2>
</div>
<div class="w-3/5 flex justify-end">
<input type="submit" name="submit_save" class="cursor-pointer bg-grey hover:bg-grey-dark text-white font-bold py-3 px-4 rounded mr-2" value="{% trans 'Save' %}" />
<input type="submit" name="submit_publish" class="cursor-pointer bg-blue hover:bg-blue-dark text-white font-bold py-3 px-4 rounded" value="{% trans 'Publish' %}" />
</div>
</div>

<div class="flex flex-wrap">
<div class="w-1/2 pr-4">
{{form.errors}}
<div class="w-full mb-4 rounded border border-solid border-grey-light shadow bg-white">
<div class="w-full p-4 rounded bg-blue-dark">
<h3 class="heading font-bold text-white">{% trans 'General Settings' %}</h3>
</div>
<div class="w-full p-4">

<!-- General Options for organization management -->
<label for="name" class="font-bold block p-2 mb-2">{% trans "Organizationname" %}</label>
{% trans "Enter the organization's name here" as name_placeholder%}
{% render_field form.name placeholder=name_placeholder class="appearance-none block w-full bg-grey-lighter text-xl text-grey-darkest border border-grey-lighter rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-grey" %}

<label for="slug" class="font-bold block p-2 mb-2">{% trans "Slug of the organization" %}</label>
{% trans "Enter the organization's slug here" as slug_placeholder%}
{% render_field form.slug placeholder=slug_placeholder class="appearance-none block w-full bg-grey-lighter text-xl text-grey-darkest border border-grey-lighter rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-grey" %}

<label for="thumbnail" class="font-bold block p-2 mb-2">{% trans "Thumbnail of the organization" %}</label>
{% trans "Enter the organization's thumbnail here" as thumbnail_placeholder%}
{% render_field form.thumbnail placeholder=thumbnail_placeholder class="appearance-none block w-full bg-grey-lighter text-xl text-grey-darkest border border-grey-lighter rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-grey" %}

</div>
</div>
</div>
<div class="w-1/2 pr-4">
<div class="w-full mb-4 rounded border border-solid border-grey-light shadow bg-white">
<div class="w-full p-4 rounded bg-blue-dark">
<h3 class="heading font-bold text-white">{% trans 'Extended Settings' %}</h3>
</div>
<div class="w-full p-4">
<div class="flex w-full bg-grey-lighter">
<img class="flex w-2/3 p-2 h-40 pt-4 pb-4 justify-start" src="https://tailwindcss.com/img/card-top.jpg">
</img>
<div class="flex h-48 w-1/3 pt-4 p-2 pb-4 justify-end">
{% render_field form.icon id="icon" class="image-field" %}
<label class="w-64 flex flex-col items-center px-4 py-6 bg-white text-blue rounded-lg shadow-lg tracking-wide uppercase border border-blue cursor-pointer hover:bg-blue hover:text-white">
<svg class="w-1/4 h-1/4" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M16.88 9.1A4 4 0 0 1 16 17H5a5 5 0 0 1-1-9.9V7a3 3 0 0 1 4.52-2.59A4.98 4.98 0 0 1 17 8c0 .38-.04.74-.12 1.1zM11 11h3l-4-4-4 4h3v3h2v-3z" />
</svg>
<span class="mt-2 text-base leading-normal">{% trans 'Set icon' %}</span>
<input type='filename' class="hidden" />
</label>
</div>
</div>
</div>

</div>
</div>
</div>
</form>
{% endblock %}

0 comments on commit ab09e86

Please sign in to comment.