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 %}">&larr; Previous</a></li>
53
    {% else %}
54
      <li class="prev disabled"><a href="#">&larr; 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 %}">&rarr; Next</a></li>
63
    {% else %}
64
      <li class="next disabled"><a href="#">&rarr; 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 %}">&larr; Previous</a></li>
53
    {% else %}
54
      <li class="prev disabled"><a href="#">&larr; 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 %}">&rarr; Next</a></li>
63
    {% else %}
64
      <li class="next disabled"><a href="#">&rarr; 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
        &nbsp;&nbsp;
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 %}">&larr; Previous</a></li>
51
    {% else %}
52
      <li class="prev disabled"><a href="#">&larr; 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 %}">&rarr; Next</a></li>
61
    {% else %}
62
      <li class="next disabled"><a href="#">&rarr; 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