Revision 8e1a5af5
b/snf-astakos-app/astakos/im/models.py | ||
---|---|---|
68 | 68 |
DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL, |
69 | 69 |
AUTH_TOKEN_DURATION, EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL, |
70 | 70 |
SITENAME, SERVICES, MODERATION_ENABLED, RESOURCES_PRESENTATION_DATA, |
71 |
PROJECT_MEMBER_JOIN_POLICIES, PROJECT_MEMBER_LEAVE_POLICIES) |
|
71 |
PROJECT_MEMBER_JOIN_POLICIES, PROJECT_MEMBER_LEAVE_POLICIES, PROJECT_ADMINS)
|
|
72 | 72 |
from astakos.im import settings as astakos_settings |
73 | 73 |
from astakos.im.endpoints.qh import ( |
74 | 74 |
register_users, send_quotas, qh_check_users, qh_get_quota_limits, |
... | ... | |
428 | 428 |
content_type=get_content_type()) |
429 | 429 |
self.user_permissions.remove(p) |
430 | 430 |
|
431 |
def is_project_admin(self, application_id=None): |
|
432 |
return self.uuid in PROJECT_ADMINS |
|
433 |
|
|
431 | 434 |
@property |
432 | 435 |
def invitation(self): |
433 | 436 |
try: |
... | ... | |
815 | 818 |
return m.user_friendly_state_display() |
816 | 819 |
|
817 | 820 |
def non_owner_can_view(self, maybe_project): |
821 |
if self.is_project_admin(): |
|
822 |
return True |
|
818 | 823 |
if maybe_project is None: |
819 | 824 |
return False |
820 | 825 |
project = maybe_project |
... | ... | |
1422 | 1427 |
""" |
1423 | 1428 |
Return projects accessed by specified user. |
1424 | 1429 |
""" |
1425 |
participates_filters = Q(owner=user) | Q(applicant=user) | \ |
|
1426 |
Q(project__projectmembership__person=user) |
|
1430 |
if user.is_project_admin(): |
|
1431 |
participates_filters = Q() |
|
1432 |
else: |
|
1433 |
participates_filters = Q(owner=user) | Q(applicant=user) | \ |
|
1434 |
Q(project__projectmembership__person=user) |
|
1427 | 1435 |
|
1428 | 1436 |
return self.user_visible_by_chain(participates_filters).order_by('issue_date').distinct() |
1429 | 1437 |
|
... | ... | |
1612 | 1620 |
except Project.DoesNotExist: |
1613 | 1621 |
return None |
1614 | 1622 |
|
1623 |
def can_be_approved(self): |
|
1624 |
return self.state == self.PENDING |
|
1625 |
|
|
1626 |
def can_be_dismissed(self): |
|
1627 |
return self.state == self.DENIED |
|
1628 |
|
|
1615 | 1629 |
def can_cancel(self): |
1616 | 1630 |
return self.state == self.PENDING |
1617 | 1631 |
|
b/snf-astakos-app/astakos/im/settings.py | ||
---|---|---|
331 | 331 |
ACTIVATION_REDIRECT_URL = getattr(settings, |
332 | 332 |
'ASTAKOS_ACTIVATION_REDIRECT_URL', |
333 | 333 |
"/im/landing") |
334 |
|
|
335 |
# Users that can approve or deny project applications from the web. |
|
336 |
PROJECT_ADMINS = getattr(settings, 'ASTAKOS_PROJECT_ADMINS', set()) |
b/snf-astakos-app/astakos/im/tables.py | ||
---|---|---|
286 | 286 |
project = record.project |
287 | 287 |
return self.user.membership_display(project) |
288 | 288 |
except Project.DoesNotExist: |
289 |
return _(Unknown)
|
|
289 |
return _("Unknown")
|
|
290 | 290 |
|
291 | 291 |
def render_members_count(self, record, *args, **kwargs): |
292 | 292 |
append = "" |
b/snf-astakos-app/astakos/im/templates/im/projects/project_detail.html | ||
---|---|---|
7 | 7 |
<div class="projects"> |
8 | 8 |
<h2> |
9 | 9 |
<em> |
10 |
{% if owner_mode %} |
|
10 |
{% if owner_mode or admin_mode %}
|
|
11 | 11 |
{% if project_view %} |
12 | 12 |
PROJECT {{ object.project_state_display|upper }} |
13 | 13 |
{% if object.has_pending_modifications %} - |
... | ... | |
43 | 43 |
</span> |
44 | 44 |
|
45 | 45 |
<!-- make room for buttons --> |
46 |
{% if owner_mode or can_join_request or can_leave_request %} |
|
46 |
{% if owner_mode or admin_mode or can_join_request or can_leave_request %}
|
|
47 | 47 |
<br /> |
48 | 48 |
{% endif %} |
49 | 49 |
|
50 |
{% if owner_mode %} |
|
50 |
{% if owner_mode or admin_mode %}
|
|
51 | 51 |
<a style="font-size:0.7em" |
52 | 52 |
href="{% url astakos.im.views.project_modify object.pk %}">MODIFY</a> |
53 | 53 |
|
54 |
{% with object.last_pending_incl_me as last_pending %} |
|
55 |
{% if last_pending %} |
|
56 |
- |
|
57 |
<a style="font-size:0.7em" |
|
58 |
href="{% url astakos.im.views.project_app_cancel last_pending.pk %}"> |
|
59 |
CANCEL {% if object.project_exists %} MODIFICATION {% else %} |
|
60 |
PROJECT {% endif %} REQUEST |
|
61 |
</a> |
|
54 |
{% if owner_mode %} |
|
55 |
{% with object.last_pending_incl_me as last_pending %} |
|
56 |
{% if last_pending %} |
|
57 |
- |
|
58 |
<a style="font-size:0.7em" |
|
59 |
href="{% url astakos.im.views.project_app_cancel last_pending.pk %}"> |
|
60 |
CANCEL {% if object.project_exists %} MODIFICATION {% else %} |
|
61 |
PROJECT {% endif %} REQUEST |
|
62 |
</a> |
|
63 |
{% endif %} |
|
64 |
{% endwith %} |
|
62 | 65 |
{% endif %} |
63 |
{% endwith %} |
|
64 | 66 |
|
67 |
{% if admin_mode %} |
|
68 |
{% if object.can_be_approved %} |
|
69 |
- <a style="font-size:0.7em" |
|
70 |
href="{% url astakos.im.views.project_app_approve object.pk %}"> |
|
71 |
APPROVE</a> |
|
72 |
- <a style="font-size:0.7em" |
|
73 |
href="{% url astakos.im.views.project_app_deny object.pk %}"> |
|
74 |
DENY</a> |
|
75 |
{% endif %} |
|
76 |
{% endif %} |
|
77 |
|
|
78 |
{% if owner_mode %} |
|
79 |
{% if object.can_be_dismissed %} |
|
80 |
- <a style="font-size:0.7em" |
|
81 |
href="{% url astakos.im.views.project_app_dismiss object.pk %}"> |
|
82 |
DISMISS</a> |
|
83 |
{% endif %} |
|
84 |
{% endif %} |
|
65 | 85 |
<!-- only one is possible, perhaps add cancel button too --> |
66 | 86 |
{% if can_join_request or can_leave_request %} |
67 | 87 |
<br /> |
b/snf-astakos-app/astakos/im/urls.py | ||
---|---|---|
71 | 71 |
url(r'^projects/(?P<chain_id>\d+)/(?P<user_id>\d+)/remove/?$', 'project_remove_member', {}, name='project_remove_member'), |
72 | 72 |
url(r'^projects/app/(?P<application_id>\d+)/?$', 'project_app', {}, name='project_app'), |
73 | 73 |
url(r'^projects/app/(?P<application_id>\d+)/modify$', 'project_modify', {}, name='project_modify'), |
74 |
url(r'^projects/app/(?P<application_id>\d+)/approve$', 'project_app_approve', {}, name='project_app_approve'), |
|
75 |
url(r'^projects/app/(?P<application_id>\d+)/deny$', 'project_app_deny', {}, name='project_app_deny'), |
|
76 |
url(r'^projects/app/(?P<application_id>\d+)/dismiss$', 'project_app_dismiss', {}, name='project_app_dismiss'), |
|
74 | 77 |
url(r'^projects/app/(?P<application_id>\d+)/cancel$', 'project_app_cancel', {}, name='project_app_cancel'), |
75 | 78 |
|
76 | 79 |
url(r'^projects/how_it_works/?$', 'how_it_works', {}, name='how_it_works'), |
b/snf-astakos-app/astakos/im/views.py | ||
---|---|---|
100 | 100 |
SendNotificationError, |
101 | 101 |
accept_membership, reject_membership, remove_membership, cancel_membership, |
102 | 102 |
leave_project, join_project, enroll_member, can_join_request, can_leave_request, |
103 |
cancel_application, get_related_project_id, |
|
104 |
get_by_chain_or_404) |
|
103 |
get_related_project_id, get_by_chain_or_404, |
|
104 |
approve_application, deny_application, |
|
105 |
cancel_application, dismiss_application) |
|
105 | 106 |
from astakos.im.settings import ( |
106 | 107 |
COOKIE_DOMAIN, LOGOUT_NEXT, |
107 | 108 |
LOGGING_LEVEL, PAGINATE_BY, |
... | ... | |
1246 | 1247 |
modifications_table = None |
1247 | 1248 |
|
1248 | 1249 |
user = request.user |
1250 |
is_project_admin = user.is_project_admin(application_id=application.id) |
|
1249 | 1251 |
is_owner = user.owns_application(application) |
1250 | 1252 |
if not is_owner and not project_view: |
1251 | 1253 |
m = _(astakos_messages.NOT_ALLOWED) |
... | ... | |
1277 | 1279 |
'addmembers_form':addmembers_form, |
1278 | 1280 |
'members_table': members_table, |
1279 | 1281 |
'owner_mode': is_owner, |
1282 |
'admin_mode': is_project_admin, |
|
1280 | 1283 |
'modifications_table': modifications_table, |
1281 | 1284 |
'mem_display': mem_display, |
1282 | 1285 |
'can_join_request': can_join_req, |
... | ... | |
1473 | 1476 |
messages.success(request, msg) |
1474 | 1477 |
return redirect(reverse('project_detail', args=(chain_id,))) |
1475 | 1478 |
|
1479 |
@require_http_methods(["POST", "GET"]) |
|
1480 |
@signed_terms_required |
|
1481 |
@login_required |
|
1482 |
@project_transaction_context(sync=True) |
|
1483 |
def project_app_approve(request, application_id, ctx=None): |
|
1484 |
|
|
1485 |
if not request.user.is_project_admin(): |
|
1486 |
m = _(astakos_messages.NOT_ALLOWED) |
|
1487 |
raise PermissionDenied(m) |
|
1488 |
|
|
1489 |
try: |
|
1490 |
app = ProjectApplication.objects.get(id=application_id) |
|
1491 |
except ProjectApplication.DoesNotExist: |
|
1492 |
raise Http404 |
|
1493 |
|
|
1494 |
approve_application(application_id) |
|
1495 |
chain_id = get_related_project_id(application_id) |
|
1496 |
return redirect(reverse('project_detail', args=(chain_id,))) |
|
1497 |
|
|
1498 |
@require_http_methods(["POST", "GET"]) |
|
1499 |
@signed_terms_required |
|
1500 |
@login_required |
|
1501 |
@project_transaction_context() |
|
1502 |
def project_app_deny(request, application_id, ctx=None): |
|
1503 |
|
|
1504 |
if not request.user.is_project_admin(): |
|
1505 |
m = _(astakos_messages.NOT_ALLOWED) |
|
1506 |
raise PermissionDenied(m) |
|
1507 |
|
|
1508 |
try: |
|
1509 |
app = ProjectApplication.objects.get(id=application_id) |
|
1510 |
except ProjectApplication.DoesNotExist: |
|
1511 |
raise Http404 |
|
1512 |
|
|
1513 |
deny_application(application_id) |
|
1514 |
return redirect(reverse('project_list')) |
|
1515 |
|
|
1516 |
@require_http_methods(["POST", "GET"]) |
|
1517 |
@signed_terms_required |
|
1518 |
@login_required |
|
1519 |
@project_transaction_context() |
|
1520 |
def project_app_dismiss(request, application_id, ctx=None): |
|
1521 |
try: |
|
1522 |
app = ProjectApplication.objects.get(id=application_id) |
|
1523 |
except ProjectApplication.DoesNotExist: |
|
1524 |
raise Http404 |
|
1525 |
|
|
1526 |
if not request.user.owns_application(app): |
|
1527 |
m = _(astakos_messages.NOT_ALLOWED) |
|
1528 |
raise PermissionDenied(m) |
|
1529 |
|
|
1530 |
# XXX: dismiss application also does authorization |
|
1531 |
dismiss_application(application_id, request_user=request.user) |
|
1532 |
return redirect(reverse('project_list')) |
|
1533 |
|
|
1476 | 1534 |
def landing(request): |
1477 | 1535 |
return render_response( |
1478 | 1536 |
'im/landing.html', |
b/snf-astakos-app/conf/20-snf-astakos-app-settings.conf | ||
---|---|---|
139 | 139 |
# NEWPASSWD_INVALIDATE_TOKEN = getattr(settings, 'ASTAKOS_NEWPASSWD_INVALIDATE_TOKEN', True) |
140 | 140 |
|
141 | 141 |
# Permit local account migration |
142 |
# ENABLE_LOCAL_ACCOUNT_MIGRATION = getattr(settings, 'ASTAKOS_ENABLE_LOCAL_ACCOUNT_MIGRATION', True) |
|
142 |
# ENABLE_LOCAL_ACCOUNT_MIGRATION = getattr(settings, 'ASTAKOS_ENABLE_LOCAL_ACCOUNT_MIGRATION', True) |
|
143 |
|
|
144 |
# UUIDs of users that can approve or deny project applications from the web. |
|
145 |
# ASTAKOS_PROJECT_ADMINS = set() # e.g. set(['01234567-89ab-cdef-0123-456789abcdef']) |
Also available in: Unified diff