Revision 5d56107c
b/README | ||
---|---|---|
13 | 13 |
To build the documentation you need to have Sphinx (http://sphinx.pocoo.org/) installed. |
14 | 14 |
|
15 | 15 |
On a typical debian-based Linux system run: |
16 |
apt-get install python-django python-django-south python-setuptools python-sphinx python-httplib2
|
|
16 |
apt-get install python-django python-setuptools python-sphinx
|
|
17 | 17 |
apt-get install python-sqlalchemy python-mysqldb python-psycopg2 |
18 | 18 |
|
19 | 19 |
Then run: |
... | ... | |
30 | 30 |
------------------ |
31 | 31 |
|
32 | 32 |
Make sure you have all required packages installed: |
33 |
apt-get install python-django python-setuptools python-sphinx python-httplib2
|
|
33 |
apt-get install python-django python-setuptools python-sphinx |
|
34 | 34 |
apt-get install python-sqlalchemy python-mysqldb python-psycopg2 |
35 | 35 |
|
36 | 36 |
Then run: |
37 | 37 |
python manage.py syncdb |
38 |
python manage.py migrate im |
|
39 |
python manage.py loaddata admin_user |
|
40 | 38 |
python manage.py runserver |
41 | 39 |
|
42 |
Go to: |
|
43 |
http://127.0.0.1:8000/im/admin?user=admin&token=0000 |
|
44 |
|
|
45 | 40 |
This server is useful during development, but should not be used for deployment. |
46 | 41 |
To deploy Pithos using Apache, take a look at the Administrator Guide in docs. |
47 | 42 |
|
b/docs/source/adminguide.rst | ||
---|---|---|
8 | 8 |
|
9 | 9 |
Install packages:: |
10 | 10 |
|
11 |
apt-get install git python-django python-django-south python-setuptools python-sphinx python-httplib2
|
|
11 |
apt-get install git python-django python-setuptools python-sphinx
|
|
12 | 12 |
apt-get install python-sqlalchemy python-mysqldb python-psycopg2 |
13 | 13 |
apt-get install apache2 libapache2-mod-wsgi |
14 | 14 |
|
... | ... | |
21 | 21 |
|
22 | 22 |
cd /pithos/pithos |
23 | 23 |
python manage.py syncdb |
24 |
python manage.py migrate im |
|
25 | 24 |
cd /pithos |
26 | 25 |
python setup.py build_sphinx |
27 | 26 |
|
... | ... | |
126 | 125 |
|
127 | 126 |
Useful alias to add in ``~/.bashrc``:: |
128 | 127 |
|
129 |
alias pithos-sync='cd /pithos && git pull && python setup.py build_sphinx && cd pithos && python manage.py migrate im && /etc/init.d/apache2 restart'
|
|
128 |
alias pithos-sync='cd /pithos && git pull && python setup.py build_sphinx && /etc/init.d/apache2 restart' |
|
130 | 129 |
|
131 | 130 |
Gunicorn Setup |
132 | 131 |
-------------- |
/dev/null | ||
---|---|---|
1 |
#!/usr/bin/env python |
|
2 |
|
|
3 |
import sys |
|
4 |
|
|
5 |
if len(sys.argv) != 4: |
|
6 |
print "Usage: %s <inviter token> <invitee name> <invitee email>" % (sys.argv[0],) |
|
7 |
sys.exit(-1) |
|
8 |
|
|
9 |
import httplib2 |
|
10 |
http = httplib2.Http(disable_ssl_certificate_validation=True) |
|
11 |
|
|
12 |
url = 'https://pithos.dev.grnet.gr/im/invite' |
|
13 |
|
|
14 |
import urllib |
|
15 |
params = urllib.urlencode({ |
|
16 |
'uniq': sys.argv[3], |
|
17 |
'realname': sys.argv[2] |
|
18 |
}) |
|
19 |
|
|
20 |
response, content = http.request(url, 'POST', params, |
|
21 |
headers={'Content-type': 'application/x-www-form-urlencoded', 'X-Auth-Token': sys.argv[1]} |
|
22 |
) |
|
23 |
|
|
24 |
if response['status'] == '200': |
|
25 |
print 'OK' |
|
26 |
sys.exit(0) |
|
27 |
else: |
|
28 |
print response, content |
|
29 |
sys.exit(-1) |
/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 traceback import format_exc |
|
35 |
from time import time, mktime |
|
36 |
from django.conf import settings |
|
37 |
from django.http import HttpResponse |
|
38 |
from django.utils import simplejson as json |
|
39 |
|
|
40 |
from pithos.im.faults import BadRequest, Unauthorized, ServiceUnavailable |
|
41 |
from pithos.im.models import User |
|
42 |
|
|
43 |
import datetime |
|
44 |
|
|
45 |
def render_fault(request, fault): |
|
46 |
if settings.DEBUG or settings.TEST: |
|
47 |
fault.details = format_exc(fault) |
|
48 |
|
|
49 |
request.serialization = 'text' |
|
50 |
data = '\n'.join((fault.message, fault.details)) + '\n' |
|
51 |
response = HttpResponse(data, status=fault.code) |
|
52 |
return response |
|
53 |
|
|
54 |
def update_response_headers(response): |
|
55 |
response['Content-Type'] = 'application/json; charset=UTF-8' |
|
56 |
response['Content-Length'] = len(response.content) |
|
57 |
|
|
58 |
def authenticate(request): |
|
59 |
# Normal Response Codes: 204 |
|
60 |
# Error Response Codes: serviceUnavailable (503) |
|
61 |
# badRequest (400) |
|
62 |
# unauthorised (401) |
|
63 |
try: |
|
64 |
if request.method != 'GET': |
|
65 |
raise BadRequest('Method not allowed.') |
|
66 |
x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN') |
|
67 |
if not x_auth_token: |
|
68 |
return render_fault(request, BadRequest('Missing X-Auth-Token')) |
|
69 |
|
|
70 |
try: |
|
71 |
user = User.objects.get(auth_token=x_auth_token) |
|
72 |
except User.DoesNotExist, e: |
|
73 |
return render_fault(request, Unauthorized('Invalid X-Auth-Token')) |
|
74 |
|
|
75 |
# Check if the is active. |
|
76 |
if user.state != 'ACTIVE': |
|
77 |
return render_fault(request, Unauthorized('User inactive')) |
|
78 |
|
|
79 |
# Check if the token has expired. |
|
80 |
if (time() - mktime(user.auth_token_expires.timetuple())) > 0: |
|
81 |
return render_fault(request, Unauthorized('Authentication expired')) |
|
82 |
|
|
83 |
response = HttpResponse() |
|
84 |
response.status=204 |
|
85 |
user_info = user.__dict__ |
|
86 |
for k,v in user_info.items(): |
|
87 |
if isinstance(v, datetime.datetime): |
|
88 |
user_info[k] = v.strftime('%a, %d-%b-%Y %H:%M:%S %Z') |
|
89 |
user_info.pop('_state') |
|
90 |
response.content = json.dumps(user_info) |
|
91 |
update_response_headers(response) |
|
92 |
return response |
|
93 |
except BaseException, e: |
|
94 |
fault = ServiceUnavailable('Unexpected error') |
|
95 |
return render_fault(request, fault) |
/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 |
def camelCase(s): |
|
35 |
return s[0].lower() + s[1:] |
|
36 |
|
|
37 |
class Fault(Exception): |
|
38 |
def __init__(self, message='', details='', name=''): |
|
39 |
Exception.__init__(self, message, details, name) |
|
40 |
self.message = message |
|
41 |
self.details = details |
|
42 |
self.name = name or camelCase(self.__class__.__name__) |
|
43 |
|
|
44 |
class BadRequest(Fault): |
|
45 |
code = 400 |
|
46 |
|
|
47 |
class Unauthorized(Fault): |
|
48 |
code = 401 |
|
49 |
|
|
50 |
class ServiceUnavailable(Fault): |
|
51 |
code = 503 |
/dev/null | ||
---|---|---|
1 |
[ |
|
2 |
{ |
|
3 |
"model": "im.User", |
|
4 |
"pk": 1, |
|
5 |
"fields": { |
|
6 |
"uniq": "admin", |
|
7 |
"level": 0, |
|
8 |
"invitations": 10000, |
|
9 |
"is_admin": true, |
|
10 |
"auth_token": "0000", |
|
11 |
"auth_token_created": "2011-09-11 09:17:14", |
|
12 |
"auth_token_expires": "2012-09-11 09:17:14", |
|
13 |
"created": "2011-09-11", |
|
14 |
"updated": "2011-09-11" |
|
15 |
} |
|
16 |
} |
|
17 |
] |
/dev/null | ||
---|---|---|
1 |
[ |
|
2 |
{ |
|
3 |
"model": "im.User", |
|
4 |
"pk": 1, |
|
5 |
"fields": { |
|
6 |
"uniq": "test", |
|
7 |
"level": 0, |
|
8 |
"invitations": 10000, |
|
9 |
"auth_token": "0000", |
|
10 |
"auth_token_created": "2011-04-07 09:17:14", |
|
11 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
12 |
"created": "2011-02-06", |
|
13 |
"updated": "2011-02-06" |
|
14 |
} |
|
15 |
}, |
|
16 |
{ |
|
17 |
"model": "im.User", |
|
18 |
"pk": 2, |
|
19 |
"fields": { |
|
20 |
"uniq": "verigak", |
|
21 |
"level": 1, |
|
22 |
"invitations": 3, |
|
23 |
"is_admin": 1, |
|
24 |
"auth_token": "0001", |
|
25 |
"auth_token_created": "2011-04-07 09:17:14", |
|
26 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
27 |
"created": "2011-02-06", |
|
28 |
"updated": "2011-02-06" |
|
29 |
} |
|
30 |
}, |
|
31 |
{ |
|
32 |
"model": "im.User", |
|
33 |
"pk": 3, |
|
34 |
"fields": { |
|
35 |
"uniq": "chazapis", |
|
36 |
"level": 1, |
|
37 |
"invitations": 3, |
|
38 |
"auth_token": "0002", |
|
39 |
"auth_token_created": "2011-04-07 09:17:14", |
|
40 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
41 |
"created": "2011-02-06", |
|
42 |
"updated": "2011-02-06" |
|
43 |
} |
|
44 |
}, |
|
45 |
{ |
|
46 |
"model": "im.User", |
|
47 |
"pk": 4, |
|
48 |
"fields": { |
|
49 |
"uniq": "gtsouk", |
|
50 |
"level": 1, |
|
51 |
"invitations": 3, |
|
52 |
"auth_token": "0003", |
|
53 |
"auth_token_created": "2011-04-07 09:17:14", |
|
54 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
55 |
"created": "2011-02-06", |
|
56 |
"updated": "2011-02-06" |
|
57 |
} |
|
58 |
}, |
|
59 |
{ |
|
60 |
"model": "im.User", |
|
61 |
"pk": 5, |
|
62 |
"fields": { |
|
63 |
"uniq": "papagian", |
|
64 |
"level": 1, |
|
65 |
"invitations": 3, |
|
66 |
"auth_token": "0004", |
|
67 |
"auth_token_created": "2011-04-07 09:17:14", |
|
68 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
69 |
"created": "2011-02-06", |
|
70 |
"updated": "2011-02-06" |
|
71 |
} |
|
72 |
}, |
|
73 |
{ |
|
74 |
"model": "im.User", |
|
75 |
"pk": 6, |
|
76 |
"fields": { |
|
77 |
"uniq": "louridas", |
|
78 |
"level": 1, |
|
79 |
"invitations": 3, |
|
80 |
"auth_token": "0005", |
|
81 |
"auth_token_created": "2011-04-07 09:17:14", |
|
82 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
83 |
"created": "2011-02-06", |
|
84 |
"updated": "2011-02-06" |
|
85 |
} |
|
86 |
}, |
|
87 |
{ |
|
88 |
"model": "im.User", |
|
89 |
"pk": 7, |
|
90 |
"fields": { |
|
91 |
"uniq": "chstath", |
|
92 |
"level": 1, |
|
93 |
"invitations": 3, |
|
94 |
"auth_token": "0006", |
|
95 |
"auth_token_created": "2011-04-07 09:17:14", |
|
96 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
97 |
"created": "2011-02-06", |
|
98 |
"updated": "2011-02-06" |
|
99 |
} |
|
100 |
}, |
|
101 |
{ |
|
102 |
"model": "im.User", |
|
103 |
"pk": 8, |
|
104 |
"fields": { |
|
105 |
"uniq": "pkanavos", |
|
106 |
"level": 1, |
|
107 |
"invitations": 3, |
|
108 |
"auth_token": "0007", |
|
109 |
"auth_token_created": "2011-04-07 09:17:14", |
|
110 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
111 |
"created": "2011-02-06", |
|
112 |
"updated": "2011-02-06" |
|
113 |
} |
|
114 |
}, |
|
115 |
{ |
|
116 |
"model": "im.User", |
|
117 |
"pk": 9, |
|
118 |
"fields": { |
|
119 |
"uniq": "mvasilak", |
|
120 |
"level": 1, |
|
121 |
"invitations": 3, |
|
122 |
"auth_token": "0008", |
|
123 |
"auth_token_created": "2011-04-07 09:17:14", |
|
124 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
125 |
"created": "2011-02-06", |
|
126 |
"updated": "2011-02-06" |
|
127 |
} |
|
128 |
}, |
|
129 |
{ |
|
130 |
"model": "im.User", |
|
131 |
"pk": 10, |
|
132 |
"fields": { |
|
133 |
"uniq": "διογÎνης", |
|
134 |
"level": 2, |
|
135 |
"invitations": 2, |
|
136 |
"auth_token": "0009", |
|
137 |
"auth_token_created": "2011-04-07 09:17:14", |
|
138 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
139 |
"created": "2011-02-06", |
|
140 |
"updated": "2011-02-06" |
|
141 |
} |
|
142 |
} |
|
143 |
] |
/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 |
from django.conf import settings |
|
37 |
|
|
38 |
from pithos.im.models import User |
|
39 |
|
|
40 |
openid_providers = ( |
|
41 |
('Google','https://www.google.com/accounts/o8/id'), |
|
42 |
('Yahoo', 'http://yahoo.com/'), |
|
43 |
('AOL','http://openid.aol.com/%s/'), |
|
44 |
('OpenID', None), |
|
45 |
('MyOpenID','http://%s.myopenid.com/'), |
|
46 |
('LiveJournal', 'http://%s.livejournal.com/'), |
|
47 |
('Flickr', 'http://flickr.com/%s/'), |
|
48 |
('Technorati', 'http://technorati.com/people/technorati/%s/'), |
|
49 |
('Wordpress', 'http://%s.wordpress.com/'), |
|
50 |
('Blogger', 'http://%s.blogspot.com/'), |
|
51 |
('Verisign', 'http://%s.pip.verisignlabs.com/'), |
|
52 |
('Vidoop', 'http://%s.myvidoop.com/'), |
|
53 |
('ClaimID','http://claimid.com/%s') |
|
54 |
) |
|
55 |
|
|
56 |
class RegisterForm(forms.Form): |
|
57 |
uniq = forms.CharField(widget=forms.widgets.TextInput()) |
|
58 |
provider = forms.CharField(widget=forms.TextInput(), |
|
59 |
label=u'Identity Provider') |
|
60 |
email = forms.EmailField(widget=forms.TextInput(), |
|
61 |
label=_('Email address')) |
|
62 |
realname = forms.CharField(widget=forms.TextInput(), |
|
63 |
label=u'Real Name') |
|
64 |
|
|
65 |
def __init__(self, *args, **kwargs): |
|
66 |
super(forms.Form, self).__init__(*args, **kwargs) |
|
67 |
|
|
68 |
#set readonly form fields |
|
69 |
self.fields['provider'].widget.attrs['readonly'] = True |
|
70 |
|
|
71 |
def clean_uniq(self): |
|
72 |
""" |
|
73 |
Validate that the uniq is alphanumeric and is not already |
|
74 |
in use. |
|
75 |
|
|
76 |
""" |
|
77 |
try: |
|
78 |
user = User.objects.get(uniq__iexact=self.cleaned_data['uniq']) |
|
79 |
except User.DoesNotExist: |
|
80 |
return self.cleaned_data['uniq'] |
|
81 |
raise forms.ValidationError(_("A user with that uniq already exists.")) |
|
82 |
|
|
83 |
class ShibbolethRegisterForm(RegisterForm): |
|
84 |
pass |
|
85 |
|
|
86 |
class TwitterRegisterForm(RegisterForm): |
|
87 |
pass |
|
88 |
|
|
89 |
class OpenidRegisterForm(RegisterForm): |
|
90 |
openidurl = forms.ChoiceField(widget=forms.Select, |
|
91 |
choices=((url, l) for l, url in openid_providers)) |
|
92 |
|
|
93 |
class LocalRegisterForm(RegisterForm): |
|
94 |
""" local signup form""" |
|
95 |
password = forms.CharField(widget=forms.PasswordInput(render_value=False), |
|
96 |
label=_('Password')) |
|
97 |
password2 = forms.CharField(widget=forms.PasswordInput(render_value=False), |
|
98 |
label=_('Confirm Password')) |
|
99 |
|
|
100 |
def __init__(self, *args, **kwargs): |
|
101 |
super(LocalRegisterForm, self).__init__(*args, **kwargs) |
|
102 |
|
|
103 |
def clean_uniq(self): |
|
104 |
""" |
|
105 |
Validate that the uniq is alphanumeric and is not already |
|
106 |
in use. |
|
107 |
|
|
108 |
""" |
|
109 |
try: |
|
110 |
user = User.objects.get(uniq__iexact=self.cleaned_data['uniq']) |
|
111 |
except User.DoesNotExist: |
|
112 |
return self.cleaned_data['uniq'] |
|
113 |
raise forms.ValidationError(_("A user with that uniq already exists.")) |
|
114 |
|
|
115 |
def clean(self): |
|
116 |
""" |
|
117 |
Verifiy that the values entered into the two password fields |
|
118 |
match. Note that an error here will end up in |
|
119 |
``non_field_errors()`` because it doesn't apply to a single |
|
120 |
field. |
|
121 |
|
|
122 |
""" |
|
123 |
if 'password' in self.cleaned_data and 'password2' in self.cleaned_data: |
|
124 |
if self.cleaned_data['password'] != self.cleaned_data['password2']: |
|
125 |
raise forms.ValidationError(_("The two password fields didn't match.")) |
|
126 |
return self.cleaned_data |
|
127 |
|
|
128 |
class InvitedRegisterForm(RegisterForm): |
|
129 |
inviter = forms.CharField(widget=forms.TextInput(), |
|
130 |
label=_('Inviter Real Name')) |
|
131 |
|
|
132 |
def __init__(self, *args, **kwargs): |
|
133 |
super(RegisterForm, self).__init__(*args, **kwargs) |
|
134 |
|
|
135 |
#set readonly form fields |
|
136 |
self.fields['uniq'].widget.attrs['readonly'] = True |
|
137 |
self.fields['inviter'].widget.attrs['readonly'] = True |
|
138 |
self.fields['provider'].widget.attrs['provider'] = True |
|
139 |
|
|
140 |
class InvitedLocalRegisterForm(LocalRegisterForm, InvitedRegisterForm): |
|
141 |
pass |
|
142 |
|
|
143 |
class InvitedOpenidRegisterForm(OpenidRegisterForm, InvitedRegisterForm): |
|
144 |
pass |
|
145 |
|
|
146 |
class InvitedTwitterRegisterForm(TwitterRegisterForm, InvitedRegisterForm): |
|
147 |
pass |
|
148 |
|
|
149 |
class InvitedShibbolethRegisterForm(ShibbolethRegisterForm, InvitedRegisterForm): |
|
150 |
pass |
/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 pithos.backends import connect_backend |
|
35 |
|
|
36 |
def get_quota(user): |
|
37 |
backend = connect_backend() |
|
38 |
quota = backend.get_account_policy(user, user)['quota'] |
|
39 |
backend.close() |
|
40 |
return quota |
|
41 |
|
|
42 |
def set_quota(user, quota): |
|
43 |
backend = connect_backend() |
|
44 |
backend.update_account_policy(user, user, {'quota': quota}) |
|
45 |
backend.close() |
|
46 |
return quota |
/dev/null | ||
---|---|---|
1 |
# encoding: utf-8 |
|
2 |
import datetime |
|
3 |
from south.db import db |
|
4 |
from south.v2 import SchemaMigration |
|
5 |
from django.db import models |
|
6 |
|
|
7 |
class Migration(SchemaMigration): |
|
8 |
|
|
9 |
def forwards(self, orm): |
|
10 |
|
|
11 |
# Adding model 'User' |
|
12 |
db.create_table('im_user', ( |
|
13 |
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
|
14 |
('uniq', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)), |
|
15 |
('realname', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), |
|
16 |
('email', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), |
|
17 |
('affiliation', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), |
|
18 |
('state', self.gf('django.db.models.fields.CharField')(default='ACTIVE', max_length=16)), |
|
19 |
('level', self.gf('django.db.models.fields.IntegerField')(default=4)), |
|
20 |
('invitations', self.gf('django.db.models.fields.IntegerField')(default=0)), |
|
21 |
('password', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), |
|
22 |
('is_admin', self.gf('django.db.models.fields.BooleanField')(default=False)), |
|
23 |
('auth_token', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), |
|
24 |
('auth_token_created', self.gf('django.db.models.fields.DateTimeField')(null=True)), |
|
25 |
('auth_token_expires', self.gf('django.db.models.fields.DateTimeField')(null=True)), |
|
26 |
('created', self.gf('django.db.models.fields.DateTimeField')()), |
|
27 |
('updated', self.gf('django.db.models.fields.DateTimeField')()), |
|
28 |
)) |
|
29 |
db.send_create_signal('im', ['User']) |
|
30 |
|
|
31 |
# Adding model 'Invitation' |
|
32 |
db.create_table('im_invitation', ( |
|
33 |
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
|
34 |
('inviter', self.gf('django.db.models.fields.related.ForeignKey')(related_name='invitations_sent', null=True, to=orm['im.User'])), |
|
35 |
('realname', self.gf('django.db.models.fields.CharField')(max_length=255)), |
|
36 |
('uniq', self.gf('django.db.models.fields.CharField')(max_length=255)), |
|
37 |
('code', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)), |
|
38 |
('is_accepted', self.gf('django.db.models.fields.BooleanField')(default=False)), |
|
39 |
('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), |
|
40 |
('accepted', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), |
|
41 |
)) |
|
42 |
db.send_create_signal('im', ['Invitation']) |
|
43 |
|
|
44 |
|
|
45 |
def backwards(self, orm): |
|
46 |
|
|
47 |
# Deleting model 'User' |
|
48 |
db.delete_table('im_user') |
|
49 |
|
|
50 |
# Deleting model 'Invitation' |
|
51 |
db.delete_table('im_invitation') |
|
52 |
|
|
53 |
|
|
54 |
models = { |
|
55 |
'im.invitation': { |
|
56 |
'Meta': {'object_name': 'Invitation'}, |
|
57 |
'accepted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
|
58 |
'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), |
|
59 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
60 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
61 |
'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.User']"}), |
|
62 |
'is_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
63 |
'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
|
64 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
|
65 |
}, |
|
66 |
'im.user': { |
|
67 |
'Meta': {'object_name': 'User'}, |
|
68 |
'affiliation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
69 |
'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
|
70 |
'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
71 |
'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
72 |
'created': ('django.db.models.fields.DateTimeField', [], {}), |
|
73 |
'email': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
74 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
75 |
'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
76 |
'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
77 |
'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}), |
|
78 |
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
79 |
'realname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
80 |
'state': ('django.db.models.fields.CharField', [], {'default': "'ACTIVE'", 'max_length': '16'}), |
|
81 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), |
|
82 |
'updated': ('django.db.models.fields.DateTimeField', [], {}) |
|
83 |
} |
|
84 |
} |
|
85 |
|
|
86 |
complete_apps = ['im'] |
/dev/null | ||
---|---|---|
1 |
# encoding: utf-8 |
|
2 |
import datetime |
|
3 |
from south.db import db |
|
4 |
from south.v2 import SchemaMigration |
|
5 |
from django.db import models |
|
6 |
|
|
7 |
class Migration(SchemaMigration): |
|
8 |
|
|
9 |
def forwards(self, orm): |
|
10 |
|
|
11 |
# Adding field 'User.is_verified' |
|
12 |
db.add_column('im_user', 'is_verified', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False) |
|
13 |
|
|
14 |
|
|
15 |
def backwards(self, orm): |
|
16 |
|
|
17 |
# Deleting field 'User.is_verified' |
|
18 |
db.delete_column('im_user', 'is_verified') |
|
19 |
|
|
20 |
|
|
21 |
models = { |
|
22 |
'im.invitation': { |
|
23 |
'Meta': {'object_name': 'Invitation'}, |
|
24 |
'accepted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
|
25 |
'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), |
|
26 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
27 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
28 |
'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.User']"}), |
|
29 |
'is_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
30 |
'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
|
31 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
|
32 |
}, |
|
33 |
'im.user': { |
|
34 |
'Meta': {'object_name': 'User'}, |
|
35 |
'affiliation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
36 |
'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
|
37 |
'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
38 |
'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
39 |
'created': ('django.db.models.fields.DateTimeField', [], {}), |
|
40 |
'email': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
41 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
42 |
'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
43 |
'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
44 |
'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
45 |
'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}), |
|
46 |
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
47 |
'realname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
48 |
'state': ('django.db.models.fields.CharField', [], {'default': "'ACTIVE'", 'max_length': '16'}), |
|
49 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), |
|
50 |
'updated': ('django.db.models.fields.DateTimeField', [], {}) |
|
51 |
} |
|
52 |
} |
|
53 |
|
|
54 |
complete_apps = ['im'] |
/dev/null | ||
---|---|---|
1 |
# encoding: utf-8 |
|
2 |
import datetime |
|
3 |
from south.db import db |
|
4 |
from south.v2 import SchemaMigration |
|
5 |
from django.db import models |
|
6 |
|
|
7 |
class Migration(SchemaMigration): |
|
8 |
|
|
9 |
def forwards(self, orm): |
|
10 |
|
|
11 |
# Adding field 'User.provider' |
|
12 |
db.add_column('im_user', 'provider', self.gf('django.db.models.fields.CharField')(default='', max_length=255), keep_default=False) |
|
13 |
|
|
14 |
# Adding field 'User.openidurl' |
|
15 |
db.add_column('im_user', 'openidurl', self.gf('django.db.models.fields.CharField')(default='', max_length=255), keep_default=False) |
|
16 |
|
|
17 |
# Adding field 'Invitation.is_consumed' |
|
18 |
db.add_column('im_invitation', 'is_consumed', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False) |
|
19 |
|
|
20 |
# Adding field 'Invitation.consumed' |
|
21 |
db.add_column('im_invitation', 'consumed', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), keep_default=False) |
|
22 |
|
|
23 |
|
|
24 |
def backwards(self, orm): |
|
25 |
|
|
26 |
# Deleting field 'User.provider' |
|
27 |
db.delete_column('im_user', 'provider') |
|
28 |
|
|
29 |
# Deleting field 'User.openidurl' |
|
30 |
db.delete_column('im_user', 'openidurl') |
|
31 |
|
|
32 |
# Deleting field 'Invitation.is_consumed' |
|
33 |
db.delete_column('im_invitation', 'is_consumed') |
|
34 |
|
|
35 |
# Deleting field 'Invitation.consumed' |
|
36 |
db.delete_column('im_invitation', 'consumed') |
|
37 |
|
|
38 |
|
|
39 |
models = { |
|
40 |
'im.invitation': { |
|
41 |
'Meta': {'object_name': 'Invitation'}, |
|
42 |
'accepted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
|
43 |
'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), |
|
44 |
'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
|
45 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
46 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
47 |
'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.User']"}), |
|
48 |
'is_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
49 |
'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
50 |
'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
|
51 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
|
52 |
}, |
|
53 |
'im.user': { |
|
54 |
'Meta': {'object_name': 'User'}, |
|
55 |
'affiliation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
56 |
'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
|
57 |
'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
58 |
'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
59 |
'created': ('django.db.models.fields.DateTimeField', [], {}), |
|
60 |
'email': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
61 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
62 |
'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
63 |
'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
64 |
'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
65 |
'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}), |
|
66 |
'openidurl': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
67 |
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
68 |
'provider': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
69 |
'realname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
70 |
'state': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '16'}), |
|
71 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), |
|
72 |
'updated': ('django.db.models.fields.DateTimeField', [], {}) |
|
73 |
} |
|
74 |
} |
|
75 |
|
|
76 |
complete_apps = ['im'] |
/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 |
import hashlib |
|
36 |
|
|
37 |
from time import asctime |
|
38 |
from datetime import datetime, timedelta |
|
39 |
from base64 import b64encode |
|
40 |
|
|
41 |
from django.conf import settings |
|
42 |
from django.db import models |
|
43 |
|
|
44 |
from pithos.im.interface import get_quota, set_quota |
|
45 |
|
|
46 |
from hashlib import new as newhasher |
|
47 |
|
|
48 |
class User(models.Model): |
|
49 |
ACCOUNT_STATE = ( |
|
50 |
('ACTIVE', 'Active'), |
|
51 |
('DELETED', 'Deleted'), |
|
52 |
('SUSPENDED', 'Suspended'), |
|
53 |
('UNVERIFIED', 'Unverified'), |
|
54 |
('PENDING', 'Pending') |
|
55 |
) |
|
56 |
|
|
57 |
uniq = models.CharField('Unique ID', max_length=255, null=True) |
|
58 |
|
|
59 |
realname = models.CharField('Real Name', max_length=255, default='') |
|
60 |
email = models.CharField('Email', max_length=255, default='') |
|
61 |
affiliation = models.CharField('Affiliation', max_length=255, default='') |
|
62 |
provider = models.CharField('Provider', max_length=255, default='') |
|
63 |
state = models.CharField('Account state', choices=ACCOUNT_STATE, |
|
64 |
max_length=16, default='PENDING') |
|
65 |
|
|
66 |
#for invitations |
|
67 |
level = models.IntegerField('Inviter level', default=4) |
|
68 |
invitations = models.IntegerField('Invitations left', default=0) |
|
69 |
|
|
70 |
#for local |
|
71 |
password = models.CharField('Password', max_length=255, default='') |
|
72 |
|
|
73 |
is_admin = models.BooleanField('Admin?', default=False) |
|
74 |
|
|
75 |
auth_token = models.CharField('Authentication Token', max_length=32, |
|
76 |
null=True, blank=True) |
|
77 |
auth_token_created = models.DateTimeField('Token creation date', |
|
78 |
null=True) |
|
79 |
auth_token_expires = models.DateTimeField('Token expiration date', |
|
80 |
null=True) |
|
81 |
|
|
82 |
created = models.DateTimeField('Creation date') |
|
83 |
updated = models.DateTimeField('Update date') |
|
84 |
|
|
85 |
is_verified = models.BooleanField('Verified?', default=False) |
|
86 |
|
|
87 |
openidurl = models.CharField('OpenID url', max_length=255, default='') |
|
88 |
|
|
89 |
@property |
|
90 |
def quota(self): |
|
91 |
return get_quota(self.uniq) |
|
92 |
|
|
93 |
@quota.setter |
|
94 |
def quota(self, value): |
|
95 |
set_quota(self.uniq, value) |
|
96 |
|
|
97 |
@property |
|
98 |
def invitation(self): |
|
99 |
try: |
|
100 |
return Invitation.objects.get(uniq=self.uniq) |
|
101 |
except Invitation.DoesNotExist: |
|
102 |
return None |
|
103 |
|
|
104 |
def save(self, update_timestamps=True, **kwargs): |
|
105 |
if update_timestamps: |
|
106 |
if not self.id: |
|
107 |
self.created = datetime.now() |
|
108 |
self.updated = datetime.now() |
|
109 |
|
|
110 |
super(User, self).save(**kwargs) |
|
111 |
|
|
112 |
#invitation consume |
|
113 |
if self.invitation and not self.invitation.is_consumed: |
|
114 |
self.invitation.consume() |
|
115 |
|
|
116 |
def renew_token(self): |
|
117 |
md5 = hashlib.md5() |
|
118 |
md5.update(self.uniq) |
|
119 |
md5.update(self.realname.encode('ascii', 'ignore')) |
|
120 |
md5.update(asctime()) |
|
121 |
|
|
122 |
self.auth_token = b64encode(md5.digest()) |
|
123 |
self.auth_token_created = datetime.now() |
|
124 |
self.auth_token_expires = self.auth_token_created + \ |
|
125 |
timedelta(hours=settings.AUTH_TOKEN_DURATION) |
|
126 |
|
|
127 |
def __unicode__(self): |
|
128 |
return self.uniq |
|
129 |
|
|
130 |
class Invitation(models.Model): |
|
131 |
inviter = models.ForeignKey(User, related_name='invitations_sent', |
|
132 |
null=True) |
|
133 |
realname = models.CharField('Real name', max_length=255) |
|
134 |
uniq = models.CharField('Unique ID', max_length=255) |
|
135 |
code = models.BigIntegerField('Invitation code', db_index=True) |
|
136 |
#obsolete: we keep it just for transfering the data |
|
137 |
is_accepted = models.BooleanField('Accepted?', default=False) |
|
138 |
is_consumed = models.BooleanField('Consumed?', default=False) |
|
139 |
created = models.DateTimeField('Creation date', auto_now_add=True) |
|
140 |
#obsolete: we keep it just for transfering the data |
|
141 |
accepted = models.DateTimeField('Acceptance date', null=True, blank=True) |
|
142 |
consumed = models.DateTimeField('Consumption date', null=True, blank=True) |
|
143 |
|
|
144 |
def consume(self): |
|
145 |
self.is_consumed = True |
|
146 |
self.consumed = datetime.now() |
|
147 |
self.save() |
|
148 |
|
|
149 |
def __unicode__(self): |
|
150 |
return '%s -> %s [%d]' % (self.inviter, self.uniq, self.code) |
/dev/null | ||
---|---|---|
1 |
/*! |
|
2 |
* Bootstrap v1.3.0 |
|
3 |
* |
|
4 |
* Copyright 2011 Twitter, Inc |
|
5 |
* Licensed under the Apache License v2.0 |
|
6 |
* http://www.apache.org/licenses/LICENSE-2.0 |
|
7 |
* |
|
8 |
* Designed and built with all the love in the world @twitter by @mdo and @fat. |
|
9 |
* Date: Thu Sep 22 12:52:42 PDT 2011 |
|
10 |
*/ |
|
11 |
/* Reset.less |
|
12 |
* Props to Eric Meyer (meyerweb.com) for his CSS reset file. We're using an adapted version here that cuts out some of the reset HTML elements we will never need here (i.e., dfn, samp, etc). |
|
13 |
* ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ |
|
14 |
html, body { |
|
15 |
margin: 0; |
|
16 |
padding: 0; |
|
17 |
} |
|
18 |
h1, |
|
19 |
h2, |
|
20 |
h3, |
|
21 |
h4, |
|
22 |
h5, |
|
23 |
h6, |
|
24 |
p, |
|
25 |
blockquote, |
|
26 |
pre, |
|
27 |
a, |
|
28 |
abbr, |
|
29 |
acronym, |
|
30 |
address, |
|
31 |
cite, |
|
32 |
code, |
|
33 |
del, |
|
34 |
dfn, |
|
35 |
em, |
|
36 |
img, |
|
37 |
q, |
|
38 |
s, |
|
39 |
samp, |
|
40 |
small, |
|
41 |
strike, |
|
42 |
strong, |
|
43 |
sub, |
|
44 |
sup, |
|
45 |
tt, |
|
46 |
var, |
|
47 |
dd, |
|
48 |
dl, |
|
49 |
dt, |
|
50 |
li, |
|
51 |
ol, |
|
52 |
ul, |
|
53 |
fieldset, |
|
54 |
form, |
|
55 |
label, |
|
56 |
legend, |
|
57 |
button, |
|
58 |
table, |
|
59 |
caption, |
|
60 |
tbody, |
|
61 |
tfoot, |
|
62 |
thead, |
|
63 |
tr, |
|
64 |
th, |
|
65 |
td { |
|
66 |
margin: 0; |
|
67 |
padding: 0; |
|
68 |
border: 0; |
|
69 |
font-weight: normal; |
|
70 |
font-style: normal; |
|
71 |
font-size: 100%; |
|
72 |
line-height: 1; |
|
73 |
font-family: inherit; |
|
74 |
} |
|
75 |
table { |
|
76 |
border-collapse: collapse; |
|
77 |
border-spacing: 0; |
|
78 |
} |
|
79 |
ol, ul { |
|
80 |
list-style: none; |
|
81 |
} |
|
82 |
q:before, |
|
83 |
q:after, |
|
84 |
blockquote:before, |
|
85 |
blockquote:after { |
|
86 |
content: ""; |
|
87 |
} |
|
88 |
html { |
|
89 |
overflow-y: scroll; |
|
90 |
font-size: 100%; |
|
91 |
-webkit-text-size-adjust: 100%; |
|
92 |
-ms-text-size-adjust: 100%; |
|
93 |
} |
|
94 |
a:focus { |
|
95 |
outline: thin dotted; |
|
96 |
} |
|
97 |
a:hover, a:active { |
|
98 |
outline: 0; |
|
99 |
} |
|
100 |
article, |
|
101 |
aside, |
|
102 |
details, |
|
103 |
figcaption, |
|
104 |
figure, |
|
105 |
footer, |
|
106 |
header, |
|
107 |
hgroup, |
|
108 |
nav, |
|
109 |
section { |
|
110 |
display: block; |
|
111 |
} |
|
112 |
audio, canvas, video { |
|
113 |
display: inline-block; |
|
114 |
*display: inline; |
|
115 |
*zoom: 1; |
|
116 |
} |
|
117 |
audio:not([controls]) { |
|
118 |
display: none; |
|
119 |
} |
|
120 |
sub, sup { |
|
121 |
font-size: 75%; |
|
122 |
line-height: 0; |
|
123 |
position: relative; |
|
124 |
vertical-align: baseline; |
|
125 |
} |
|
126 |
sup { |
|
127 |
top: -0.5em; |
|
128 |
} |
|
129 |
sub { |
|
130 |
bottom: -0.25em; |
|
131 |
} |
|
132 |
img { |
|
133 |
border: 0; |
|
134 |
-ms-interpolation-mode: bicubic; |
|
135 |
} |
|
136 |
button, |
|
137 |
input, |
|
138 |
select, |
|
139 |
textarea { |
|
140 |
font-size: 100%; |
|
141 |
margin: 0; |
|
142 |
vertical-align: baseline; |
|
143 |
*vertical-align: middle; |
|
144 |
} |
|
145 |
button, input { |
|
146 |
line-height: normal; |
|
147 |
*overflow: visible; |
|
148 |
} |
|
149 |
button::-moz-focus-inner, input::-moz-focus-inner { |
|
150 |
border: 0; |
|
151 |
padding: 0; |
|
152 |
} |
|
153 |
button, |
|
154 |
input[type="button"], |
|
155 |
input[type="reset"], |
|
156 |
input[type="submit"] { |
|
157 |
cursor: pointer; |
|
158 |
-webkit-appearance: button; |
|
159 |
} |
|
160 |
input[type="search"] { |
|
161 |
-webkit-appearance: textfield; |
|
162 |
-webkit-box-sizing: content-box; |
|
163 |
-moz-box-sizing: content-box; |
|
164 |
box-sizing: content-box; |
|
165 |
} |
|
166 |
input[type="search"]::-webkit-search-decoration { |
|
167 |
-webkit-appearance: none; |
|
168 |
} |
|
169 |
textarea { |
|
170 |
overflow: auto; |
|
171 |
vertical-align: top; |
|
172 |
} |
|
173 |
/* Variables.less |
|
174 |
* Variables to customize the look and feel of Bootstrap |
|
175 |
* ----------------------------------------------------- */ |
|
176 |
/* Variables.less |
|
177 |
* Snippets of reusable CSS to develop faster and keep code readable |
|
178 |
* ----------------------------------------------------------------- */ |
|
179 |
/* |
|
180 |
* Scaffolding |
|
181 |
* Basic and global styles for generating a grid system, structural layout, and page templates |
|
182 |
* ------------------------------------------------------------------------------------------- */ |
|
183 |
html, body { |
|
184 |
background-color: #ffffff; |
|
185 |
} |
|
186 |
body { |
|
187 |
margin: 0; |
|
188 |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; |
|
189 |
font-size: 13px; |
|
190 |
font-weight: normal; |
|
191 |
line-height: 18px; |
|
192 |
color: #404040; |
|
193 |
} |
|
194 |
.container { |
|
195 |
width: 940px; |
|
196 |
margin-left: auto; |
|
197 |
margin-right: auto; |
|
198 |
zoom: 1; |
|
199 |
} |
|
200 |
.container:before, .container:after { |
|
201 |
display: table; |
|
202 |
content: ""; |
|
203 |
zoom: 1; |
|
204 |
*display: inline; |
|
205 |
} |
|
206 |
.container:after { |
|
207 |
clear: both; |
|
208 |
} |
|
209 |
.container-fluid { |
|
210 |
position: relative; |
|
211 |
min-width: 940px; |
|
212 |
padding-left: 20px; |
|
213 |
padding-right: 20px; |
|
214 |
zoom: 1; |
|
215 |
} |
|
216 |
.container-fluid:before, .container-fluid:after { |
|
217 |
display: table; |
|
218 |
content: ""; |
|
219 |
zoom: 1; |
|
220 |
*display: inline; |
|
221 |
} |
|
222 |
.container-fluid:after { |
|
223 |
clear: both; |
|
224 |
} |
|
225 |
.container-fluid > .sidebar { |
|
226 |
float: left; |
|
227 |
width: 220px; |
|
228 |
} |
|
229 |
.container-fluid > .content { |
|
230 |
margin-left: 240px; |
|
231 |
} |
|
232 |
a { |
|
233 |
color: #0069d6; |
|
234 |
text-decoration: none; |
|
235 |
line-height: inherit; |
|
236 |
font-weight: inherit; |
|
237 |
} |
|
238 |
a:hover { |
|
239 |
color: #00438a; |
|
240 |
text-decoration: underline; |
|
241 |
} |
|
242 |
.pull-right { |
|
243 |
float: right; |
|
244 |
} |
|
245 |
.pull-left { |
|
246 |
float: left; |
|
247 |
} |
|
248 |
.hide { |
|
249 |
display: none; |
|
250 |
} |
|
251 |
.show { |
|
252 |
display: block; |
|
253 |
} |
|
254 |
.row { |
|
255 |
zoom: 1; |
|
256 |
margin-left: -20px; |
|
257 |
} |
|
258 |
.row:before, .row:after { |
|
259 |
display: table; |
|
260 |
content: ""; |
|
261 |
zoom: 1; |
|
262 |
*display: inline; |
|
263 |
} |
|
264 |
.row:after { |
|
265 |
clear: both; |
|
266 |
} |
|
267 |
[class*="span"] { |
|
268 |
display: inline; |
|
269 |
float: left; |
|
270 |
margin-left: 20px; |
|
271 |
} |
|
272 |
.span1 { |
|
273 |
width: 40px; |
|
274 |
} |
|
275 |
.span2 { |
|
276 |
width: 100px; |
|
277 |
} |
|
278 |
.span3 { |
|
279 |
width: 160px; |
|
280 |
} |
|
281 |
.span4 { |
|
282 |
width: 220px; |
|
283 |
} |
|
284 |
.span5 { |
|
285 |
width: 280px; |
|
286 |
} |
|
287 |
.span6 { |
|
288 |
width: 340px; |
|
289 |
} |
|
290 |
.span7 { |
|
291 |
width: 400px; |
|
292 |
} |
|
293 |
.span8 { |
|
294 |
width: 460px; |
|
295 |
} |
|
296 |
.span9 { |
|
297 |
width: 520px; |
|
298 |
} |
|
299 |
.span10 { |
|
300 |
width: 580px; |
|
301 |
} |
|
302 |
.span11 { |
|
303 |
width: 640px; |
|
304 |
} |
|
305 |
.span12 { |
|
306 |
width: 700px; |
|
307 |
} |
|
308 |
.span13 { |
|
309 |
width: 760px; |
|
310 |
} |
|
311 |
.span14 { |
|
312 |
width: 820px; |
|
313 |
} |
|
314 |
.span15 { |
|
315 |
width: 880px; |
|
316 |
} |
|
317 |
.span16 { |
|
318 |
width: 940px; |
|
319 |
} |
|
320 |
.span17 { |
|
321 |
width: 1000px; |
|
322 |
} |
|
323 |
.span18 { |
|
324 |
width: 1060px; |
|
325 |
} |
|
326 |
.span19 { |
|
327 |
width: 1120px; |
|
328 |
} |
|
329 |
.span20 { |
|
330 |
width: 1180px; |
|
331 |
} |
|
332 |
.span21 { |
|
333 |
width: 1240px; |
|
334 |
} |
|
335 |
.span22 { |
|
336 |
width: 1300px; |
|
337 |
} |
|
338 |
.span23 { |
|
339 |
width: 1360px; |
|
340 |
} |
|
341 |
.span24 { |
|
342 |
width: 1420px; |
|
343 |
} |
|
344 |
.offset1 { |
|
345 |
margin-left: 80px; |
|
346 |
} |
|
347 |
.offset2 { |
|
348 |
margin-left: 140px; |
|
349 |
} |
|
350 |
.offset3 { |
|
351 |
margin-left: 200px; |
|
352 |
} |
|
353 |
.offset4 { |
|
354 |
margin-left: 260px; |
|
355 |
} |
|
356 |
.offset5 { |
|
357 |
margin-left: 320px; |
|
358 |
} |
|
359 |
.offset6 { |
|
360 |
margin-left: 380px; |
|
361 |
} |
|
362 |
.offset7 { |
|
363 |
margin-left: 440px; |
|
364 |
} |
|
365 |
.offset8 { |
|
366 |
margin-left: 500px; |
|
367 |
} |
|
368 |
.offset9 { |
|
369 |
margin-left: 560px; |
|
370 |
} |
|
371 |
.offset10 { |
|
372 |
margin-left: 620px; |
|
373 |
} |
|
374 |
.offset11 { |
|
375 |
margin-left: 680px; |
|
376 |
} |
|
377 |
.offset12 { |
|
378 |
margin-left: 740px; |
|
379 |
} |
|
380 |
.span-one-third { |
|
381 |
width: 300px; |
|
382 |
} |
|
383 |
.span-two-thirds { |
|
384 |
width: 620px; |
|
385 |
} |
|
386 |
.offset-one-third { |
|
387 |
margin-left: 340px; |
|
388 |
} |
|
389 |
.offset-two-thirds { |
|
390 |
margin-left: 660px; |
|
391 |
} |
|
392 |
/* Typography.less |
|
393 |
* Headings, body text, lists, code, and more for a versatile and durable typography system |
|
394 |
* ---------------------------------------------------------------------------------------- */ |
|
395 |
p { |
|
396 |
font-size: 13px; |
|
397 |
font-weight: normal; |
|
398 |
line-height: 18px; |
|
399 |
margin-bottom: 9px; |
|
400 |
} |
|
401 |
p small { |
|
402 |
font-size: 11px; |
|
403 |
color: #bfbfbf; |
|
404 |
} |
|
405 |
h1, |
|
406 |
h2, |
|
407 |
h3, |
|
408 |
h4, |
|
409 |
h5, |
|
410 |
h6 { |
|
411 |
font-weight: bold; |
|
412 |
color: #404040; |
|
413 |
} |
|
414 |
h1 small, |
|
415 |
h2 small, |
|
416 |
h3 small, |
|
417 |
h4 small, |
|
418 |
h5 small, |
|
419 |
h6 small { |
|
420 |
color: #bfbfbf; |
|
421 |
} |
|
422 |
h1 { |
|
423 |
margin-bottom: 18px; |
|
424 |
font-size: 30px; |
|
425 |
line-height: 36px; |
|
426 |
} |
|
427 |
h1 small { |
|
428 |
font-size: 18px; |
|
429 |
} |
|
430 |
h2 { |
|
431 |
font-size: 24px; |
|
432 |
line-height: 36px; |
|
433 |
} |
|
434 |
h2 small { |
|
435 |
font-size: 14px; |
|
436 |
} |
|
437 |
h3, |
|
438 |
h4, |
|
439 |
h5, |
|
440 |
h6 { |
|
441 |
line-height: 36px; |
|
442 |
} |
|
443 |
h3 { |
|
444 |
font-size: 18px; |
|
445 |
} |
|
446 |
h3 small { |
|
447 |
font-size: 14px; |
|
448 |
} |
Also available in: Unified diff