Revision 01ac12d5
b/snf-astakos-app/astakos/im/api/__init__.py | ||
---|---|---|
164 | 164 |
l.append(dict(url=absolute(reverse('index')), name=user.email)) |
165 | 165 |
l.append(dict(url=absolute(reverse('edit_profile')), name="My account")) |
166 | 166 |
if with_extra_links: |
167 |
if user.has_usable_password() and user.provider == 'local':
|
|
167 |
if user.has_usable_password() and user.provider in ('local', ''):
|
|
168 | 168 |
l.append(dict(url=absolute(reverse('password_change')), name="Change password")) |
169 | 169 |
if EMAILCHANGE_ENABLED: |
170 | 170 |
l.append(dict(url=absolute(reverse('email_change')), name="Change email")) |
171 | 171 |
if INVITATIONS_ENABLED: |
172 | 172 |
l.append(dict(url=absolute(reverse('invite')), name="Invitations")) |
173 | 173 |
l.append(dict(url=absolute(reverse('feedback')), name="Feedback")) |
174 |
if request.user.has_perm('im.add_astakosgroup'): |
|
175 |
l.append(dict(url=absolute(reverse('group_add')), name="Add group")) |
|
176 |
url = absolute(reverse('group_list')) |
|
177 |
l.append(dict(url=url, name="Subscribed groups")) |
|
178 |
url = '%s?relation=owner' % url |
|
179 |
l.append(dict(url=url, name="My groups")) |
|
174 |
l.append(dict(url=absolute(reverse('group_list')), name="Groups")) |
|
180 | 175 |
if with_signout: |
181 | 176 |
l.append(dict(url=absolute(reverse('logout')), name="Sign out")) |
182 | 177 |
|
b/snf-astakos-app/astakos/im/forms.py | ||
---|---|---|
48 | 48 |
from django.utils.encoding import smart_str |
49 | 49 |
from django.forms.extras.widgets import SelectDateWidget |
50 | 50 |
from django.db.models import Q |
51 |
from django.db.models.query import EmptyQuerySet |
|
51 | 52 |
|
52 | 53 |
from astakos.im.models import * |
53 | 54 |
from astakos.im.settings import INVITATIONS_PER_LEVEL, DEFAULT_FROM_EMAIL, \ |
... | ... | |
527 | 528 |
model = AstakosGroupQuota |
528 | 529 |
|
529 | 530 |
return AstakosGroupPolicyCreationForm |
531 |
|
|
532 |
class AstakosGroupSearchForm(forms.Form): |
|
533 |
q = forms.CharField(max_length=200, label='') |
|
534 |
|
|
535 |
class MembershipCreationForm(forms.ModelForm): |
|
536 |
# TODO check not to hit the db |
|
537 |
group = forms.ModelChoiceField(queryset=AstakosGroup.objects.all(), widget=forms.HiddenInput()) |
|
538 |
person = forms.ModelChoiceField(queryset=AstakosUser.objects.all(), widget=forms.HiddenInput()) |
|
539 |
date_requested = forms.DateField(widget=forms.HiddenInput(), input_formats="%d/%m/%Y") |
|
540 |
|
|
541 |
class Meta: |
|
542 |
model = Membership |
|
543 |
exclude = ('date_joined',) |
|
544 |
|
|
545 |
def __init__(self, *args, **kwargs): |
|
546 |
super(MembershipCreationForm, self).__init__(*args, **kwargs) |
b/snf-astakos-app/astakos/im/models.py | ||
---|---|---|
141 | 141 |
|
142 | 142 |
@property |
143 | 143 |
def participants(self): |
144 |
if not self.id: |
|
145 |
return 0 |
|
146 |
return self.user_set.count() |
|
144 |
return len(self.approved_members) |
|
147 | 145 |
|
148 | 146 |
def approve(self): |
149 | 147 |
self.approval_date = datetime.now() |
... | ... | |
153 | 151 |
self.approval_date = None |
154 | 152 |
self.save() |
155 | 153 |
|
156 |
def approve_member(self, member):
|
|
157 |
m, created = self.membership_set.get_or_create(person=member, group=self)
|
|
158 |
m.date_joined = datetime.now()
|
|
159 |
m.save()
|
|
160 |
|
|
161 |
def disapprove_member(self, member):
|
|
162 |
m = self.membership_set.remove(member)
|
|
154 |
def approve_member(self, person):
|
|
155 |
try:
|
|
156 |
self.membership_set.create(person=person, date_joined=datetime.now())
|
|
157 |
except IntegrityError:
|
|
158 |
m = self.membership_set.get(person=person) |
|
159 |
m.date_joined = datetime.now()
|
|
160 |
m.save()
|
|
163 | 161 |
|
164 |
def get_members(self, approved=True): |
|
165 |
if approved: |
|
166 |
return self.membership_set().filter(is_approved=True) |
|
167 |
return self.membership_set().all() |
|
162 |
def disapprove_member(self, person): |
|
163 |
self.membership_set.remove(person=person) |
|
164 |
|
|
165 |
@property |
|
166 |
def members(self): |
|
167 |
return map(lambda m:m.person, self.membership_set.all()) |
|
168 | 168 |
|
169 |
def get_policies(self): |
|
170 |
related = self.policy.through.objects |
|
171 |
return map(lambda r: {r.name:related.get(resource__id=r.id, group__id=self.id).limit}, self.policy.all()) |
|
169 |
@property |
|
170 |
def approved_members(self): |
|
171 |
f = filter(lambda m:m.is_approved, self.membership_set.all()) |
|
172 |
return map(lambda m:m.person, f) |
|
172 | 173 |
|
174 |
@property |
|
175 |
def policies(self): |
|
176 |
return self.astakosgroupquota_set.all() |
|
177 |
|
|
178 |
@property |
|
173 | 179 |
def has_undefined_policies(self): |
174 | 180 |
# TODO: can avoid query? |
175 | 181 |
return Resource.objects.filter(~Q(astakosgroup=self)).exists() |
... | ... | |
346 | 352 |
class Membership(models.Model): |
347 | 353 |
person = models.ForeignKey(AstakosUser) |
348 | 354 |
group = models.ForeignKey(AstakosGroup) |
349 |
date_requested = models.DateField(default=datetime.now()) |
|
350 |
date_joined = models.DateField(null=True, db_index=True) |
|
355 |
date_requested = models.DateField(default=datetime.now(), blank=True)
|
|
356 |
date_joined = models.DateField(null=True, db_index=True, blank=True)
|
|
351 | 357 |
|
352 | 358 |
class Meta: |
353 | 359 |
unique_together = ("person", "group") |
... | ... | |
357 | 363 |
if self.date_joined: |
358 | 364 |
return True |
359 | 365 |
return False |
366 |
|
|
367 |
def approve(self): |
|
368 |
self.date_joined = datetime.now() |
|
369 |
self.save() |
|
370 |
|
|
371 |
def disapprove(self): |
|
372 |
self.delete() |
|
360 | 373 |
|
361 | 374 |
class AstakosGroupQuota(models.Model): |
362 | 375 |
limit = models.PositiveIntegerField('Limit') |
b/snf-astakos-app/astakos/im/templates/im/astakosgroup_detail.html | ||
---|---|---|
1 | 1 |
{% extends "im/account_base.html" %} |
2 | 2 |
|
3 |
{% load filters %} |
|
4 |
|
|
5 | 3 |
{% block page.body %} |
6 | 4 |
<div class="maincol {% block innerpage.class %}{% endblock %}"> |
7 | 5 |
<table class="zebra-striped id-sorted"> |
8 | 6 |
<tr> |
9 | 7 |
<th>Name: {{object.name}}</th> |
10 | 8 |
</tr> |
9 |
<tr> |
|
10 |
<th>Type: {{object.kind}}</th> |
|
11 |
</tr> |
|
12 |
<tr> |
|
13 |
<th>Issue date: {{object.issue_date}}</th> |
|
14 |
</tr> |
|
15 |
<tr> |
|
16 |
<th>Expiration date: {{object.expiration_date}}</th> |
|
17 |
</tr> |
|
18 |
<tr> |
|
19 |
<th>Owner: {% for o in object.owner.all %} |
|
20 |
{% if user == o %} |
|
21 |
Me! |
|
22 |
{% else%} |
|
23 |
{{o.realname}} ({{o.email}}) |
|
24 |
|
|
25 |
{% endif %} |
|
26 |
{% endfor %} |
|
27 |
</th> |
|
28 |
</tr> |
|
11 | 29 |
</table> |
12 | 30 |
<div class="section"> |
13 | 31 |
<h2>Members:</h2> |
14 |
{% if members %} |
|
32 |
{% if object.members %}
|
|
15 | 33 |
<table class="zebra-striped id-sorted"> |
16 | 34 |
<thead> |
17 | 35 |
<tr> |
36 |
<th>Email</th> |
|
18 | 37 |
<th>Realname</th> |
19 | 38 |
<th>Status</th> |
20 | 39 |
</tr> |
21 | 40 |
</thead> |
22 | 41 |
<tbody> |
23 |
{% for name, approved in members %}
|
|
42 |
{% for m in object.membership_set.all %}
|
|
24 | 43 |
<tr> |
25 |
<td>{{name}}</td> |
|
26 |
<td>{{approved}}</td> |
|
44 |
<td>{{m.person.email}}</td> |
|
45 |
<td>{{m.person.realname}}</td> |
|
46 |
{% if m.person in m.group.owner.all %} |
|
47 |
<td>Owner</td> |
|
48 |
{% else %} |
|
49 |
{% if m.is_approved %} |
|
50 |
<td>Approved</td> |
|
51 |
{% else %} |
|
52 |
<td>Pending</td> |
|
53 |
{% if user in m.group.owner.all %} |
|
54 |
<td><a href="{% url approve_member m.id %}">Approve</a></td> |
|
55 |
<td><a href="{% url disapprove_member m.id %}">Disapprove</a></td> |
|
56 |
{% endif %} |
|
57 |
{% endif %} |
|
58 |
{% endif %} |
|
27 | 59 |
</tr> |
28 | 60 |
{% endfor %} |
29 | 61 |
</tbody> |
... | ... | |
44 | 76 |
</thead> |
45 | 77 |
<tbody> |
46 | 78 |
{% for q in quota %} |
47 |
{% for k in q|dkeys %} |
|
48 | 79 |
<tr> |
49 |
<td>{{k}}</td>
|
|
50 |
<td>{{q|lookup:k}}</td>
|
|
80 |
<td>{{q.resource.name}}</td>
|
|
81 |
<td>{{q.limit}}</td>
|
|
51 | 82 |
</tr> |
52 |
{% endfor %} |
|
53 | 83 |
{% endfor %} |
54 | 84 |
</tbody> |
55 | 85 |
</table> |
b/snf-astakos-app/astakos/im/templates/im/astakosgroup_list.html | ||
---|---|---|
1 | 1 |
{% extends "im/account_base.html" %} |
2 | 2 |
|
3 |
{% load filters %} |
|
4 |
|
|
3 | 5 |
{% block page.body %} |
4 | 6 |
<div class="maincol {% block innerpage.class %}{% endblock %}"> |
7 |
{% if form %} |
|
8 |
<form action="{% url group_search %}" method="post" class="innerlabels signup">{% csrf_token %} |
|
9 |
<h2><span>Search group:</span></h2> |
|
10 |
{% include "im/form_render.html" %} |
|
11 |
<div class="form-row submit"> |
|
12 |
<input type="submit" class="submit altcol" value="SEARCH" /> |
|
13 |
</div> |
|
14 |
</form> |
|
15 |
{% else %} |
|
16 |
<p class="submit-rt"> |
|
17 |
<a href="{% url group_add %}" class="submit">Create a group</a> |
|
18 |
<a href="{% url group_search %}" class="submit">Join a group</a> |
|
19 |
</p> |
|
20 |
{% endif %} |
|
5 | 21 |
{% if object_list %} |
6 | 22 |
<h2>Groups:</h2> |
7 | 23 |
<table class="zebra-striped id-sorted"> |
8 | 24 |
<thead> |
9 | 25 |
<tr> |
10 | 26 |
<th>Name</th> |
27 |
<th>Type</th> |
|
28 |
<th>Issue date</th> |
|
29 |
<th>Expiration date</th> |
|
30 |
<th>Owner?</th> |
|
31 |
<th>Participants</th> |
|
32 |
<th>Enrollment status</th> |
|
11 | 33 |
</tr> |
12 | 34 |
</thead> |
13 | 35 |
<tbody> |
14 | 36 |
{% for o in object_list %} |
15 | 37 |
<tr> |
16 |
<td><a class="extra-link" href="{% url group_detail o.id %}">{{ o.name }}</a></td> |
|
38 |
<td><a class="extra-link" href="{% url group_detail o.id %}">{{o.name}}</a></td> |
|
39 |
<td>{{o.kind}}</td> |
|
40 |
<td>{{o.issue_date|date:"D d M Y"}}</td> |
|
41 |
<td>{{o.expiration_date|date:"D d M Y"}}</td> |
|
42 |
<td>{% if user in o.owner.all %}Yes{% else %}No{% endif %}</td> |
|
43 |
<td>{{ o.approved_members|length }}/{{ o.members|length }}</td> |
|
44 |
{% if user in o.approved_members %} |
|
45 |
<td>Active</td> |
|
46 |
{% if user not in o.owner.all %} |
|
47 |
<td> |
|
48 |
<form action="{% url group_leave o.id %}" method="post"class="login innerlabels">{% csrf_token %} |
|
49 |
<div class="form-row submit clearfix"> |
|
50 |
<input type="submit" class="submit altcol" value="LEAVE" /> |
|
51 |
</div> |
|
52 |
</form> |
|
53 |
</td> |
|
54 |
{% endif %} |
|
55 |
{% else %} |
|
56 |
{% if user in o.members %} |
|
57 |
<td>Pending</td> |
|
58 |
{% else %} |
|
59 |
<td>Not member</td> |
|
60 |
{% if join_forms %} |
|
61 |
<td> |
|
62 |
<form action="{% url group_join o.id %}" method="post"class="login innerlabels">{% csrf_token %} |
|
63 |
{% with join_forms|lookup:o.name as form %} |
|
64 |
{% include "im/form_render.html" %} |
|
65 |
{% endwith %} |
|
66 |
<div class="form-row submit clearfix"> |
|
67 |
<input type="submit" class="submit altcol" value="JOIN" /> |
|
68 |
</div> |
|
69 |
</form> |
|
70 |
</td> |
|
71 |
{% endif %} |
|
72 |
{% endif %} |
|
73 |
{% endif %} |
|
17 | 74 |
</tr> |
18 | 75 |
{% endfor %} |
19 | 76 |
</tbody> |
b/snf-astakos-app/astakos/im/urls.py | ||
---|---|---|
54 | 54 |
url(r'^group/(?P<group_id>\d+)/?$', 'group_detail', {}, name='group_detail'), |
55 | 55 |
url(r'^group/(?P<group_id>\d+)/policies/list/?$', 'group_policies_list', {}, name='group_policies_list'), |
56 | 56 |
url(r'^group/(?P<group_id>\d+)/policies/add/?$', 'group_policies_add', {}, name='group_policies_add'), |
57 |
url(r'^group/search/?$', 'group_search', {}, name='group_search'), |
|
58 |
url(r'^group/(?P<group_id>\d+)/join/?$', 'group_join', {}, name='group_join'), |
|
59 |
url(r'^group/(?P<group_id>\d+)/leave/?$', 'group_leave', {}, name='group_leave'), |
|
57 | 60 |
url(r'^group/(?P<group_id>\d+)/request/approval/?$', 'group_approval_request', {}, name='group_approval_request'), |
61 |
url(r'^group/(?P<membership_id>\d+)/approve/?$', 'approve_member', {}, name='approve_member'), |
|
62 |
url(r'^group/(?P<membership_id>\d+)/disapprove/?$', 'disapprove_member', {}, name='disapprove_member'), |
|
58 | 63 |
) |
59 | 64 |
|
60 | 65 |
if EMAILCHANGE_ENABLED: |
b/snf-astakos-app/astakos/im/views.py | ||
---|---|---|
38 | 38 |
from urllib import quote |
39 | 39 |
from functools import wraps |
40 | 40 |
|
41 |
from django.core.mail import send_mail |
|
42 |
from django.http import HttpResponse, HttpResponseBadRequest |
|
43 |
from django.shortcuts import redirect |
|
44 |
from django.template.loader import render_to_string |
|
45 |
from django.utils.translation import ugettext as _ |
|
46 |
from django.core.urlresolvers import reverse |
|
47 |
from django.contrib.auth.decorators import login_required |
|
48 | 41 |
from django.contrib import messages |
49 |
from django.db import transaction |
|
50 |
from django.utils.http import urlencode |
|
51 |
from django.http import HttpResponseRedirect, HttpResponseBadRequest |
|
52 |
from django.db.utils import IntegrityError |
|
42 |
from django.contrib.auth.decorators import login_required |
|
53 | 43 |
from django.contrib.auth.views import password_change |
54 | 44 |
from django.core.exceptions import ValidationError |
45 |
from django.core.mail import send_mail |
|
46 |
from django.core.urlresolvers import reverse |
|
47 |
from django.db import transaction |
|
55 | 48 |
from django.db.models import Q |
56 |
from django.forms.models import inlineformset_factory |
|
57 |
from django.forms.models import inlineformset_factory |
|
49 |
from django.db.utils import IntegrityError |
|
50 |
from django.forms.fields import URLField |
|
51 |
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, \ |
|
52 |
HttpResponseRedirect, HttpResponseBadRequest |
|
53 |
from django.shortcuts import redirect |
|
54 |
from django.template.loader import render_to_string |
|
55 |
from django.utils.http import urlencode |
|
56 |
from django.utils.translation import ugettext as _ |
|
58 | 57 |
from django.views.generic.create_update import * |
59 | 58 |
from django.views.generic.list_detail import * |
60 | 59 |
|
... | ... | |
588 | 587 |
extra_context)) |
589 | 588 |
|
590 | 589 |
@signed_terms_required |
590 |
@login_required |
|
591 | 591 |
def group_add(request): |
592 | 592 |
return create_object(request, |
593 |
form_class=get_astakos_group_creation_form(request), |
|
594 |
login_required = True, |
|
595 |
post_save_redirect = '/im/group/%(id)s/') |
|
593 |
form_class=get_astakos_group_creation_form(request), |
|
594 |
post_save_redirect = '/im/group/%(id)s/') |
|
596 | 595 |
|
597 | 596 |
@signed_terms_required |
598 | 597 |
@login_required |
599 | 598 |
def group_list(request): |
600 |
relation = get_query(request).get('relation', 'member') |
|
601 |
if relation == 'member': |
|
602 |
list = AstakosGroup.objects.filter(membership__person=request.user) |
|
603 |
else: |
|
604 |
list = AstakosGroup.objects.filter(owner__id=request.user.id) |
|
599 |
list = AstakosGroup.objects.filter(membership__person=request.user) |
|
605 | 600 |
return object_list(request, queryset=list) |
606 | 601 |
|
607 | 602 |
@signed_terms_required |
... | ... | |
611 | 606 |
group = AstakosGroup.objects.select_related().get(id=group_id) |
612 | 607 |
except AstakosGroup.DoesNotExist: |
613 | 608 |
return HttpResponseBadRequest(_('Invalid group.')) |
614 |
members = map(lambda m:{m.person.realname:m.is_approved}, group.membership_set.all()) |
|
615 | 609 |
return object_detail(request, |
616 |
AstakosGroup.objects.all(), |
|
617 |
object_id=group_id, |
|
618 |
extra_context = {'quota':group.get_policies(), |
|
619 |
'members':members, |
|
620 |
'form':get_astakos_group_policy_creation_form(group), |
|
621 |
'more_policies':group.has_undefined_policies()}) |
|
610 |
AstakosGroup.objects.all(), |
|
611 |
object_id=group_id, |
|
612 |
extra_context = {'form':get_astakos_group_policy_creation_form(group), |
|
613 |
'quota':group.policies, |
|
614 |
'more_policies':group.has_undefined_policies}) |
|
622 | 615 |
|
623 | 616 |
@signed_terms_required |
624 | 617 |
@login_required |
... | ... | |
627 | 620 |
return object_list(request, queryset=list) |
628 | 621 |
|
629 | 622 |
@signed_terms_required |
623 |
@login_required |
|
630 | 624 |
def group_policies_add(request, group_id): |
631 | 625 |
try: |
632 | 626 |
group = AstakosGroup.objects.select_related().get(id=group_id) |
633 | 627 |
except AstakosGroup.DoesNotExist: |
634 | 628 |
return HttpResponseBadRequest(_('Invalid group.')) |
635 | 629 |
return create_object(request, |
636 |
form_class=get_astakos_group_policy_creation_form(group), |
|
637 |
login_required=True, |
|
638 |
template_name = 'im/astakosgroup_detail.html', |
|
639 |
post_save_redirect = reverse('group_detail', kwargs=dict(group_id=group_id)), |
|
640 |
extra_context = {'group':group, |
|
641 |
'quota':group.get_policies(), |
|
642 |
'more_policies':group.has_undefined_policies()}) |
|
630 |
form_class=get_astakos_group_policy_creation_form(group), |
|
631 |
template_name = 'im/astakosgroup_detail.html', |
|
632 |
post_save_redirect = reverse('group_detail', kwargs=dict(group_id=group_id)), |
|
633 |
extra_context = {'group':group, |
|
634 |
'quota':group.policies, |
|
635 |
'more_policies':group.has_undefined_policies}) |
|
643 | 636 |
@signed_terms_required |
644 | 637 |
@login_required |
645 | 638 |
def group_approval_request(request, group_id): |
646 | 639 |
return HttpResponse() |
640 |
|
|
641 |
@signed_terms_required |
|
642 |
@login_required |
|
643 |
def group_search(request, queryset=EmptyQuerySet(), extra_context={}, **kwargs): |
|
644 |
join_forms = {} |
|
645 |
if request.method == 'GET': |
|
646 |
form = AstakosGroupSearchForm() |
|
647 |
else: |
|
648 |
form = AstakosGroupSearchForm(get_query(request)) |
|
649 |
if form.is_valid(): |
|
650 |
q = form.cleaned_data['q'].strip() |
|
651 |
q = URLField().to_python(q) |
|
652 |
queryset = AstakosGroup.objects.select_related().filter(name=q) |
|
653 |
f = MembershipCreationForm |
|
654 |
for g in queryset: |
|
655 |
join_forms[g.name] = f(dict(group=g, |
|
656 |
person=request.user, |
|
657 |
date_requested=datetime.now().strftime("%d/%m/%Y"))) |
|
658 |
return object_list(request, |
|
659 |
queryset, |
|
660 |
template_name='im/astakosgroup_list.html', |
|
661 |
extra_context=dict(form=form, is_search=True, join_forms=join_forms)) |
|
662 |
|
|
663 |
@signed_terms_required |
|
664 |
@login_required |
|
665 |
def group_join(request, group_id): |
|
666 |
return create_object(request, |
|
667 |
model=Membership, |
|
668 |
template_name='im/astakosgroup_list.html', |
|
669 |
post_save_redirect = reverse('group_detail', kwargs=dict(group_id=group_id))) |
|
670 |
|
|
671 |
@signed_terms_required |
|
672 |
@login_required |
|
673 |
def group_leave(request, group_id): |
|
674 |
try: |
|
675 |
m = Membership.objects.select_related().get(group__id=group_id, person=request.user) |
|
676 |
except Membership.DoesNotExist: |
|
677 |
return HttpResponseBadRequest(_('Invalid membership.')) |
|
678 |
if request.user in m.group.owner.all(): |
|
679 |
return HttpResponseForbidden(_('Owner can not leave the group.')) |
|
680 |
return delete_object(request, |
|
681 |
model=Membership, |
|
682 |
object_id = m.id, |
|
683 |
template_name='im/astakosgroup_list.html', |
|
684 |
post_delete_redirect = reverse('group_detail', kwargs=dict(group_id=group_id))) |
|
685 |
|
|
686 |
def handle_membership(): |
|
687 |
def decorator(func): |
|
688 |
@wraps(func) |
|
689 |
def wrapper(request, membership_id): |
|
690 |
try: |
|
691 |
m = Membership.objects.select_related().get(id=membership_id) |
|
692 |
except Membership.DoesNotExist: |
|
693 |
return HttpResponseBadRequest(_('Invalid membership.')) |
|
694 |
else: |
|
695 |
if request.user not in m.group.owner.all(): |
|
696 |
return HttpResponseForbidden(_('User is not a group owner.')) |
|
697 |
func(request, m) |
|
698 |
return render_response(template='im/astakosgroup_detail.html', |
|
699 |
context_instance=get_context(request), |
|
700 |
object=m.group, |
|
701 |
quota=m.group.policies, |
|
702 |
more_policies=m.group.has_undefined_policies) |
|
703 |
return wrapper |
|
704 |
return decorator |
|
705 |
|
|
706 |
@signed_terms_required |
|
707 |
@login_required |
|
708 |
@handle_membership() |
|
709 |
def approve_member(request, membership): |
|
710 |
try: |
|
711 |
membership.approve() |
|
712 |
realname = membership.person.realname |
|
713 |
msg = _('%s has been successfully joined the group.' % realname) |
|
714 |
messages.success(request, msg) |
|
715 |
except BaseException, e: |
|
716 |
logger.exception(e) |
|
717 |
msg = _('Something went wrong during %s\'s approval.' % realname) |
|
718 |
messages.error(request, msg) |
|
647 | 719 |
|
720 |
@signed_terms_required |
|
721 |
@login_required |
|
722 |
@handle_membership() |
|
723 |
def disapprove_member(request, membership): |
|
724 |
try: |
|
725 |
membership.disapprove() |
|
726 |
realname = membership.person.realname |
|
727 |
msg = _('%s has been successfully removed from the group.' % realname) |
|
728 |
messages.success(request, msg) |
|
729 |
except BaseException, e: |
|
730 |
logger.exception(e) |
|
731 |
msg = _('Something went wrong during %s\'s disapproval.' % realname) |
|
732 |
messages.error(request, msg) |
Also available in: Unified diff