Refactor Group extension models into a single OneToOne model

Added group leader field
This commit is contained in:
Basraah 2016-12-04 23:05:04 +10:00
parent 648753a68a
commit bf345361b2
7 changed files with 220 additions and 87 deletions

View File

@ -1,13 +1,15 @@
from __future__ import unicode_literals
from django.contrib import admin
from groupmanagement.models import GroupDescription
from groupmanagement.models import GroupRequest
from groupmanagement.models import HiddenGroup
from groupmanagement.models import OpenGroup
from groupmanagement.models import AuthGroup
admin.site.register(GroupDescription)
class AuthGroupAdmin(admin.ModelAdmin):
"""
Admin model for AuthGroup
"""
filter_horizontal = ('group_leaders',)
admin.site.register(GroupRequest)
admin.site.register(HiddenGroup)
admin.site.register(OpenGroup)
admin.site.register(AuthGroup, AuthGroupAdmin)

View File

@ -0,0 +1,20 @@
from django.contrib.auth.models import Group
from django.conf import settings
class GroupManager:
def __init__(self):
pass
@staticmethod
def get_joinable_groups():
return Group.objects.exclude(authgroup__internal=True)
@staticmethod
def joinable_group(group):
"""
Check if a group is a user joinable group, i.e.
not an internal group for Corp, Alliance, Members etc
:param group: django.contrib.auth.models.Group object
:return: bool True if its joinable, False otherwise
"""
return not group.authgroup.internal

View File

@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2016-12-04 10:25
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
from django.core.exceptions import ObjectDoesNotExist
import django.db.models.deletion
def internal_group(group):
return (
"Corp_" in group.name or
"Alliance_" in group.name or
settings.DEFAULT_AUTH_GROUP in group.name or
settings.DEFAULT_BLUE_GROUP in group.name
)
def combine_group_models(apps, schema_editor):
Group = apps.get_model("auth", "Group")
AuthGroup = apps.get_model("groupmanagement", "AuthGroup")
for group in Group.objects.all():
authgroup = AuthGroup(group=group)
if not hasattr(group, 'hiddengroup'):
authgroup.hidden = False
if hasattr(group, 'opengroup'):
authgroup.open = True
if hasattr(group, 'groupdescription'):
authgroup.description = group.groupdescription.description
authgroup.internal = internal_group(group)
authgroup.save()
def reverse_group_models(apps, schema_editor):
Group = apps.get_model("auth", "Group")
GroupDescription = apps.get_model("groupmanagement", "GroupDescription")
OpenGroup = apps.get_model("groupmanagement", "OpenGroup")
HiddenGroup = apps.get_model("groupmanagement", "HiddenGroup")
for group in Group.objects.all():
if not hasattr(group, 'authgroup') or group.authgroup is None:
continue
if group.authgroup.open:
OpenGroup.objects.get_or_create(group=group)
else:
try:
OpenGroup.objects.get(group=group).delete()
except ObjectDoesNotExist:
pass
if group.authgroup.hidden:
HiddenGroup.objects.get_or_create(group=group)
else:
try:
HiddenGroup.objects.get(group=group).delete()
except ObjectDoesNotExist:
pass
if len(group.authgroup.description):
GroupDescription.objects.update_or_create(group=group,
defaults={'description': group.authgroup.description})
class Migration(migrations.Migration):
dependencies = [
('auth', '0008_alter_user_username_max_length'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('groupmanagement', '0003_default_groups'),
]
operations = [
migrations.CreateModel(
name='AuthGroup',
fields=[
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True,
serialize=False, to='auth.Group')),
('internal', models.BooleanField(default=True, help_text='Internal group, users cannot see, join or request to join this group.<br>Used for groups such as Members, Corp_*, Alliance_* etc.<br><b>Overrides Hidden and Open options when selected.</b>')),
('hidden', models.BooleanField(default=True, help_text='Group is hidden from users but can still join with the correct link.')),
('open', models.BooleanField(default=False, help_text='Group is open and users will be automatically added upon request. <br>If the group is not open users will need their request manually approved.')),
('description', models.CharField(max_length=512, help_text='Description of the group shown to users.', )),
('group_leaders', models.ManyToManyField(related_name='leads_groups', to=settings.AUTH_USER_MODEL, help_text='Group leaders can process group requests for this group specifically. Use the auth.group_management permission to allow a user to manage all groups.',)),
],
),
migrations.RunPython(combine_group_models, reverse_group_models),
migrations.RemoveField(
model_name='groupdescription',
name='group',
),
migrations.RemoveField(
model_name='hiddengroup',
name='group',
),
migrations.RemoveField(
model_name='opengroup',
name='group',
),
migrations.DeleteModel(
name='GroupDescription',
),
migrations.DeleteModel(
name='HiddenGroup',
),
migrations.DeleteModel(
name='OpenGroup',
),
]

View File

