Revision 17bebf7e
/dev/null | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
from django import forms |
|
35 |
from django.utils.translation import ugettext as _ |
|
36 |
|
|
37 |
from astakos.im.models import AstakosUser |
|
38 |
from astakos.im.forms import LocalUserCreationForm |
|
39 |
|
|
40 |
import logging |
|
41 |
|
|
42 |
logger = logging.getLogger(__name__) |
|
43 |
|
|
44 |
class AdminProfileForm(forms.ModelForm): |
|
45 |
""" |
|
46 |
Subclass of ``ModelForm`` for permiting user to edit his/her profile. |
|
47 |
Most of the fields are readonly since the user is not allowed to change them. |
|
48 |
|
|
49 |
The class defines a save method which sets ``is_verified`` to True so as the user |
|
50 |
during the next login will not to be redirected to profile page. |
|
51 |
""" |
|
52 |
quota = forms.CharField(label=_('Quota (GiB)')) |
|
53 |
|
|
54 |
class Meta: |
|
55 |
model = AstakosUser |
|
56 |
fields = ('email', 'first_name', 'last_name', 'is_superuser', |
|
57 |
'affiliation', 'is_active', 'invitations', 'quota', |
|
58 |
'auth_token', 'auth_token_created', 'auth_token_expires', |
|
59 |
'date_joined', 'updated') |
|
60 |
|
|
61 |
def __init__(self, *args, **kwargs): |
|
62 |
super(AdminProfileForm, self).__init__(*args, **kwargs) |
|
63 |
instance = getattr(self, 'instance', None) |
|
64 |
ro_fields = ('date_joined', 'auth_token', 'auth_token_created', |
|
65 |
'auth_token_expires', 'updated', 'email') |
|
66 |
if instance and instance.id: |
|
67 |
for field in ro_fields: |
|
68 |
self.fields[field].widget.attrs['readonly'] = True |
|
69 |
user = kwargs['instance'] |
|
70 |
if user: |
|
71 |
quota = lambda x: int(x) / 1024 ** 3 |
|
72 |
self.fields['quota'].widget.attrs['value'] = quota(user.quota) |
|
73 |
|
|
74 |
def save(self, commit=True): |
|
75 |
user = super(AdminProfileForm, self).save(commit=False) |
|
76 |
quota = lambda x: int(x or 0) * (1024 ** 3) |
|
77 |
user.quota = quota(self.cleaned_data['quota']) |
|
78 |
user.save() |
|
79 |
|
|
80 |
class AdminUserCreationForm(LocalUserCreationForm): |
|
81 |
class Meta: |
|
82 |
model = AstakosUser |
|
83 |
fields = ("email", "first_name", "last_name", "is_superuser", |
|
84 |
"is_active", "affiliation") |
|
85 |
|
|
86 |
def __init__(self, *args, **kwargs): |
|
87 |
""" |
|
88 |
Changes the order of fields, and removes the username field. |
|
89 |
""" |
|
90 |
super(AdminUserCreationForm, self).__init__(*args, **kwargs) |
|
91 |
self.fields.keyOrder = ['email', 'first_name', 'last_name', |
|
92 |
'is_superuser', 'is_active', 'affiliation', |
|
93 |
'password1', 'password2'] |
|
94 |
|
|
95 |
def save(self, commit=True): |
|
96 |
user = super(AdminUserCreationForm, self).save(commit=False) |
|
97 |
user.renew_token() |
|
98 |
if commit: |
|
99 |
user.save() |
|
100 |
logger.info('Created user %s', user) |
|
101 |
return user |
/dev/null | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
import logging |
|
35 |
|
|
36 |
from django.utils.translation import ugettext as _ |
|
37 |
from django.template.loader import render_to_string |
|
38 |
from django.core.mail import send_mail |
|
39 |
from django.core.urlresolvers import reverse |
|
40 |
from urlparse import urljoin |
|
41 |
from random import randint |
|
42 |
|
|
43 |
from astakos.im.settings import DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, SITEURL, SITENAME, BASEURL |
|
44 |
from astakos.im.models import Invitation |
|
45 |
|
|
46 |
logger = logging.getLogger(__name__) |
|
47 |
|
|
48 |
def activate(user, email_template_name='welcome_email.txt'): |
|
49 |
""" |
|
50 |
Activates the specific user and sends email. |
|
51 |
|
|
52 |
Raises SMTPException, socket.error |
|
53 |
""" |
|
54 |
user.is_active = True |
|
55 |
user.save() |
|
56 |
subject = _('Welcome to %s' % SITENAME) |
|
57 |
message = render_to_string(email_template_name, { |
|
58 |
'user': user, |
|
59 |
'url': SITEURL, |
|
60 |
'baseurl': BASEURL, |
|
61 |
'site_name': SITENAME, |
|
62 |
'support': DEFAULT_CONTACT_EMAIL % SITENAME.lower()}) |
|
63 |
sender = DEFAULT_FROM_EMAIL % SITENAME |
|
64 |
send_mail(subject, message, sender, [user.email]) |
|
65 |
logger.info('Sent greeting %s', user) |
|
66 |
|
|
67 |
def _generate_invitation_code(): |
|
68 |
while True: |
|
69 |
code = randint(1, 2L**63 - 1) |
|
70 |
try: |
|
71 |
Invitation.objects.get(code=code) |
|
72 |
# An invitation with this code already exists, try again |
|
73 |
except Invitation.DoesNotExist: |
|
74 |
return code |
|
75 |
|
|
76 |
def invite(inviter, username, realname): |
|
77 |
""" |
|
78 |
Send an invitation email and upon success reduces inviter's invitation by one. |
|
79 |
|
|
80 |
Raises SMTPException, socket.error |
|
81 |
""" |
|
82 |
code = _generate_invitation_code() |
|
83 |
invitation = Invitation(inviter=inviter, |
|
84 |
username=username, |
|
85 |
code=code, |
|
86 |
realname=realname) |
|
87 |
invitation.save() |
|
88 |
subject = _('Invitation to %s' % SITENAME) |
|
89 |
url = '%s?code=%d' % (urljoin(BASEURL, reverse('astakos.im.views.signup')), code) |
|
90 |
message = render_to_string('im/invitation.txt', { |
|
91 |
'invitation': invitation, |
|
92 |
'url': url, |
|
93 |
'baseurl': BASEURL, |
|
94 |
'service': SITENAME, |
|
95 |
'support': DEFAULT_CONTACT_EMAIL % SITENAME.lower()}) |
|
96 |
sender = DEFAULT_FROM_EMAIL % SITENAME |
|
97 |
send_mail(subject, message, sender, [invitation.username]) |
|
98 |
logger.info('Sent invitation %s', invitation) |
|
99 |
inviter.invitations = max(0, inviter.invitations - 1) |
|
100 |
inviter.save() |
/dev/null | ||
---|---|---|
1 |
{% extends "admin_base.html" %} |
|
2 |
|
|
3 |
{% block body %} |
|
4 |
<ul class="unstyled"> |
|
5 |
<li><strong>{{ stats.users }}</strong> User{{ stats.users|pluralize }} (<strong>{{ stats.pending }}</strong> pending)</li> |
|
6 |
<li><strong>{{ stats.invitations }}</strong> Invitation{{ stats.invitations|pluralize }} sent (<strong>{{ stats.invitations_consumed }}</strong> used)</li> |
|
7 |
</ul> |
|
8 |
{% endblock body %} |
/dev/null | ||
---|---|---|
1 |
<!DOCTYPE html> |
|
2 |
<html> |
|
3 |
<head> |
|
4 |
<meta charset="utf-8" /> |
|
5 |
<title>{{ title|default:"Astakos Login" }}</title> |
|
6 |
<link rel="stylesheet" href="{{ IM_STATIC_URL }}css/bootstrap.css"> |
|
7 |
<script src="{{ IM_STATIC_URL }}js/jquery.js"></script> |
|
8 |
<script src="{{ IM_STATIC_URL }}js/jquery.tablesorter.js"></script> |
|
9 |
<script src="{{ IM_STATIC_URL }}js/main.js"></script> |
|
10 |
{% block head %}{% endblock %} |
|
11 |
</head> |
|
12 |
<body> |
|
13 |
<div class="container"> |
|
14 |
<div style="padding: 5px 0px 0px 0px"> |
|
15 |
<img src="{{ IM_STATIC_URL }}images/banner.png" width="900" height="200"> |
|
16 |
</div> |
|
17 |
{% block title %}{% endblock %} |
|
18 |
|
|
19 |
{% if messages %} |
|
20 |
<ul class="messages"> |
|
21 |
{% for message in messages %} |
|
22 |
<li{% if message.tags %} class="alert-message.{{ message.tags }}"{% endif %}>{{ message }}</li> |
|
23 |
{% endfor %} |
|
24 |
</ul> |
|
25 |
{% endif %} |
|
26 |
|
|
27 |
{% block tabs %} |
|
28 |
<ul class="tabs"> |
|
29 |
<li{% ifequal tab "admin" %} class="active"{% endifequal %}> |
|
30 |
<a href="{% url astakos.im.admin.views.admin %}">Home</a> |
|
31 |
</li> |
|
32 |
<li{% ifequal tab "users" %} class="active"{% endifequal %}> |
|
33 |
<a href="{% url astakos.im.admin.views.users_list %}">Users</a> |
|
34 |
</li> |
|
35 |
<li{% ifequal tab "pending" %} class="active"{% endifequal %}> |
|
36 |
<a href="{% url astakos.im.admin.views.pending_users %}">Pending Users</a> |
|
37 |
</li> |
|
38 |
<li{% ifequal tab "invitations" %} class="active"{% endifequal %}> |
|
39 |
<a href="{% url astakos.im.admin.views.invitations_list %}">Invitations</a> |
|
40 |
</li> |
|
41 |
<li{% ifequal tab "logout" %} class="active"{% endifequal %}> |
|
42 |
<a href="{% url astakos.im.views.logout %}">Logout</a> |
|
43 |
</li> |
|
44 |
</ul> |
|
45 |
{% endblock %} |
|
46 |
|
|
47 |
{% block body %}{% endblock %} |
|
48 |
</div> |
|
49 |
</body> |
|
50 |
</html> |
|
51 |
|
/dev/null | ||
---|---|---|
1 |
{% extends "admin_base.html" %} |
|
2 |
|
|
3 |
{% load formatters %} |
|
4 |
|
|
5 |
{% block body %} |
|
6 |
|
|
7 |
<div class="row"> |
|
8 |
<div class="offset10 span3"> |
|
9 |
<form method="get"> |
|
10 |
<div class="input"> |
|
11 |
<input class="span3" name="filter" type="search" placeholder="search" value="{{ filter }}"> |
|
12 |
</div> |
|
13 |
</form> |
|
14 |
</div> |
|
15 |
</div> |
|
16 |
|
|
17 |
<table class="zebra-striped id-sorted"> |
|
18 |
<thead> |
|
19 |
<tr> |
|
20 |
<th>ID</th> |
|
21 |
<th>Email</th> |
|
22 |
<th>Real Name</th> |
|
23 |
<th>Code</th> |
|
24 |
<th>Inviter email</th> |
|
25 |
<th>Inviter Real Name</th> |
|
26 |
<th>Is used?</th> |
|
27 |
<th>Created</th> |
|
28 |
<th>Consumed</th> |
|
29 |
</tr> |
|
30 |
</thead> |
|
31 |
<tbody> |
|
32 |
{% for inv in invitations %} |
|
33 |
<tr> |
|
34 |
<td>{{ inv.id }}</td> |
|
35 |
<td>{{ inv.username }}</td> |
|
36 |
<td>{{ inv.realname }}</td> |
|
37 |
<td>{{ inv.code }}</td> |
|
38 |
<td>{{ inv.inviter.email }}</td> |
|
39 |
<td>{{ inv.inviter.realname }}</td> |
|
40 |
<td>{{ inv.is_consumed }}</td> |
|
41 |
<td>{{ inv.created }}</td> |
|
42 |
<td>{{ inv.consumed }}</td> |
|
43 |
</tr> |
|
44 |
{% endfor %} |
|
45 |
</tbody> |
|
46 |
</table> |
|
47 |
|
|
48 |
{% if pages|length > 1 %} |
|
49 |
<div class="pagination"> |
|
50 |
<ul> |
|
51 |
{% if prev %} |
|
52 |
<li class="prev"><a href="?page={{ prev }}{% if filter %}&filter={{ filter }}{% endif %}">← Previous</a></li> |
|
53 |
{% else %} |
|
54 |
<li class="prev disabled"><a href="#">← Previous</a></li> |
|
55 |
{% endif %} |
|
56 |
|
|
57 |
{% for p in pages %} |
|
58 |
<li{% if page == p %} class="active"{% endif %}><a href="?page={{ p }}{% if filter %}&filter={{ filter }}{% endif %}">{{ p }}</a></li> |
|
59 |
{% endfor %} |
|
60 |
|
|
61 |
{% if next %} |
|
62 |
<li class="next"><a href="?page={{ next }}{% if filter %}&filter={{ filter }}{% endif %}">→ Next</a></li> |
|
63 |
{% else %} |
|
64 |
<li class="next disabled"><a href="#">→ Next</a></li> |
|
65 |
{% endif %} |
|
66 |
</ul> |
|
67 |
</div> |
|
68 |
{% endif %} |
|
69 |
|
|
70 |
<a class="btn success" href="{% url astakos.im.admin.views.invitations_export %}">Export</a> |
|
71 |
|
|
72 |
<br /><br /> |
|
73 |
{% endblock body %} |
/dev/null | ||
---|---|---|
1 |
{% extends "admin_base.html" %} |
|
2 |
|
|
3 |
{% load formatters %} |
|
4 |
|
|
5 |
{% block body %} |
|
6 |
|
|
7 |
<div class="row"> |
|
8 |
<div class="offset10 span3"> |
|
9 |
<form method="get"> |
|
10 |
<div class="input"> |
|
11 |
<input class="span3" name="filter" type="search" placeholder="search" value="{{ filter }}"> |
|
12 |
</div> |
|
13 |
</form> |
|
14 |
</div> |
|
15 |
</div> |
|
16 |
|
|
17 |
<table class="zebra-striped id-sorted"> |
|
18 |
<thead> |
|
19 |
<tr> |
|
20 |
<th>ID</th> |
|
21 |
<th>Email</th> |
|
22 |
<th>Real Name</th> |
|
23 |
<th>Affiliation</th> |
|
24 |
<th>Email</th> |
|
25 |
<th>Inviter</th> |
|
26 |
<th>Action</th> |
|
27 |
</tr> |
|
28 |
</thead> |
|
29 |
<tbody> |
|
30 |
{% for user in users %} |
|
31 |
<tr> |
|
32 |
<td>{{ user.id }}</td> |
|
33 |
<td>{{ user.email }}</td> |
|
34 |
<td>{{ user.realname }}</td> |
|
35 |
<td>{{ user.affiliation }}</td> |
|
36 |
<td>{{ user.email }}</td> |
|
37 |
<td>{{ user.inviter.realname }}</td> |
|
38 |
<td> |
|
39 |
<form action="{% url astakos.im.admin.views.users_activate user.id %}" method="post">{% csrf_token %} |
|
40 |
<input type="hidden" name="page" value="{{ page }}"> |
|
41 |
<button type="submit" class="btn primary">Activate</button> |
|
42 |
</form> |
|
43 |
</tr> |
|
44 |
{% endfor %} |
|
45 |
</tbody> |
|
46 |
</table> |
|
47 |
|
|
48 |
{% if pages|length > 1 %} |
|
49 |
<div class="pagination"> |
|
50 |
<ul> |
|
51 |
{% if prev %} |
|
52 |
<li class="prev"><a href="?page={{ prev }}{% if filter %}&filter={{ filter }}{% endif %}">← Previous</a></li> |
|
53 |
{% else %} |
|
54 |
<li class="prev disabled"><a href="#">← Previous</a></li> |
|
55 |
{% endif %} |
|
56 |
|
|
57 |
{% for p in pages %} |
|
58 |
<li{% if page == p %} class="active"{% endif %}><a href="?page={{ p }}{% if filter %}&filter={{ filter }}{% endif %}">{{ p }}</a></li> |
|
59 |
{% endfor %} |
|
60 |
|
|
61 |
{% if next %} |
|
62 |
<li class="next"><a href="?page={{ next }}{% if filter %}&filter={{ filter }}{% endif %}">→ Next</a></li> |
|
63 |
{% else %} |
|
64 |
<li class="next disabled"><a href="#">→ Next</a></li> |
|
65 |
{% endif %} |
|
66 |
</ul> |
|
67 |
</div> |
|
68 |
{% endif %} |
|
69 |
|
|
70 |
<br /><br /> |
|
71 |
{% endblock body %} |
/dev/null | ||
---|---|---|
1 |
{% extends "admin_base.html" %} |
|
2 |
|
|
3 |
{% block body %} |
|
4 |
|
|
5 |
<form action="{% url astakos.im.admin.views.users_create %}" method="post">{% csrf_token %} |
|
6 |
{{ form.as_p }} |
|
7 |
|
|
8 |
<div class="actions"> |
|
9 |
<button type="submit" class="btn primary">Create</button> |
|
10 |
<button type="reset" class="btn">Reset</button> |
|
11 |
</div> |
|
12 |
</form> |
|
13 |
{% endblock body %} |
/dev/null | ||
---|---|---|
1 |
{% extends "admin_base.html" %} |
|
2 |
|
|
3 |
{% load formatters %} |
|
4 |
|
|
5 |
{% block body %} |
|
6 |
|
|
7 |
<form action="{% url astakos.im.admin.views.users_modify user.id %}" method="post">{% csrf_token %} |
|
8 |
{{ form.as_p }} |
|
9 |
|
|
10 |
<div class="actions"> |
|
11 |
<button type="submit" class="btn primary">Save Changes</button> |
|
12 |
<button type="reset" class="btn">Reset</button> |
|
13 |
|
|
14 |
<a class="btn danger needs-confirm" href="{% url astakos.im.admin.views.users_delete user.id %}">Delete User</a> |
|
15 |
</div> |
|
16 |
|
|
17 |
<div class="alert-message block-message error"> |
|
18 |
<p><strong>WARNING:</strong> Are you sure you want to delete this user?</p> |
|
19 |
<div class="alert-actions"> |
|
20 |
<a class="btn danger" href="{% url astakos.im.admin.views.users_delete user.id %}">Delete</a> |
|
21 |
<a class="btn alert-close">Cancel</a> |
|
22 |
</div> |
|
23 |
</div> |
|
24 |
</form> |
|
25 |
{% endblock body %} |
/dev/null | ||
---|---|---|
1 |
{% extends "admin_base.html" %} |
|
2 |
|
|
3 |
{% load formatters %} |
|
4 |
|
|
5 |
{% block body %} |
|
6 |
|
|
7 |
<div class="row"> |
|
8 |
<div class="offset10 span3"> |
|
9 |
<form method="get"> |
|
10 |
<div class="input"> |
|
11 |
<input class="span3" name="filter" type="search" placeholder="search" value="{{ filter }}"> |
|
12 |
</div> |
|
13 |
</form> |
|
14 |
</div> |
|
15 |
</div> |
|
16 |
|
|
17 |
<table class="zebra-striped id-sorted"> |
|
18 |
<thead> |
|
19 |
<tr> |
|
20 |
<th>ID</th> |
|
21 |
<th>Email</th> |
|
22 |
<th>Real Name</th> |
|
23 |
<th>Admin</th> |
|
24 |
<th>Affiliation</th> |
|
25 |
<th>Is active?</th> |
|
26 |
<th>Quota</th> |
|
27 |
<th>Updated</th> |
|
28 |
</tr> |
|
29 |
</thead> |
|
30 |
<tbody> |
|
31 |
{% for user in users %} |
|
32 |
<tr> |
|
33 |
<td><a href="{% url astakos.im.admin.views.users_info user.id %}">{{ user.id }}</a></td> |
|
34 |
<td><a href="{% url astakos.im.admin.views.users_info user.id %}">{{ user.email }}</a></td> |
|
35 |
<td>{{ user.realname }}</td> |
|
36 |
<td>{{ user.is_superuser }}</td> |
|
37 |
<td>{{ user.affiliation }}</td> |
|
38 |
<td>{{ user.is_active }}</td> |
|
39 |
<td>{{ user.quota|GiB }} GiB</td> |
|
40 |
<td>{{ user.updated }}</td> |
|
41 |
</tr> |
|
42 |
{% endfor %} |
|
43 |
</tbody> |
|
44 |
</table> |
|
45 |
|
|
46 |
{% if pages|length > 1 %} |
|
47 |
<div class="pagination"> |
|
48 |
<ul> |
|
49 |
{% if prev %} |
|
50 |
<li class="prev"><a href="?page={{ prev }}{% if filter %}&filter={{ filter }}{% endif %}">← Previous</a></li> |
|
51 |
{% else %} |
|
52 |
<li class="prev disabled"><a href="#">← Previous</a></li> |
|
53 |
{% endif %} |
|
54 |
|
|
55 |
{% for p in pages %} |
|
56 |
<li{% if page == p %} class="active"{% endif %}><a href="?page={{ p }}{% if filter %}&filter={{ filter }}{% endif %}">{{ p }}</a></li> |
|
57 |
{% endfor %} |
|
58 |
|
|
59 |
{% if next %} |
|
60 |
<li class="next"><a href="?page={{ next }}{% if filter %}&filter={{ filter }}{% endif %}">→ Next</a></li> |
|
61 |
{% else %} |
|
62 |
<li class="next disabled"><a href="#">→ Next</a></li> |
|
63 |
{% endif %} |
|
64 |
</ul> |
|
65 |
</div> |
|
66 |
{% endif %} |
|
67 |
|
|
68 |
<a class="btn success" href="{% url astakos.im.admin.views.users_create %}">Create a user</a> |
|
69 |
<a class="btn success" href="{% url astakos.im.admin.views.users_export %}">Export</a> |
|
70 |
|
|
71 |
<br /><br /> |
|
72 |
{% endblock body %} |
/dev/null | ||
---|---|---|
1 |
--- A translation in English follows --- |
|
2 |
|
|
3 |
Αγαπητέ/η {{ user.realname }}, |
|
4 |
|
|
5 |
Ο λογαρισμός σας για την υπηρεσία {{ site_name }} της ΕΔΕΤ κατά την Alpha (πιλοτική) |
|
6 |
φάση λειτουργίας της έχει ενεργοποιηθεί. |
|
7 |
|
|
8 |
Για να συνδεθείτε, χρησιμοποιήστε τον παρακάτω σύνδεσμο: |
|
9 |
|
|
10 |
{{ url }} |
|
11 |
|
|
12 |
Σημείωση: |
|
13 |
|
|
14 |
Η υπηρεσία θα είναι για μερικές εβδομάδες σε φάση λειτουργίας Alpha. Αν |
|
15 |
και έχουμε κάνει ό,τι είναι δυνατό για να εξασφαλίσουμε την ποιότητα της |
|
16 |
υπηρεσίας, δεν αποκλείεται να εμφανιστούν προβλήματα στο λογισμικό |
|
17 |
διαχείρισης ή η υπηρεσία να μην είναι διαθέσιμη κατά διαστήματα. Για |
|
18 |
αυτό το λόγο, σας παρακαλούμε να μη μεταφέρετε ακόμη σημαντικά αρχεία |
|
19 |
στην υπηρεσία {{ site_name }}. Επίσης, παρακαλούμε να έχετε |
|
20 |
υπόψη σας ότι όλα τα δεδομένα, θα διαγραφούν κατά τη μετάβαση από την |
|
21 |
έκδοση Alpha στην έκδοση Beta. Θα υπάρξει έγκαιρη ειδοποίησή σας πριν |
|
22 |
από τη μετάβαση αυτή. |
|
23 |
|
|
24 |
Περισσότερα για την υπηρεσία θα βρείτε στο {{ baseurl }}, αφού |
|
25 |
ενεργοποιήσετε την πρόσκλησή σας. |
|
26 |
|
|
27 |
Αναμένουμε τα σχόλια και τις παρατηρήσεις σας, για να βελτιώσουμε τη |
|
28 |
λειτουργικότητα και την αξιοπιστία της καινοτομικής αυτής υπηρεσίας. |
|
29 |
|
|
30 |
Για όποιες παρατηρήσεις ή προβλήματα στη λειτουργεία της υπηρεσίας μπορείτε να |
|
31 |
απευθυνθείτε στο {{ support }}. |
|
32 |
|
|
33 |
Σας ευχαριστούμε πολύ για τη συμμετοχή στην Alpha λειτουργία του {{ site_name }}. |
|
34 |
|
|
35 |
Με εκτίμηση, |
|
36 |
|
|
37 |
Εθνικό Δίκτυο Έρευνας και Τεχνολογίας (ΕΔΕΤ/GRNET) |
|
38 |
|
|
39 |
-- |
|
40 |
|
|
41 |
Dear {{ user.realname }}, |
|
42 |
|
|
43 |
Your account for GRNET's {{ site_name }} service for its Alpha test phase has been |
|
44 |
activated. |
|
45 |
|
|
46 |
To login, please use the following link: |
|
47 |
|
|
48 |
{{ url }} |
|
49 |
|
|
50 |
Please note: |
|
51 |
|
|
52 |
This service is, for a few weeks, in Alpha. Although every care has been |
|
53 |
taken to ensure high quality, it is possible there may still be software |
|
54 |
bugs, or periods of limited service availability. For this reason we kindly |
|
55 |
request you not to transfer important files to {{ site_name}} yet. |
|
56 |
Also, please bear in mind that all data will be deleted when the service moves to Beta. |
|
57 |
We will notify you before the transition. |
|
58 |
|
|
59 |
For more information, please visit {{ baseurl }}, after |
|
60 |
activating your invitation. |
|
61 |
|
|
62 |
We look forward to your feedback, to improve the functionality and |
|
63 |
reliability of this innovative service. |
|
64 |
|
|
65 |
For any remarks or problems you can contact {{ support }}. |
|
66 |
|
|
67 |
Thank you for participating in the Alpha test of {{ site_name }}. |
|
68 |
|
|
69 |
Greek Research and Technonogy Network - GRNET |
/dev/null | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
from django.conf.urls.defaults import patterns, url |
|
35 |
|
|
36 |
urlpatterns = patterns('astakos.im.admin.views', |
|
37 |
url(r'^$', 'admin'), |
|
38 |
|
|
39 |
url(r'^users/?$', 'users_list'), |
|
40 |
url(r'^users/(\d+)/?$', 'users_info'), |
|
41 |
url(r'^users/create$', 'users_create'), |
|
42 |
url(r'^users/(\d+)/modify/?$', 'users_modify'), |
|
43 |
url(r'^users/(\d+)/delete/?$', 'users_delete'), |
|
44 |
url(r'^users/export/?$', 'users_export'), |
|
45 |
url(r'^users/pending/?$', 'pending_users'), |
|
46 |
url(r'^users/activate/(\d+)/?$', 'users_activate'), |
|
47 |
|
|
48 |
url(r'^invitations/?$', 'invitations_list'), |
|
49 |
url(r'^invitations/export/?$', 'invitations_export'), |
|
50 |
) |
/dev/null | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
import socket |
|
35 |
import csv |
|
36 |
|
|
37 |
from functools import wraps |
|
38 |
from math import ceil |
|
39 |
from smtplib import SMTPException |
|
40 |
|
|
41 |
from django.http import HttpResponse, HttpResponseRedirect |
|
42 |
from django.shortcuts import redirect |
|
43 |
from django.template.loader import render_to_string |
|
44 |
from django.utils.http import urlencode |
|
45 |
from django.utils.translation import ugettext as _ |
|
46 |
from django.core.urlresolvers import reverse |
|
47 |
from django.contrib import messages |
|
48 |
from django.db import transaction |
|
49 |
|
|
50 |
from astakos.im.models import AstakosUser, Invitation |
|
51 |
from astakos.im.util import get_context |
|
52 |
from astakos.im.forms import * |
|
53 |
from astakos.im.views import render_response, index |
|
54 |
from astakos.im.admin.forms import AdminProfileForm |
|
55 |
from astakos.im.admin.forms import AdminUserCreationForm |
|
56 |
from astakos.im.settings import BYPASS_ADMIN_AUTH, ADMIN_PAGE_LIMIT, DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL |
|
57 |
from astakos.im.admin.functions import activate |
|
58 |
|
|
59 |
def requires_admin(func): |
|
60 |
""" |
|
61 |
Decorator checkes whether the request.user is a superuser and if not |
|
62 |
redirects to login page. |
|
63 |
""" |
|
64 |
@wraps(func) |
|
65 |
def wrapper(request, *args): |
|
66 |
if not BYPASS_ADMIN_AUTH: |
|
67 |
if request.user.is_anonymous(): |
|
68 |
next = urlencode({'next': request.build_absolute_uri()}) |
|
69 |
login_uri = reverse(index) + '?' + next |
|
70 |
return HttpResponseRedirect(login_uri) |
|
71 |
if not request.user.is_superuser: |
|
72 |
return HttpResponse('Forbidden', status=403) |
|
73 |
return func(request, *args) |
|
74 |
return wrapper |
|
75 |
|
|
76 |
@requires_admin |
|
77 |
def admin(request, template_name='admin.html', extra_context={}): |
|
78 |
""" |
|
79 |
Renders the admin page |
|
80 |
|
|
81 |
If the ``request.user`` is not a superuser redirects to login page. |
|
82 |
|
|
83 |
**Arguments** |
|
84 |
|
|
85 |
``template_name`` |
|
86 |
A custom template to use. This is optional; if not specified, |
|
87 |
this will default to ``admin.html``. |
|
88 |
|
|
89 |
``extra_context`` |
|
90 |
An dictionary of variables to add to the template context. |
|
91 |
|
|
92 |
**Template:** |
|
93 |
|
|
94 |
admin.html or ``template_name`` keyword argument. |
|
95 |
|
|
96 |
**Template Context:** |
|
97 |
|
|
98 |
The template context is extended by: |
|
99 |
|
|
100 |
* tab: the name of the active tab |
|
101 |
* stats: dictionary containing the number of all and prending users |
|
102 |
""" |
|
103 |
stats = {} |
|
104 |
stats['users'] = AstakosUser.objects.count() |
|
105 |
stats['pending'] = AstakosUser.objects.filter(is_active = False).count() |
|
106 |
|
|
107 |
invitations = Invitation.objects.all() |
|
108 |
stats['invitations'] = invitations.count() |
|
109 |
stats['invitations_consumed'] = invitations.filter(is_consumed=True).count() |
|
110 |
|
|
111 |
kwargs = {'tab': 'home', 'stats': stats} |
|
112 |
context = get_context(request, extra_context,**kwargs) |
|
113 |
return render_response(template_name, context_instance = context) |
|
114 |
|
|
115 |
@requires_admin |
|
116 |
def users_list(request, template_name='users_list.html', extra_context={}): |
|
117 |
""" |
|
118 |
Displays the list of all users. |
|
119 |
|
|
120 |
If the ``request.user`` is not a superuser redirects to login page. |
|
121 |
|
|
122 |
**Arguments** |
|
123 |
|
|
124 |
``template_name`` |
|
125 |
A custom template to use. This is optional; if not specified, |
|
126 |
this will default to ``users_list.html``. |
|
127 |
|
|
128 |
``extra_context`` |
|
129 |
An dictionary of variables to add to the template context. |
|
130 |
|
|
131 |
**Template:** |
|
132 |
|
|
133 |
users_list.html or ``template_name`` keyword argument. |
|
134 |
|
|
135 |
**Template Context:** |
|
136 |
|
|
137 |
The template context is extended by: |
|
138 |
|
|
139 |
* users: list of users fitting in current page |
|
140 |
* filter: search key |
|
141 |
* pages: the number of pages |
|
142 |
* prev: the previous page |
|
143 |
* next: the current page |
|
144 |
|
|
145 |
**Settings:** |
|
146 |
|
|
147 |
* ADMIN_PAGE_LIMIT: Show these many users per page in admin interface |
|
148 |
""" |
|
149 |
users = AstakosUser.objects.order_by('id') |
|
150 |
|
|
151 |
filter = request.GET.get('filter', '') |
|
152 |
if filter: |
|
153 |
if filter.startswith('-'): |
|
154 |
users = users.exclude(username__icontains=filter[1:]) |
|
155 |
else: |
|
156 |
users = users.filter(username__icontains=filter) |
|
157 |
|
|
158 |
try: |
|
159 |
page = int(request.GET.get('page', 1)) |
|
160 |
except ValueError: |
|
161 |
page = 1 |
|
162 |
offset = max(0, page - 1) * ADMIN_PAGE_LIMIT |
|
163 |
limit = offset + ADMIN_PAGE_LIMIT |
|
164 |
|
|
165 |
npages = int(ceil(1.0 * users.count() / ADMIN_PAGE_LIMIT)) |
|
166 |
prev = page - 1 if page > 1 else None |
|
167 |
next = page + 1 if page < npages else None |
|
168 |
|
|
169 |
kwargs = {'users':users[offset:limit], |
|
170 |
'filter':filter, |
|
171 |
'pages':range(1, npages + 1), |
|
172 |
'prev':prev, |
|
173 |
'next':next} |
|
174 |
context = get_context(request, extra_context,**kwargs) |
|
175 |
return render_response(template_name, context_instance = context) |
|
176 |
|
|
177 |
@requires_admin |
|
178 |
def users_info(request, user_id, template_name='users_info.html', extra_context={}): |
|
179 |
""" |
|
180 |
Displays the specific user profile. |
|
181 |
|
|
182 |
If the ``request.user`` is not a superuser redirects to login page. |
|
183 |
|
|
184 |
**Arguments** |
|
185 |
|
|
186 |
``template_name`` |
|
187 |
A custom template to use. This is optional; if not specified, |
|
188 |
this will default to ``users_info.html``. |
|
189 |
|
|
190 |
``extra_context`` |
|
191 |
An dictionary of variables to add to the template context. |
|
192 |
|
|
193 |
**Template:** |
|
194 |
|
|
195 |
users_info.html or ``template_name`` keyword argument. |
|
196 |
|
|
197 |
**Template Context:** |
|
198 |
|
|
199 |
The template context is extended by: |
|
200 |
|
|
201 |
* user: the user instance identified by ``user_id`` keyword argument |
|
202 |
""" |
|
203 |
if not extra_context: |
|
204 |
extra_context = {} |
|
205 |
user = AstakosUser.objects.get(id=user_id) |
|
206 |
return render_response(template_name, |
|
207 |
form = AdminProfileForm(instance=user), |
|
208 |
user = user, |
|
209 |
context_instance = get_context(request, extra_context)) |
|
210 |
|
|
211 |
@requires_admin |
|
212 |
def users_modify(request, user_id, template_name='users_info.html', extra_context={}): |
|
213 |
""" |
|
214 |
Update the specific user information. Upon success redirects to ``user_info`` view. |
|
215 |
|
|
216 |
If the ``request.user`` is not a superuser redirects to login page. |
|
217 |
""" |
|
218 |
user = AstakosUser.objects.get(id = user_id) |
|
219 |
form = AdminProfileForm(request.POST, instance=user) |
|
220 |
if form.is_valid(): |
|
221 |
form.save() |
|
222 |
return users_info(request, user.id, template_name, extra_context) |
|
223 |
return render_response(template_name, |
|
224 |
form = form, |
|
225 |
context_instance = get_context(request, extra_context)) |
|
226 |
|
|
227 |
@requires_admin |
|
228 |
def users_delete(request, user_id): |
|
229 |
""" |
|
230 |
Deletes the specified user |
|
231 |
|
|
232 |
If the ``request.user`` is not a superuser redirects to login page. |
|
233 |
""" |
|
234 |
user = AstakosUser.objects.get(id=user_id) |
|
235 |
user.delete() |
|
236 |
return redirect(users_list) |
|
237 |
|
|
238 |
@requires_admin |
|
239 |
def pending_users(request, template_name='pending_users.html', extra_context={}): |
|
240 |
""" |
|
241 |
Displays the list of the pending users. |
|
242 |
|
|
243 |
If the ``request.user`` is not a superuser redirects to login page. |
|
244 |
|
|
245 |
**Arguments** |
|
246 |
|
|
247 |
``template_name`` |
|
248 |
A custom template to use. This is optional; if not specified, |
|
249 |
this will default to ``users_list.html``. |
|
250 |
|
|
251 |
``extra_context`` |
|
252 |
An dictionary of variables to add to the template context. |
|
253 |
|
|
254 |
**Template:** |
|
255 |
|
|
256 |
pending_users.html or ``template_name`` keyword argument. |
|
257 |
|
|
258 |
**Template Context:** |
|
259 |
|
|
260 |
The template context is extended by: |
|
261 |
|
|
262 |
* users: list of pending users fitting in current page |
|
263 |
* filter: search key |
|
264 |
* pages: the number of pages |
|
265 |
* prev: the previous page |
|
266 |
* next: the current page |
|
267 |
|
|
268 |
**Settings:** |
|
269 |
|
|
270 |
* ADMIN_PAGE_LIMIT: Show these many users per page in admin interface |
|
271 |
""" |
|
272 |
users = AstakosUser.objects.order_by('id') |
|
273 |
|
|
274 |
users = users.filter(is_active = False) |
|
275 |
|
|
276 |
filter = request.GET.get('filter', '') |
|
277 |
if filter: |
|
278 |
if filter.startswith('-'): |
|
279 |
users = users.exclude(username__icontains=filter[1:]) |
|
280 |
else: |
|
281 |
users = users.filter(username__icontains=filter) |
|
282 |
|
|
283 |
try: |
|
284 |
page = int(request.GET.get('page', 1)) |
|
285 |
except ValueError: |
|
286 |
page = 1 |
|
287 |
offset = max(0, page - 1) * ADMIN_PAGE_LIMIT |
|
288 |
limit = offset + ADMIN_PAGE_LIMIT |
|
289 |
|
|
290 |
npages = int(ceil(1.0 * users.count() / ADMIN_PAGE_LIMIT)) |
|
291 |
prev = page - 1 if page > 1 else None |
|
292 |
next = page + 1 if page < npages else None |
|
293 |
kwargs = {'users':users[offset:limit], |
|
294 |
'filter':filter, |
|
295 |
'pages':range(1, npages + 1), |
|
296 |
'page':page, |
|
297 |
'prev':prev, |
|
298 |
'next':next} |
|
299 |
return render_response(template_name, |
|
300 |
context_instance = get_context(request, extra_context,**kwargs)) |
|
301 |
|
|
302 |
@requires_admin |
|
303 |
@transaction.commit_manually |
|
304 |
def users_activate(request, user_id, template_name='pending_users.html', extra_context={}, email_template_name='welcome_email.txt'): |
|
305 |
""" |
|
306 |
Activates the specific user and sends an email. Upon success renders the |
|
307 |
``template_name`` keyword argument if exists else renders ``pending_users.html``. |
|
308 |
|
|
309 |
If the ``request.user`` is not a superuser redirects to login page. |
|
310 |
|
|
311 |
**Arguments** |
|
312 |
|
|
313 |
``template_name`` |
|
314 |
A custom template to use. This is optional; if not specified, |
|
315 |
this will default to ``users_list.html``. |
|
316 |
|
|
317 |
``extra_context`` |
|
318 |
An dictionary of variables to add to the template context. |
|
319 |
|
|
320 |
**Templates:** |
|
321 |
|
|
322 |
pending_users.html or ``template_name`` keyword argument. |
|
323 |
welcome_email.txt or ``email_template_name`` keyword argument. |
|
324 |
|
|
325 |
**Template Context:** |
|
326 |
|
|
327 |
The template context is extended by: |
|
328 |
|
|
329 |
* users: list of pending users fitting in current page |
|
330 |
* filter: search key |
|
331 |
* pages: the number of pages |
|
332 |
* prev: the previous page |
|
333 |
* next: the current page |
|
334 |
""" |
|
335 |
user = AstakosUser.objects.get(id=user_id) |
|
336 |
status = messages.SUCCESS |
|
337 |
try: |
|
338 |
activate(user, email_template_name) |
|
339 |
message = _('Greeting sent to %s' % user.email) |
|
340 |
transaction.commit() |
|
341 |
except (SMTPException, socket.error) as e: |
|
342 |
status = messages.ERROR |
|
343 |
name = 'strerror' |
|
344 |
message = getattr(e, name) if hasattr(e, name) else e |
|
345 |
transaction.rollback() |
|
346 |
messages.add_message(request, status, message) |
|
347 |
|
|
348 |
users = AstakosUser.objects.order_by('id') |
|
349 |
users = users.filter(is_active = False) |
|
350 |
|
|
351 |
try: |
|
352 |
page = int(request.POST.get('page', 1)) |
|
353 |
except ValueError: |
|
354 |
page = 1 |
|
355 |
offset = max(0, page - 1) * ADMIN_PAGE_LIMIT |
|
356 |
limit = offset + ADMIN_PAGE_LIMIT |
|
357 |
|
|
358 |
npages = int(ceil(1.0 * users.count() / ADMIN_PAGE_LIMIT)) |
|
359 |
prev = page - 1 if page > 1 else None |
|
360 |
next = page + 1 if page < npages else None |
|
361 |
kwargs = {'users':users[offset:limit], |
|
362 |
'filter':'', |
|
363 |
'pages':range(1, npages + 1), |
|
364 |
'page':page, |
|
365 |
'prev':prev, |
|
366 |
'next':next} |
|
367 |
return render_response(template_name, |
|
368 |
context_instance = get_context(request, extra_context,**kwargs)) |
|
369 |
|
|
370 |
@requires_admin |
|
371 |
def invitations_list(request, template_name='invitations_list.html', extra_context={}): |
|
372 |
""" |
|
373 |
Displays a list with the Invitations. |
|
374 |
|
|
375 |
If the ``request.user`` is not a superuser redirects to login page. |
|
376 |
|
|
377 |
**Arguments** |
|
378 |
|
|
379 |
``template_name`` |
|
380 |
A custom template to use. This is optional; if not specified, |
|
381 |
this will default to ``invitations_list.html``. |
|
382 |
|
|
383 |
``extra_context`` |
|
384 |
An dictionary of variables to add to the template context. |
|
385 |
|
|
386 |
**Templates:** |
|
387 |
|
|
388 |
invitations_list.html or ``template_name`` keyword argument. |
|
389 |
|
|
390 |
**Template Context:** |
|
391 |
|
|
392 |
The template context is extended by: |
|
393 |
|
|
394 |
* invitations: list of invitations fitting in current page |
|
395 |
* filter: search key |
|
396 |
* pages: the number of pages |
|
397 |
* prev: the previous page |
|
398 |
* next: the current page |
|
399 |
""" |
|
400 |
invitations = Invitation.objects.order_by('id') |
|
401 |
|
|
402 |
filter = request.GET.get('filter', '') |
|
403 |
if filter: |
|
404 |
if filter.startswith('-'): |
|
405 |
invitations = invitations.exclude(username__icontains=filter[1:]) |
|
406 |
else: |
|
407 |
invitations = invitations.filter(username__icontains=filter) |
|
408 |
|
|
409 |
try: |
|
410 |
page = int(request.GET.get('page', 1)) |
|
411 |
except ValueError: |
|
412 |
page = 1 |
|
413 |
offset = max(0, page - 1) * ADMIN_PAGE_LIMIT |
|
414 |
limit = offset + ADMIN_PAGE_LIMIT |
|
415 |
|
|
416 |
npages = int(ceil(1.0 * invitations.count() / ADMIN_PAGE_LIMIT)) |
|
417 |
prev = page - 1 if page > 1 else None |
|
418 |
next = page + 1 if page < npages else None |
|
419 |
kwargs = {'invitations':invitations[offset:limit], |
|
420 |
'filter':filter, |
|
421 |
'pages':range(1, npages + 1), |
|
422 |
'page':page, |
|
423 |
'prev':prev, |
|
424 |
'next':next} |
|
425 |
return render_response(template_name, |
|
426 |
context_instance = get_context(request, extra_context,**kwargs)) |
|
427 |
|
|
428 |
@requires_admin |
|
429 |
def invitations_export(request): |
|
430 |
""" |
|
431 |
Exports the invitation list in csv file. |
|
432 |
""" |
|
433 |
# Create the HttpResponse object with the appropriate CSV header. |
|
434 |
response = HttpResponse(mimetype='text/csv') |
|
435 |
response['Content-Disposition'] = 'attachment; filename=invitations.csv' |
|
436 |
|
|
437 |
writer = csv.writer(response) |
|
438 |
writer.writerow(['ID', |
|
439 |
'Username', |
|
440 |
'Real Name', |
|
441 |
'Code', |
|
442 |
'Inviter username', |
|
443 |
'Inviter Real Name', |
|
444 |
'Is_accepted', |
|
445 |
'Created', |
|
446 |
'Consumed',]) |
|
447 |
invitations = Invitation.objects.order_by('id') |
|
448 |
for inv in invitations: |
|
449 |
|
|
450 |
writer.writerow([inv.id, |
|
451 |
inv.username.encode("utf-8"), |
|
452 |
inv.realname.encode("utf-8"), |
|
453 |
inv.code, |
|
454 |
inv.inviter.username.encode("utf-8"), |
|
455 |
inv.inviter.realname.encode("utf-8"), |
|
456 |
inv.is_accepted, |
|
457 |
inv.created, |
|
458 |
inv.consumed]) |
|
459 |
|
|
460 |
return response |
|
461 |
|
|
462 |
|
|
463 |
@requires_admin |
|
464 |
def users_export(request): |
|
465 |
""" |
|
466 |
Exports the user list in csv file. |
|
467 |
""" |
|
468 |
# Create the HttpResponse object with the appropriate CSV header. |
|
469 |
response = HttpResponse(mimetype='text/csv') |
|
470 |
response['Content-Disposition'] = 'attachment; filename=users.csv' |
|
471 |
|
|
472 |
writer = csv.writer(response) |
|
473 |
writer.writerow(['ID', |
|
474 |
'Username', |
|
475 |
'Real Name', |
|
476 |
'Admin', |
|
477 |
'Affiliation', |
|
478 |
'Is active?', |
|
479 |
'Quota (GiB)', |
|
480 |
'Updated',]) |
|
481 |
users = AstakosUser.objects.order_by('id') |
|
482 |
for u in users: |
|
483 |
writer.writerow([u.id, |
|
484 |
u.username.encode("utf-8"), |
|
485 |
u.realname.encode("utf-8"), |
|
486 |
u.is_superuser, |
|
487 |
u.affiliation.encode("utf-8"), |
|
488 |
u.is_active, |
|
489 |
u.quota, |
|
490 |
u.updated]) |
|
491 |
|
|
492 |
return response |
|
493 |
|
|
494 |
@requires_admin |
|
495 |
def users_create(request, template_name='users_create.html', extra_context={}): |
|
496 |
""" |
|
497 |
Creates a user. Upon success redirect to ``users_info`` view. |
|
498 |
|
|
499 |
**Arguments** |
|
500 |
|
|
501 |
``template_name`` |
|
502 |
A custom template to use. This is optional; if not specified, |
|
503 |
this will default to ``users_create.html``. |
|
504 |
|
|
505 |
``extra_context`` |
|
506 |
An dictionary of variables to add to the template context. |
|
507 |
|
|
508 |
**Templates:** |
|
509 |
|
|
510 |
users_create.html or ``template_name`` keyword argument. |
|
511 |
""" |
|
512 |
if request.method == 'GET': |
|
513 |
form = AdminUserCreationForm() |
|
514 |
elif request.method == 'POST': |
|
515 |
form = AdminUserCreationForm(request.POST) |
|
516 |
if form.is_valid(): |
|
517 |
try: |
|
518 |
user = form.save() |
|
519 |
return users_info(request, user.id) |
|
520 |
except ValueError, e: |
|
521 |
messages.add_message(request, messages.ERROR, e) |
|
522 |
return render_response(template_name, |
|
523 |
form = form, |
|
524 |
context_instance=get_context(request, extra_context)) |
/dev/null | ||
---|---|---|
1 |
# Copyright 2011-2012 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
from astakos.im.settings import BACKEND_BLOCK_MODULE, BACKEND_BLOCK_PATH, BACKEND_DB_CONNECTION, BACKEND_DB_MODULE, BACKEND_QUOTA, BACKEND_VERSIONING |
|
35 |
|
|
36 |
from pithos.backends import connect_backend |
|
37 |
|
|
38 |
def get_backend(): |
|
39 |
backend = connect_backend(db_module=BACKEND_DB_MODULE, |
|
40 |
db_connection=BACKEND_DB_CONNECTION, |
|
41 |
block_module=BACKEND_BLOCK_MODULE, |
|
42 |
block_path=BACKEND_BLOCK_PATH) |
|
43 |
backend.default_policy['quota'] = BACKEND_QUOTA |
|
44 |
backend.default_policy['versioning'] = BACKEND_VERSIONING |
|
45 |
return backend |
|
46 |
|
|
47 |
def get_quota(user): |
|
48 |
backend = get_backend() |
|
49 |
quota = backend.get_account_policy(user, user)['quota'] |
|
50 |
backend.close() |
|
51 |
return quota |
|
52 |
|
|
53 |
def set_quota(user, quota): |
|
54 |
backend = get_backend() |
|
55 |
backend.update_account_policy(user, user, {'quota': quota}) |
|
56 |
backend.close() |
|
57 |
return quota |
b/astakos/im/models.py | ||
---|---|---|
41 | 41 |
from django.db import models |
42 | 42 |
from django.contrib.auth.models import User, UserManager |
43 | 43 |
|
44 |
from astakos.im.interface import get_quota, set_quota |
|
45 | 44 |
from astakos.im.settings import DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL, AUTH_TOKEN_DURATION |
46 | 45 |
|
47 | 46 |
class AstakosUser(User): |
... | ... | |
84 | 83 |
self.last_name = parts[0] |
85 | 84 |
|
86 | 85 |
@property |
87 |
def quota(self): |
|
88 |
return get_quota(self.username) |
|
89 |
|
|
90 |
@quota.setter |
|
91 |
def quota(self, value): |
|
92 |
set_quota(self.username, value) |
|
93 |
|
|
94 |
@property |
|
95 | 86 |
def invitation(self): |
96 | 87 |
try: |
97 | 88 |
return Invitation.objects.get(username=self.email) |
b/astakos/im/settings.py | ||
---|---|---|
8 | 8 |
# to be this many hours after their creation time. |
9 | 9 |
AUTH_TOKEN_DURATION = getattr(settings, 'ASTAKOS_AUTH_TOKEN_DURATION', 30 * 24) |
10 | 10 |
|
11 |
# Bypass authentication for user administration. |
|
12 |
BYPASS_ADMIN_AUTH = getattr(settings, 'ASTAKOS_BYPASS_ADMIN_AUTH', False) |
|
13 |
|
|
14 |
# Show these many users per page in admin interface. |
|
15 |
ADMIN_PAGE_LIMIT = getattr(settings, 'ASTAKOS_ADMIN_PAGE_LIMIT', 100) |
|
16 |
|
|
17 | 11 |
# Authenticate via Twitter. |
18 | 12 |
TWITTER_KEY = getattr(settings, 'ASTAKOS_TWITTER_KEY', '') |
19 | 13 |
TWITTER_SECRET = getattr(settings, 'ASTAKOS_TWITTER_SECRET', '') |
... | ... | |
55 | 49 |
|
56 | 50 |
# Set service name |
57 | 51 |
SITENAME = getattr(settings, 'ASTAKOS_SITENAME', 'Pithos+') |
58 |
|
|
59 |
# SQLAlchemy (choose SQLite/MySQL/PostgreSQL). |
|
60 |
BACKEND_DB_MODULE = getattr(settings, 'PITHOS_BACKEND_DB_MODULE', 'pithos.backends.lib.sqlalchemy') |
|
61 |
BACKEND_DB_CONNECTION = getattr(settings, 'PITHOS_BACKEND_DB_CONNECTION', 'sqlite:///' + join(PROJECT_PATH, 'backend.db')) |
|
62 |
|
|
63 |
# Block storage. |
|
64 |
BACKEND_BLOCK_MODULE = getattr(settings, 'PITHOS_BACKEND_BLOCK_MODULE', 'pithos.backends.lib.hashfiler') |
|
65 |
BACKEND_BLOCK_PATH = getattr(settings, 'PITHOS_BACKEND_BLOCK_PATH', join(PROJECT_PATH, 'data/')) |
|
66 |
|
|
67 |
# Default setting for new accounts. |
|
68 |
BACKEND_QUOTA = getattr(settings, 'PITHOS_BACKEND_QUOTA', 50 * 1024 * 1024 * 1024) |
|
69 |
BACKEND_VERSIONING = getattr(settings, 'PITHOS_BACKEND_VERSIONING', 'auto') |
b/astakos/im/urls.py | ||
---|---|---|
43 | 43 |
url(r'^feedback/?$', 'send_feedback'), |
44 | 44 |
url(r'^signup/?$', 'signup'), |
45 | 45 |
url(r'^logout/?$', 'logout'), |
46 |
url(r'^activate/?$', 'activate'), |
|
47 |
url(r'^admin/', include('astakos.im.admin.urls')), |
|
46 |
url(r'^activate/?$', 'activate') |
|
48 | 47 |
) |
49 | 48 |
|
50 | 49 |
urlpatterns += patterns('astakos.im.target', |
... | ... | |
73 | 72 |
|
74 | 73 |
if INVITATIONS_ENABLED: |
75 | 74 |
urlpatterns += patterns('astakos.im.views', |
76 |
url(r'^invite/?$', 'invite'),
|
|
75 |
url(r'^invite/?$', 'invite') |
|
77 | 76 |
) |
78 | 77 |
|
79 | 78 |
if 'shibboleth' in IM_MODULES: |
... | ... | |
90 | 89 |
urlpatterns += patterns('astakos.im.api', |
91 | 90 |
url(r'^authenticate/?$', 'authenticate') |
92 | 91 |
) |
93 |
|
Also available in: Unified diff