mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-22 06:03:20 +00:00
Some refactoring and misc improvements
This commit is contained in:
parent
ed080b76a2
commit
a14d940db2
28
README.md
28
README.md
@ -19,9 +19,33 @@ There is a Doxyfile at the root of the project, meaning that if you have Doxygen
|
|||||||
generate a complete HTML documentation that will be available in the *./doc/html/* folder.
|
generate a complete HTML documentation that will be available in the *./doc/html/* folder.
|
||||||
|
|
||||||
### Dependencies:
|
### Dependencies:
|
||||||
* Django 1.8
|
See requirements.txt
|
||||||
* Pillow
|
|
||||||
|
|
||||||
The development is done with sqlite, but it is advised to set a more robust DBMS for production (Postgresql for example)
|
The development is done with sqlite, but it is advised to set a more robust DBMS for production (Postgresql for example)
|
||||||
|
|
||||||
|
|
||||||
|
### Misc about development
|
||||||
|
|
||||||
|
#### Controlling the rights
|
||||||
|
|
||||||
|
When you need to protect an object, there are three levels:
|
||||||
|
* Editing the object properties
|
||||||
|
* Editing the object various values
|
||||||
|
* Viewing the object
|
||||||
|
|
||||||
|
Now you have many solutions in your model:
|
||||||
|
* You can define a `is_owned_by(self, user)`, a `can_be_edited_by(self, user)`, and/or a `can_be_viewed_by(self, user)`
|
||||||
|
method, each returning True is the user passed can edit/view the object, False otherwise.
|
||||||
|
This allows you to make complex request when the group solution is not powerful enough.
|
||||||
|
It's useful too when you want to define class-wide permissions, e.g. the club members, that are viewable only for
|
||||||
|
Subscribers.
|
||||||
|
* You can add an `owner_group` field, as a ForeignKey to Group. Second is an `edit_groups` field, as a ManyToMany to
|
||||||
|
Group, and third is a `view_groups`, same as for edit.
|
||||||
|
|
||||||
|
Finally, when building a class based view, which is highly advised, you just have to inherit it from CanEditPropMixin,
|
||||||
|
CanEditMixin, or CanViewMixin, which are located in core.views. Your view will then be protected using either the
|
||||||
|
appropriate group fields, or the right method to check user permissions.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,12 +14,12 @@ class Migration(migrations.Migration):
|
|||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='club',
|
model_name='club',
|
||||||
name='edit_group',
|
name='edit_groups',
|
||||||
field=models.ManyToManyField(to='core.Group', blank=True, related_name='editable_club'),
|
field=models.ManyToManyField(to='core.Group', blank=True, related_name='editable_club'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='club',
|
model_name='club',
|
||||||
name='view_group',
|
name='view_groups',
|
||||||
field=models.ManyToManyField(to='core.Group', blank=True, related_name='viewable_club'),
|
field=models.ManyToManyField(to='core.Group', blank=True, related_name='viewable_club'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -6,6 +6,7 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from core.models import User, Group
|
from core.models import User, Group
|
||||||
|
from subscription.models import Subscriber
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
@ -31,8 +32,8 @@ class Club(models.Model):
|
|||||||
# email = models.EmailField(_('email address'), unique=True) # This should, and will be generated automatically
|
# email = models.EmailField(_('email address'), unique=True) # This should, and will be generated automatically
|
||||||
owner_group = models.ForeignKey(Group, related_name="owned_club",
|
owner_group = models.ForeignKey(Group, related_name="owned_club",
|
||||||
default=settings.AE_GROUPS['root']['id'])
|
default=settings.AE_GROUPS['root']['id'])
|
||||||
edit_group = models.ManyToManyField(Group, related_name="editable_club", blank=True)
|
edit_groups = models.ManyToManyField(Group, related_name="editable_club", blank=True)
|
||||||
view_group = models.ManyToManyField(Group, related_name="viewable_club", blank=True)
|
view_groups = models.ManyToManyField(Group, related_name="viewable_club", blank=True)
|
||||||
|
|
||||||
def check_loop(self):
|
def check_loop(self):
|
||||||
"""Raise a validation error when a loop is found within the parent list"""
|
"""Raise a validation error when a loop is found within the parent list"""
|
||||||
@ -53,6 +54,38 @@ class Club(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('club:club_view', kwargs={'club_id': self.id})
|
return reverse('club:club_view', kwargs={'club_id': self.id})
|
||||||
|
|
||||||
|
def is_owned_by(self, user):
|
||||||
|
"""
|
||||||
|
Method to see if that object can be super edited by the given user
|
||||||
|
"""
|
||||||
|
if user.groups.filter(name=settings.AE_GROUPS['board']['name']).exists():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def can_be_edited_by(self, user):
|
||||||
|
"""
|
||||||
|
Method to see if that object can be edited by the given user
|
||||||
|
"""
|
||||||
|
ms = self.get_membership_for(user)
|
||||||
|
if ms is not None and ms.role >= 7:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def can_be_viewed_by(self, user):
|
||||||
|
"""
|
||||||
|
Method to see if that object can be seen by the given user
|
||||||
|
"""
|
||||||
|
sub = Subscriber.objects.filter(pk=user.pk).first()
|
||||||
|
if sub is None:
|
||||||
|
return False
|
||||||
|
return sub.is_subscribed()
|
||||||
|
|
||||||
|
def get_membership_for(self, user):
|
||||||
|
"""
|
||||||
|
Returns the current membership the given user
|
||||||
|
"""
|
||||||
|
return self.members.filter(user=user.id).filter(end_date=None).first()
|
||||||
|
|
||||||
class Membership(models.Model):
|
class Membership(models.Model):
|
||||||
"""
|
"""
|
||||||
The Membership class makes the connection between User and Clubs
|
The Membership class makes the connection between User and Clubs
|
||||||
|
@ -3,17 +3,16 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<h3>Club</h3>
|
<h3>Club</h3>
|
||||||
<p><a href="{{ url('club:club_list') }}">Back to list</a></p>
|
<p><a href="{{ url('club:club_list') }}">Back to list</a></p>
|
||||||
{% if user.can_edit(club) %}
|
{% if can_edit(club, user) %}
|
||||||
<p><a href="{{ url('club:club_edit', club_id=club.pk) }}">Edit</a></p>
|
<p><a href="{{ url('club:club_edit', club_id=club.pk) }}">Edit</a></p>
|
||||||
<p><a href="{{ url('club:club_members', club_id=club.pk) }}">Edit members</a></p>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.is_owner(club) or user.membership.filter(end_date=None).filter(club=club.id).first() is not none %}
|
{% if can_edit_prop(club, user) %}
|
||||||
<p><a href="{{ url('club:club_prop', club_id=club.pk) }}">Prop</a>
|
<p><a href="{{ url('club:club_prop', club_id=club.pk) }}">Prop</a>
|
||||||
TODO FIXME: this code sucks and is just a test!
|
|
||||||
We need something really easy to manage the views rights regarding the subscribing status and the membership of
|
|
||||||
someone!
|
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if can_view(club, user) %}
|
||||||
|
<p><a href="{{ url('club:club_members', club_id=club.pk) }}">Members</a></p>
|
||||||
|
{% endif %}
|
||||||
<h3>{{ club.name }}</h3>
|
<h3>{{ club.name }}</h3>
|
||||||
<p>{{ club.address }}</p>
|
<p>{{ club.address }}</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -6,7 +6,7 @@ urlpatterns = [
|
|||||||
url(r'^$', ClubListView.as_view(), name='club_list'),
|
url(r'^$', ClubListView.as_view(), name='club_list'),
|
||||||
url(r'^(?P<club_id>[0-9]+)/$', ClubView.as_view(), name='club_view'),
|
url(r'^(?P<club_id>[0-9]+)/$', ClubView.as_view(), name='club_view'),
|
||||||
url(r'^(?P<club_id>[0-9]+)/edit$', ClubEditView.as_view(), name='club_edit'),
|
url(r'^(?P<club_id>[0-9]+)/edit$', ClubEditView.as_view(), name='club_edit'),
|
||||||
url(r'^(?P<club_id>[0-9]+)/members$', ClubEditMembersView.as_view(), name='club_members'),
|
url(r'^(?P<club_id>[0-9]+)/members$', ClubMembersView.as_view(), name='club_members'),
|
||||||
url(r'^(?P<club_id>[0-9]+)/prop$', ClubEditPropView.as_view(), name='club_prop'),
|
url(r'^(?P<club_id>[0-9]+)/prop$', ClubEditPropView.as_view(), name='club_prop'),
|
||||||
#url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='club_tools'),
|
#url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='club_tools'),
|
||||||
|
|
||||||
|
@ -3,10 +3,12 @@ from django.shortcuts import render
|
|||||||
from django.views.generic import ListView, DetailView
|
from django.views.generic import ListView, DetailView
|
||||||
from django.views.generic.edit import UpdateView, CreateView
|
from django.views.generic.edit import UpdateView, CreateView
|
||||||
from django.forms import CheckboxSelectMultiple
|
from django.forms import CheckboxSelectMultiple
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin
|
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin
|
||||||
from club.models import Club, Membership
|
from club.models import Club, Membership
|
||||||
|
from subscription.views import SubscriberMixin
|
||||||
|
|
||||||
class ClubListView(CanViewMixin, ListView):
|
class ClubListView(CanViewMixin, ListView):
|
||||||
model = Club
|
model = Club
|
||||||
@ -31,23 +33,31 @@ class ClubMemberForm(forms.ModelForm):
|
|||||||
fields = ['user', 'role']
|
fields = ['user', 'role']
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
print(self.__dict__)
|
ret = super(ClubMemberForm, self).clean()
|
||||||
# TODO: see how to get access to request.user! We need some right validation somewhere!
|
ms = self.instance.club.get_membership_for(self._user)
|
||||||
return super(ClubMemberForm, self).clean()
|
if ms is not None and ms.role >= self.cleaned_data['role']:
|
||||||
|
return ret
|
||||||
|
raise ValidationError("You do not have the permission to do that")
|
||||||
|
|
||||||
class ClubEditMembersView(CanEditMixin, UpdateView):
|
class ClubMembersView(CanViewMixin, UpdateView):
|
||||||
model = Club
|
model = Club
|
||||||
pk_url_kwarg = "club_id"
|
pk_url_kwarg = "club_id"
|
||||||
form_class = ClubMemberForm
|
form_class = ClubMemberForm
|
||||||
template_name = 'club/club_members.jinja'
|
template_name = 'club/club_members.jinja'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(ClubMembersView, self).__init__(*args, **kwargs)
|
||||||
|
# TODO FIXME: error forbidden when adding new member to club, because self.object changes to the Membership object
|
||||||
|
# somewhere!!!
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
form = super(ClubEditMembersView, self).get_form()
|
form = super(ClubMembersView, self).get_form()
|
||||||
if 'user' in form.data and form.data.get('user') != '': # Load an existing membership if possible
|
if 'user' in form.data and form.data.get('user') != '': # Load an existing membership if possible
|
||||||
form.instance = Membership.objects.filter(club=self.object).filter(user=form.data.get('user')).filter(end_date=None).first()
|
form.instance = Membership.objects.filter(club=self.object).filter(user=form.data.get('user')).filter(end_date=None).first()
|
||||||
if form.instance is None: # Instanciate a new membership
|
if form.instance is None: # Instanciate a new membership
|
||||||
form.instance = Membership(club=self.object, user=self.request.user)
|
form.instance = Membership(club=self.object, user=self.request.user)
|
||||||
form.initial = {'user': self.request.user}
|
form.initial = {'user': self.request.user}
|
||||||
|
form._user = self.request.user
|
||||||
return form
|
return form
|
||||||
|
|
||||||
class ClubEditPropView(CanEditPropMixin, UpdateView):
|
class ClubEditPropView(CanEditPropMixin, UpdateView):
|
||||||
|
@ -1 +1 @@
|
|||||||
[{"pk": 1, "model": "core.page", "fields": {"full_name": "guy2", "owner_group": 1, "parent": null, "edit_group": [], "name": "guy2", "view_group": []}}, {"pk": 2, "model": "core.page", "fields": {"full_name": "guy2/bibou", "owner_group": 1, "parent": 1, "edit_group": [], "name": "bibou", "view_group": []}}, {"pk": 3, "model": "core.page", "fields": {"full_name": "guy2/bibou/troll", "owner_group": 1, "parent": 2, "edit_group": [], "name": "troll", "view_group": []}}, {"pk": 4, "model": "core.page", "fields": {"full_name": "guy", "owner_group": 1, "parent": null, "edit_group": [1], "name": "guy", "view_group": [1]}}, {"pk": 5, "model": "core.page", "fields": {"full_name": "bibou", "owner_group": 3, "parent": null, "edit_group": [1], "name": "bibou", "view_group": []}}, {"pk": 6, "model": "core.page", "fields": {"full_name": "guy2/guy", "owner_group": 1, "parent": 1, "edit_group": [], "name": "guy", "view_group": []}}]
|
[{"pk": 1, "model": "core.page", "fields": {"full_name": "guy2", "owner_group": 1, "parent": null, "edit_groups": [], "name": "guy2", "view_groups": []}}, {"pk": 2, "model": "core.page", "fields": {"full_name": "guy2/bibou", "owner_group": 1, "parent": 1, "edit_group": [], "name": "bibou", "view_group": []}}, {"pk": 3, "model": "core.page", "fields": {"full_name": "guy2/bibou/troll", "owner_group": 1, "parent": 2, "edit_group": [], "name": "troll", "view_group": []}}, {"pk": 4, "model": "core.page", "fields": {"full_name": "guy", "owner_group": 1, "parent": null, "edit_group": [1], "name": "guy", "view_group": [1]}}, {"pk": 5, "model": "core.page", "fields": {"full_name": "bibou", "owner_group": 3, "parent": null, "edit_group": [1], "name": "bibou", "view_group": []}}, {"pk": 6, "model": "core.page", "fields": {"full_name": "guy2/guy", "owner_group": 1, "parent": 1, "edit_group": [], "name": "guy", "view_group": []}}]
|
@ -16,9 +16,10 @@ class Command(BaseCommand):
|
|||||||
parser.add_argument('--prod', action="store_true")
|
parser.add_argument('--prod', action="store_true")
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
|
root_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
||||||
try:
|
try:
|
||||||
os.unlink(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), 'db.sqlite3'))
|
os.unlink(os.path.join(root_path, 'db.sqlite3'))
|
||||||
os.mkdir(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))+'/data')
|
os.mkdir(os.path.join(root_path)+'/data')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
call_command('migrate')
|
call_command('migrate')
|
||||||
@ -37,37 +38,41 @@ class Command(BaseCommand):
|
|||||||
# Here we add a lot of test datas, that are not necessary for the Sith, but that provide a basic development environment
|
# Here we add a lot of test datas, that are not necessary for the Sith, but that provide a basic development environment
|
||||||
if not options['prod']:
|
if not options['prod']:
|
||||||
print("Dev mode, adding some test data")
|
print("Dev mode, adding some test data")
|
||||||
|
# Adding user Skia
|
||||||
s = User(username='skia', last_name="Kia", first_name="S'",
|
s = User(username='skia', last_name="Kia", first_name="S'",
|
||||||
email="skia@git.an",
|
email="skia@git.an",
|
||||||
date_of_birth="1942-06-12",
|
date_of_birth="1942-06-12")
|
||||||
is_superuser=True, is_staff=True)
|
|
||||||
s.set_password("plop")
|
s.set_password("plop")
|
||||||
s.save()
|
s.save()
|
||||||
|
# Adding user Guy
|
||||||
u = User(username='guy', last_name="Carlier", first_name="Guy",
|
u = User(username='guy', last_name="Carlier", first_name="Guy",
|
||||||
email="guy@git.an",
|
email="guy@git.an",
|
||||||
date_of_birth="1942-06-12",
|
date_of_birth="1942-06-12",
|
||||||
is_superuser=False, is_staff=False)
|
is_superuser=False, is_staff=False)
|
||||||
u.set_password("plop")
|
u.set_password("plop")
|
||||||
u.save()
|
u.save()
|
||||||
|
# Adding syntax help page
|
||||||
p = Page(name='Aide_sur_la_syntaxe')
|
p = Page(name='Aide_sur_la_syntaxe')
|
||||||
p.set_lock(s)
|
p.set_lock(s)
|
||||||
p.save()
|
p.save()
|
||||||
PageRev(page=p, title="Aide sur la syntaxe", author=s, content="""
|
PageRev(page=p, title="Aide sur la syntaxe", author=s, content="""
|
||||||
Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site.
|
Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site.
|
||||||
""").save()
|
""").save()
|
||||||
|
# Adding README
|
||||||
# Accounting test values:
|
p = Page(name='README')
|
||||||
Customer(user=s, account_id="6568j").save()
|
p.set_lock(s)
|
||||||
p = ProductType(name="Bières bouteilles")
|
|
||||||
p.save()
|
p.save()
|
||||||
Product(name="Barbar", code="BARB", product_type=p, purchase_price="1.50", selling_price="1.7",
|
p.view_groups=[settings.AE_GROUPS['public']['id']]
|
||||||
special_selling_price="1.6").save()
|
p.set_lock(s)
|
||||||
GeneralJournal(start_date="2015-06-12", name="A15").save()
|
p.save()
|
||||||
|
with open(os.path.join(root_path)+'/README.md', 'r') as rm:
|
||||||
|
PageRev(page=p, title="REAMDE", author=s, content=rm.read()).save()
|
||||||
|
|
||||||
# Subscription
|
# Subscription
|
||||||
Subscription(member=Subscriber.objects.filter(pk=s.pk).first(), subscription_type=list(settings.AE_SUBSCRIPTIONS.keys())[0],
|
Subscription(member=Subscriber.objects.filter(pk=s.pk).first(), subscription_type=list(settings.AE_SUBSCRIPTIONS.keys())[0],
|
||||||
payment_method=settings.AE_PAYMENT_METHOD[0]).save()
|
payment_method=settings.AE_PAYMENT_METHOD[0]).save()
|
||||||
|
|
||||||
|
# Clubs
|
||||||
Club(name="Bibo'UT", unix_name="bibout",
|
Club(name="Bibo'UT", unix_name="bibout",
|
||||||
address="46 de la Boustifaille", parent=ae).save()
|
address="46 de la Boustifaille", parent=ae).save()
|
||||||
guyut = Club(name="Guy'UT", unix_name="guyut",
|
guyut = Club(name="Guy'UT", unix_name="guyut",
|
||||||
@ -77,3 +82,12 @@ Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site.
|
|||||||
address="Woenzel", parent=guyut).save()
|
address="Woenzel", parent=guyut).save()
|
||||||
Club(name="BdF", unix_name="bdf",
|
Club(name="BdF", unix_name="bdf",
|
||||||
address="Guyéuéyuéyuyé").save()
|
address="Guyéuéyuéyuyé").save()
|
||||||
|
|
||||||
|
# Accounting test values:
|
||||||
|
Customer(user=s, account_id="6568j").save()
|
||||||
|
p = ProductType(name="Bières bouteilles")
|
||||||
|
p.save()
|
||||||
|
Product(name="Barbar", code="BARB", product_type=p, purchase_price="1.50", selling_price="1.7",
|
||||||
|
special_selling_price="1.6").save()
|
||||||
|
GeneralJournal(start_date="2015-06-12", name="A15").save()
|
||||||
|
|
||||||
|
@ -54,10 +54,10 @@ class Migration(migrations.Migration):
|
|||||||
('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
|
('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
|
||||||
('name', models.CharField(max_length=30, verbose_name='page name')),
|
('name', models.CharField(max_length=30, verbose_name='page name')),
|
||||||
('_full_name', models.CharField(max_length=255, verbose_name='page name', blank=True)),
|
('_full_name', models.CharField(max_length=255, verbose_name='page name', blank=True)),
|
||||||
('edit_group', models.ManyToManyField(to='core.Group', related_name='editable_page', blank=True)),
|
('edit_groups', models.ManyToManyField(to='core.Group', related_name='editable_page', blank=True)),
|
||||||
('owner_group', models.ForeignKey(to='core.Group', related_name='owned_page', default=1)),
|
('owner_group', models.ForeignKey(to='core.Group', related_name='owned_page', default=1)),
|
||||||
('parent', models.ForeignKey(to='core.Page', on_delete=django.db.models.deletion.SET_NULL, null=True, related_name='children', blank=True)),
|
('parent', models.ForeignKey(to='core.Page', on_delete=django.db.models.deletion.SET_NULL, null=True, related_name='children', blank=True)),
|
||||||
('view_group', models.ManyToManyField(to='core.Group', related_name='viewable_page', blank=True)),
|
('view_groups', models.ManyToManyField(to='core.Group', related_name='viewable_page', blank=True)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'permissions': (('change_prop_page', "Can change the page's properties (groups, ...)"), ('view_page', 'Can view the page')),
|
'permissions': (('change_prop_page', "Can change the page's properties (groups, ...)"), ('view_page', 'Can view the page')),
|
||||||
@ -79,7 +79,7 @@ class Migration(migrations.Migration):
|
|||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='user',
|
model_name='user',
|
||||||
name='edit_group',
|
name='edit_groups',
|
||||||
field=models.ManyToManyField(to='core.Group', related_name='editable_user', blank=True),
|
field=models.ManyToManyField(to='core.Group', related_name='editable_user', blank=True),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
@ -99,7 +99,7 @@ class Migration(migrations.Migration):
|
|||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='user',
|
model_name='user',
|
||||||
name='view_group',
|
name='view_groups',
|
||||||
field=models.ManyToManyField(to='core.Group', related_name='viewable_user', blank=True),
|
field=models.ManyToManyField(to='core.Group', related_name='viewable_user', blank=True),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
|
@ -65,8 +65,8 @@ class User(AbstractBaseUser, PermissionsMixin):
|
|||||||
date_joined = models.DateField(_('date joined'), auto_now_add=True)
|
date_joined = models.DateField(_('date joined'), auto_now_add=True)
|
||||||
owner_group = models.ForeignKey(Group, related_name="owned_user",
|
owner_group = models.ForeignKey(Group, related_name="owned_user",
|
||||||
default=settings.AE_GROUPS['root']['id'])
|
default=settings.AE_GROUPS['root']['id'])
|
||||||
edit_group = models.ManyToManyField(Group, related_name="editable_user", blank=True)
|
edit_groups = models.ManyToManyField(Group, related_name="editable_user", blank=True)
|
||||||
view_group = models.ManyToManyField(Group, related_name="viewable_user", blank=True)
|
view_groups = models.ManyToManyField(Group, related_name="viewable_user", blank=True)
|
||||||
|
|
||||||
objects = UserManager()
|
objects = UserManager()
|
||||||
|
|
||||||
@ -159,6 +159,8 @@ class User(AbstractBaseUser, PermissionsMixin):
|
|||||||
self.has_perm(obj.__class__.__module__.split('.')[0]+".change_prop_"+obj.__class__.__name__.lower()) or
|
self.has_perm(obj.__class__.__module__.split('.')[0]+".change_prop_"+obj.__class__.__name__.lower()) or
|
||||||
self.groups.filter(id=settings.AE_GROUPS['root']['id']).exists()):
|
self.groups.filter(id=settings.AE_GROUPS['root']['id']).exists()):
|
||||||
return True
|
return True
|
||||||
|
if hasattr(obj, "is_owned_by") and obj.is_owned_by(self):
|
||||||
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def can_edit(self, obj):
|
def can_edit(self, obj):
|
||||||
@ -167,12 +169,14 @@ class User(AbstractBaseUser, PermissionsMixin):
|
|||||||
"""
|
"""
|
||||||
if self.is_owner(obj):
|
if self.is_owner(obj):
|
||||||
return True
|
return True
|
||||||
if hasattr(obj, "edit_group"):
|
if hasattr(obj, "edit_groups"):
|
||||||
for g in obj.edit_group.all():
|
for g in obj.edit_groups.all():
|
||||||
if self.groups.filter(name=g.name).exists():
|
if self.groups.filter(name=g.name).exists():
|
||||||
return True
|
return True
|
||||||
if isinstance(obj, User) and obj == self:
|
if isinstance(obj, User) and obj == self:
|
||||||
return True
|
return True
|
||||||
|
if hasattr(obj, "can_be_edited_by") and obj.can_be_edited_by(self):
|
||||||
|
return True
|
||||||
if self.has_perm(obj.__class__.__module__.split('.')[0]+".change_"+obj.__class__.__name__.lower()):
|
if self.has_perm(obj.__class__.__module__.split('.')[0]+".change_"+obj.__class__.__name__.lower()):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@ -183,10 +187,12 @@ class User(AbstractBaseUser, PermissionsMixin):
|
|||||||
"""
|
"""
|
||||||
if self.can_edit(obj):
|
if self.can_edit(obj):
|
||||||
return True
|
return True
|
||||||
if hasattr(obj, "view_group"):
|
if hasattr(obj, "view_groups"):
|
||||||
for g in obj.view_group.all():
|
for g in obj.view_groups.all():
|
||||||
if self.groups.filter(name=g.name).exists():
|
if self.groups.filter(name=g.name).exists():
|
||||||
return True
|
return True
|
||||||
|
if hasattr(obj, "can_be_viewed_by") and obj.can_be_viewed_by(self):
|
||||||
|
return True
|
||||||
if self.has_perm(obj.__class__.__module__.split('.')[0]+".view_"+obj.__class__.__name__.lower()):
|
if self.has_perm(obj.__class__.__module__.split('.')[0]+".view_"+obj.__class__.__name__.lower()):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@ -202,7 +208,7 @@ class AnonymousUser(AuthAnonymousUser):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def can_view(self, obj):
|
def can_view(self, obj):
|
||||||
if obj.view_group.filter(pk=settings.AE_GROUPS['public']['id']).exists():
|
if obj.view_groups.filter(pk=settings.AE_GROUPS['public']['id']).exists():
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -236,8 +242,8 @@ class Page(models.Model):
|
|||||||
_full_name = models.CharField(_('page name'), max_length=255, blank=True)
|
_full_name = models.CharField(_('page name'), max_length=255, blank=True)
|
||||||
owner_group = models.ForeignKey(Group, related_name="owned_page",
|
owner_group = models.ForeignKey(Group, related_name="owned_page",
|
||||||
default=settings.AE_GROUPS['root']['id'])
|
default=settings.AE_GROUPS['root']['id'])
|
||||||
edit_group = models.ManyToManyField(Group, related_name="editable_page", blank=True)
|
edit_groups = models.ManyToManyField(Group, related_name="editable_page", blank=True)
|
||||||
view_group = models.ManyToManyField(Group, related_name="viewable_page", blank=True)
|
view_groups = models.ManyToManyField(Group, related_name="viewable_page", blank=True)
|
||||||
lock_mutex = {}
|
lock_mutex = {}
|
||||||
|
|
||||||
|
|
||||||
@ -397,10 +403,10 @@ class PageRev(models.Model):
|
|||||||
def __getattribute__(self, attr):
|
def __getattribute__(self, attr):
|
||||||
if attr == "owner_group":
|
if attr == "owner_group":
|
||||||
return self.page.owner_group
|
return self.page.owner_group
|
||||||
elif attr == "edit_group":
|
elif attr == "edit_groups":
|
||||||
return self.page.edit_group
|
return self.page.edit_groups
|
||||||
elif attr == "view_group":
|
elif attr == "view_groups":
|
||||||
return self.page.view_group
|
return self.page.view_groups
|
||||||
elif attr == "unset_lock":
|
elif attr == "unset_lock":
|
||||||
return self.page.unset_lock
|
return self.page.unset_lock
|
||||||
else:
|
else:
|
||||||
|
@ -12,6 +12,20 @@ def forbidden(request):
|
|||||||
def not_found(request):
|
def not_found(request):
|
||||||
return render(request, "core/404.jinja")
|
return render(request, "core/404.jinja")
|
||||||
|
|
||||||
|
def can_edit_prop(obj, user):
|
||||||
|
if obj is None or user.is_owner(obj):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def can_edit(obj, user):
|
||||||
|
if obj is None or user.can_edit(obj):
|
||||||
|
return True
|
||||||
|
return can_edit_prop(obj, user)
|
||||||
|
|
||||||
|
def can_view(obj, user):
|
||||||
|
if obj is None or user.can_view(obj):
|
||||||
|
return True
|
||||||
|
return can_edit(obj, user)
|
||||||
|
|
||||||
class CanEditPropMixin(View):
|
class CanEditPropMixin(View):
|
||||||
"""
|
"""
|
||||||
@ -22,8 +36,11 @@ class CanEditPropMixin(View):
|
|||||||
"""
|
"""
|
||||||
def dispatch(self, request, *arg, **kwargs):
|
def dispatch(self, request, *arg, **kwargs):
|
||||||
res = super(CanEditPropMixin, self).dispatch(request, *arg, **kwargs)
|
res = super(CanEditPropMixin, self).dispatch(request, *arg, **kwargs)
|
||||||
if ((hasattr(self, 'object') and (self.object is None or self.request.user.is_owner(self.object))) or
|
if hasattr(self, 'object'):
|
||||||
(hasattr(self, 'object_list') and (self.object_list is None or self.object_list is [] or self.request.user.is_owner(self.object_list[0])))):
|
obj = self.object
|
||||||
|
elif hasattr(self, 'object_list'):
|
||||||
|
obj = self.object_list[0] if self.object_list else None
|
||||||
|
if can_edit_prop(obj, self.request.user):
|
||||||
return res
|
return res
|
||||||
try: # Always unlock when 403
|
try: # Always unlock when 403
|
||||||
self.object.unset_lock()
|
self.object.unset_lock()
|
||||||
@ -32,35 +49,38 @@ class CanEditPropMixin(View):
|
|||||||
|
|
||||||
class CanEditMixin(View):
|
class CanEditMixin(View):
|
||||||
"""
|
"""
|
||||||
This view makes exactly the same this as its direct parent, but checks the group on the edit_group field of the
|
This view makes exactly the same this as its direct parent, but checks the group on the edit_groups field of the
|
||||||
object
|
object
|
||||||
"""
|
"""
|
||||||
def dispatch(self, request, *arg, **kwargs):
|
def dispatch(self, request, *arg, **kwargs):
|
||||||
# TODO: WIP: fix permissions with exceptions!
|
|
||||||
res = super(CanEditMixin, self).dispatch(request, *arg, **kwargs)
|
res = super(CanEditMixin, self).dispatch(request, *arg, **kwargs)
|
||||||
if ((hasattr(self, 'object') and (self.object is None or self.request.user.can_edit(self.object))) or
|
if hasattr(self, 'object'):
|
||||||
(hasattr(self, 'object_list') and (self.object_list is None or self.object_list is [] or self.request.user.can_edit(self.object_list[0])))):
|
obj = self.object
|
||||||
|
elif hasattr(self, 'object_list'):
|
||||||
|
obj = self.object_list[0] if self.object_list else None
|
||||||
|
if can_edit(obj, self.request.user):
|
||||||
return res
|
return res
|
||||||
try: # Always unlock when 403
|
try: # Always unlock when 403
|
||||||
self.object.unset_lock()
|
self.object.unset_lock()
|
||||||
except: pass
|
except: pass
|
||||||
print("CanEditMixin 403")
|
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
class CanViewMixin(View):
|
class CanViewMixin(View):
|
||||||
"""
|
"""
|
||||||
This view still makes exactly the same this as its direct parent, but checks the group on the view_group field of
|
This view still makes exactly the same this as its direct parent, but checks the group on the view_groups field of
|
||||||
the object
|
the object
|
||||||
"""
|
"""
|
||||||
def dispatch(self, request, *arg, **kwargs):
|
def dispatch(self, request, *arg, **kwargs):
|
||||||
res = super(CanViewMixin, self).dispatch(request, *arg, **kwargs)
|
res = super(CanViewMixin, self).dispatch(request, *arg, **kwargs)
|
||||||
if ((hasattr(self, 'object') and (self.object is None or self.request.user.can_view(self.object))) or
|
if hasattr(self, 'object'):
|
||||||
(hasattr(self, 'object_list') and (self.object_list is None or self.object_list is [] or self.request.user.can_view(self.object_list[0])))):
|
obj = self.object
|
||||||
|
elif hasattr(self, 'object_list'):
|
||||||
|
obj = self.object_list[0] if self.object_list else None
|
||||||
|
if can_view(obj, self.request.user):
|
||||||
return res
|
return res
|
||||||
try: # Always unlock when 403
|
try: # Always unlock when 403
|
||||||
self.object.unset_lock()
|
self.object.unset_lock()
|
||||||
except: pass
|
except: pass
|
||||||
print("CanViewMixin 403")
|
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
from .user import *
|
from .user import *
|
||||||
|
@ -27,21 +27,21 @@ class UserPropForm(forms.ModelForm):
|
|||||||
required_css_class = 'required'
|
required_css_class = 'required'
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ['groups', 'edit_group', 'view_group']
|
fields = ['groups', 'edit_groups', 'view_groups']
|
||||||
labels = {
|
labels = {
|
||||||
'edit_group': "Edit profile group",
|
'edit_groups': "Edit profile group",
|
||||||
'view_group': "View profile group",
|
'view_groups': "View profile group",
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'edit_group': "Groups that can edit this user's profile",
|
'edit_groups': "Groups that can edit this user's profile",
|
||||||
'view_group': "Groups that can view this user's profile",
|
'view_groups': "Groups that can view this user's profile",
|
||||||
'groups': "Which groups this user belongs to",
|
'groups': "Which groups this user belongs to",
|
||||||
}
|
}
|
||||||
widgets = {
|
widgets = {
|
||||||
'groups': CheckboxSelectMultiple,
|
'groups': CheckboxSelectMultiple,
|
||||||
'user_permissions': CheckboxSelectMultiple,
|
'user_permissions': CheckboxSelectMultiple,
|
||||||
'edit_group': CheckboxSelectMultiple,
|
'edit_groups': CheckboxSelectMultiple,
|
||||||
'view_group': CheckboxSelectMultiple,
|
'view_groups': CheckboxSelectMultiple,
|
||||||
}
|
}
|
||||||
|
|
||||||
class PagePropForm(forms.ModelForm):
|
class PagePropForm(forms.ModelForm):
|
||||||
@ -49,16 +49,16 @@ class PagePropForm(forms.ModelForm):
|
|||||||
required_css_class = 'required'
|
required_css_class = 'required'
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Page
|
model = Page
|
||||||
fields = ['parent', 'name', 'owner_group', 'edit_group', 'view_group', ]
|
fields = ['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ]
|
||||||
widgets = {
|
widgets = {
|
||||||
'edit_group': CheckboxSelectMultiple,
|
'edit_groups': CheckboxSelectMultiple,
|
||||||
'view_group': CheckboxSelectMultiple,
|
'view_groups': CheckboxSelectMultiple,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *arg, **kwargs):
|
def __init__(self, *arg, **kwargs):
|
||||||
super(PagePropForm, self).__init__(*arg, **kwargs)
|
super(PagePropForm, self).__init__(*arg, **kwargs)
|
||||||
self.fields['edit_group'].required = False
|
self.fields['edit_groups'].required = False
|
||||||
self.fields['view_group'].required = False
|
self.fields['view_groups'].required = False
|
||||||
|
|
||||||
|
|
||||||
class GroupEditForm(forms.ModelForm):
|
class GroupEditForm(forms.ModelForm):
|
||||||
|
@ -9,7 +9,7 @@ from core.models import Page, PageRev, LockError
|
|||||||
from core.views.forms import PagePropForm
|
from core.views.forms import PagePropForm
|
||||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin
|
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin
|
||||||
|
|
||||||
class PageListView(ListView):
|
class PageListView(CanViewMixin, ListView):
|
||||||
model = Page
|
model = Page
|
||||||
template_name = 'core/page_list.jinja'
|
template_name = 'core/page_list.jinja'
|
||||||
|
|
||||||
|
@ -93,6 +93,11 @@ TEMPLATES = [
|
|||||||
"filters": {
|
"filters": {
|
||||||
"markdown": "core.templatetags.renderer.markdown",
|
"markdown": "core.templatetags.renderer.markdown",
|
||||||
},
|
},
|
||||||
|
"globals": {
|
||||||
|
"can_edit_prop": "core.views.can_edit_prop",
|
||||||
|
"can_edit": "core.views.can_edit",
|
||||||
|
"can_view": "core.views.can_view",
|
||||||
|
},
|
||||||
"bytecode_cache": {
|
"bytecode_cache": {
|
||||||
"name": "default",
|
"name": "default",
|
||||||
"backend": "django_jinja.cache.BytecodeCache",
|
"backend": "django_jinja.cache.BytecodeCache",
|
||||||
@ -228,12 +233,11 @@ AE_SUBSCRIPTIONS = {
|
|||||||
CLUB_ROLES = {
|
CLUB_ROLES = {
|
||||||
10: 'Président',
|
10: 'Président',
|
||||||
9: 'Vice-Président',
|
9: 'Vice-Président',
|
||||||
8: 'Vice-Président',
|
|
||||||
7: 'Trésorier',
|
7: 'Trésorier',
|
||||||
5: 'Responsable com',
|
5: 'Responsable com',
|
||||||
4: 'Secrétaire',
|
4: 'Secrétaire',
|
||||||
3: 'Responsable info',
|
3: 'Responsable info',
|
||||||
2: 'Membre du bureau',
|
2: 'Membre du bureau',
|
||||||
1: 'Membre actif',
|
1: 'Membre actif',
|
||||||
0: 'Membre',
|
0: 'Curieux',
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,12 @@ class Subscriber(User):
|
|||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(Subscriber, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def is_subscribed(self):
|
def is_subscribed(self):
|
||||||
return self.subscriptions.last().is_valid_now()
|
s = self.subscriptions.last()
|
||||||
|
return s.is_valid_now() if s is not None else False
|
||||||
|
|
||||||
class Subscription(models.Model):
|
class Subscription(models.Model):
|
||||||
member = models.ForeignKey(Subscriber, related_name='subscriptions')
|
member = models.ForeignKey(Subscriber, related_name='subscriptions')
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.views.generic.edit import UpdateView, CreateView
|
from django.views.generic.edit import UpdateView, CreateView
|
||||||
|
from django.views.generic.base import View
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms import Select
|
from django.forms import Select
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from subscription.models import Subscriber, Subscription
|
from subscription.models import Subscriber, Subscription
|
||||||
from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin
|
from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin
|
||||||
|
from core.models import User
|
||||||
|
|
||||||
|
class SubscriberMixin(View):
|
||||||
|
def dispatch(self, request, *arg, **kwargs):
|
||||||
|
res = super(SubscriberMixin, self).dispatch(request, *arg, **kwargs)
|
||||||
|
subscriber = Subscriber.objects.filter(pk=request.user.pk).first()
|
||||||
|
if subscriber is not None and subscriber.is_subscribed():
|
||||||
|
return ret
|
||||||
|
raise PermissionDenied
|
||||||
|
|
||||||
class SubscriptionForm(forms.ModelForm):
|
class SubscriptionForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
Loading…
Reference in New Issue
Block a user