@ -3,19 +3,12 @@ from django.utils.encoding import python_2_unicode_compatible
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import Group
from django.db.models.signals import post_save
from django.dispatch import receiver
from eveonline.models import EveCharacter
@python_2_unicode_compatible
class GroupDescription(models.Model):
description = models.CharField(max_length=512)
group = models.OneToOneField(Group)
def __str__(self):
return self.group.name + " - Description"
@python_2_unicode_compatible
class GroupRequest(models.Model):
status = models.CharField(max_length=254)
@ -29,16 +22,57 @@ class GroupRequest(models.Model):
@python_2_unicode_compatible
class HiddenGroup(models.Model):
group = models.OneToOneField(Group)
class AuthGroup(models.Model):
"""
Extends Django Group model with a one-to-one field
Attributes are accessible via group as if they were in the model
e.g. group.authgroup.internal
Logic:
Internal - not requestable by users, at all. Covers Corp_, Alliance_, Members etc groups.
Groups are internal by default
Not Internal and:
Hidden - users cannot view, can request if they have the direct link.
Not Hidden - Users can view and request the group
Open - Users are automatically accepted into the group
Not Open - Users requests must be approved before they are added to the group
"""
group = models.OneToOneField(Group, on_delete=models.CASCADE, primary_key=True)
internal = models.BooleanField(default=True,
help_text="Internal group, users cannot see, join or request to join this group.<br>"
"Used for groups such as Members, Corp_*, Alliance_* etc.<br>"
"<b>Overrides Hidden and Open options when selected.</b>")
hidden = models.BooleanField(default=True,
help_text="Group is hidden from users but can still join with the correct link.")
open = models.BooleanField(default=False,
help_text="Group is open and users will be automatically added upon request. <br>"
"If the group is not open users will need their request manually approved.")
# Group leaders have management access to this group
group_leaders = models.ManyToManyField(User, related_name='leads_groups',
help_text="Group leaders can process group requests for this group "
"specifically. Use the auth.group_management permission to allow "
"a user to manage all groups.")
description = models.CharField(max_length=512, help_text="Description of the group shown to users.")
def __str__(self):
return self.group.name + " - Hidden"
return self.group.name
@python_2_unicode_compatible
class OpenGroup(models.Model):
group = models.OneToOneField(Group)
@receiver(post_save, sender=Group)
def create_auth_group(sender, instance, created, **kwargs):
"""
Creates the AuthGroup model when a group is created
"""
if created:
AuthGroup.objects.create(group=instance)
def __str__(self):
return self.group.name + " - Open"
@receiver(post_save, sender=Group)
def save_auth_group(sender, instance, **kwargs):
"""
Ensures AuthGroup model is saved automatically
"""
instance.authgroup.save()

View File

@ -1,22 +1,18 @@
from __future__ import unicode_literals
from django.shortcuts import render, redirect
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import Group
from django.contrib import messages
from notifications import notify
from groupmanagement.models import GroupDescription
from groupmanagement.managers import GroupManager
from groupmanagement.models import GroupRequest
from groupmanagement.models import HiddenGroup
from groupmanagement.models import OpenGroup
from authentication.models import AuthServicesInfo
from eveonline.managers import EveManager
from django.utils.translation import ugettext_lazy as _
from django.db.models import Count
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.http import Http404
from itertools import chain
import logging
@ -48,8 +44,7 @@ def group_management(request):
def group_membership(request):
logger.debug("group_membership called by user %s" % request.user)
# Get all open and closed groups
groups = [group for group in Group.objects.all().annotate(num_members=Count('user')).order_by('name')
if joinable_group(group)]
groups = Group.objects.exclude(authgroup__internal=True).annotate(num_members=Count('user')).order_by('name')
render_items = {'groups': groups}
@ -64,7 +59,7 @@ def group_membership_list(request, group_id):
group = Group.objects.get(id=group_id)
# Check its a joinable group i.e. not corp or internal
if not joinable_group(group):
if not GroupManager.joinable_group(group):
raise PermissionDenied
except ObjectDoesNotExist:
@ -214,31 +209,16 @@ def group_leave_reject_request(request, group_request_id):
@login_required
def groups_view(request):
logger.debug("groups_view called by user %s" % request.user)
paired_list = []
groups = []
for group in Group.objects.all():
# Check if group is a corp
if not joinable_group(group):
pass
elif HiddenGroup.objects.filter(group=group).exists():
pass
else:
# Get the descriptionn
group_desc = GroupDescription.objects.filter(group=group)
for group in GroupManager.get_joinable_groups():
# Exclude hidden
if not group.authgroup.hidden:
group_request = GroupRequest.objects.filter(user=request.user).filter(group=group)
if group_desc:
if group_request:
paired_list.append((group, group_desc[0], group_request[0]))
else:
paired_list.append((group, group_desc[0], ""))
else:
if group_request:
paired_list.append((group, "", group_request[0]))
else:
paired_list.append((group, "", ""))
groups.append({'group': group, 'request': group_request[0] if group_request else None})
render_items = {'pairs': paired_list}
render_items = {'groups': groups}
return render(request, 'registered/groups.html', context=render_items)
@ -246,12 +226,12 @@ def groups_view(request):
def group_request_add(request, group_id):
logger.debug("group_request_add called by user %s for group id %s" % (request.user, group_id))
group = Group.objects.get(id=group_id)
if not joinable_group(group):
if not GroupManager.joinable_group(group):
logger.warning("User %s attempted to join group id %s but it is not a joinable group" %
(request.user, group_id))
messages.warning(request, "You cannot join that group")
return redirect('auth_groups')
if OpenGroup.objects.filter(group=group).exists():
if group.authgroup.open:
logger.info("%s joining %s as is an open group" % (request.user, group))
request.user.groups.add(group)
return redirect("auth_groups")
@ -272,7 +252,7 @@ def group_request_add(request, group_id):
def group_request_leave(request, group_id):
logger.debug("group_request_leave called by user %s for group id %s" % (request.user, group_id))
group = Group.objects.get(id=group_id)
if not joinable_group(group):
if not GroupManager.joinable_group(group):
logger.warning("User %s attempted to leave group id %s but it is not a joinable group" %
(request.user, group_id))
messages.warning(request, "You cannot leave that group")
@ -282,7 +262,7 @@ def group_request_leave(request, group_id):
(request.user, group_id))
messages.warning(request, "You are not a member of that group")
return redirect('auth_groups')
if OpenGroup.objects.filter(group=group).exists():
if group.authgroup.open:
logger.info("%s leaving %s as is an open group" % (request.user, group))
request.user.groups.remove(group)
return redirect("auth_groups")
@ -297,18 +277,3 @@ def group_request_leave(request, group_id):
logger.info("Created group leave request for user %s to group %s" % (request.user, Group.objects.get(id=group_id)))
messages.success(request, 'Applied to leave group %s.' % group)
return redirect("auth_groups")
def joinable_group(group):
"""
Check if a group is a user joinable group, i.e.
not an internal group for Corp, Alliance, Members etc
:param group: django.contrib.auth.models.Group object
:return: bool True if its joinable, False otherwise
"""
return (
"Corp_" not in group.name and
"Alliance_" not in group.name and
settings.DEFAULT_AUTH_GROUP not in group.name and
settings.DEFAULT_BLUE_GROUP not in group.name
)

View File

@ -24,11 +24,11 @@
{% for group in groups %}
<tr>
<td class="text-center">{{ group.name }}</td>
<td class="text-center">{{ group.groupdescription.description }}</td>
<td class="text-center">{{ group.authgroup.description }}</td>
<td class="text-center">
{% if group.hiddengroup %}
{% if group.authgroup.hidden %}
<span class="label label-info">{% trans "Hidden" %}</span>
{% elif group.opengroup %}
{% elif group.authgroup.open %}
<span class="label label-success">{% trans "Open" %}</span>
{% else %}
<span class="label label-default">{% trans "Requestable" %}</span>

View File

@ -11,36 +11,36 @@
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Available Groups" %}</h1>
{% if STATE == MEMBER_STATE or user.is_superuser %}
{% if pairs %}
{% if groups %}
<table class="table">
<tr>
<th class="text-center">{% trans "GroupName" %}</th>
<th class="text-center">{% trans "GroupDesc" %}</th>
<th class="text-center">{% trans "Name" %}</th>
<th class="text-center">{% trans "Description" %}</th>
<th class="text-center">{% trans "Action" %}</th>
</tr>
{% for pair in pairs %}
{% for g in groups %}
<tr>
<td class="text-center">{{ pair.0.name }}</td>
<td class="text-center">{{ pair.1.description }}</td>
<td class="text-center">{{ g.group.name }}</td>
<td class="text-center">{{ g.group.authgroup.description }}</td>
<td class="text-center">
{% if pair.0 in user.groups.all %}
{% if pair.2 == "" %}
<a href="{% url 'auth_group_request_leave' pair.0.id %}" class="btn btn-danger">
{% if g.group in user.groups.all %}
{% if not g.request %}
<a href="{% url 'auth_group_request_leave' g.group.id %}" class="btn btn-danger">
{% trans "Leave" %}
</a>
{% else %}
<button type="button" class="btn btn-primary" disabled>
{{ pair.2.status }}
{{ g.request.status }}
</button>
{% endif %}
{% elif pair.2 == "" %}
<a href="{% url 'auth_group_request_add' pair.0.id %}" class="btn btn-success">
{% elif not g.request %}
<a href="{% url 'auth_group_request_add' g.group.id %}" class="btn btn-success">
{% trans "Request" %}
</a>
{% else %}
<button type="button" class="btn btn-primary" disabled>
{{ pair.2.status }}
{{ g.request.status }}
</button>
{% endif %}
</td>