Revision 794852f2
b/README | ||
---|---|---|
29 | 29 |
------------------ |
30 | 30 |
|
31 | 31 |
Make sure you have all required packages installed: |
32 |
apt-get install python-django python-setuptools python-sphinx python-httplib2 |
|
32 |
apt-get install python-django python-django-south python-setuptools python-sphinx python-httplib2
|
|
33 | 33 |
|
34 | 34 |
Then run: |
35 | 35 |
python manage.py syncdb |
b/README.upgrade | ||
---|---|---|
1 | 1 |
UPGRADE |
2 |
======= |
|
3 |
|
|
4 |
0.7.9 -> 0.7.10 |
|
5 |
--------------- |
|
6 |
* Update settings.py (BACKEND_*, SERVICE_NAME, *_EMAIL, *_TARGET, IM_*) |
|
7 |
* Update 'attributes' table in mysql (backend): |
|
8 |
|
|
9 |
mysql> update attributes set `key`='ETag' where `key`='hash'; |
|
10 |
|
|
11 |
* Upgrade 'im_user' table (im app): |
|
12 |
|
|
13 |
ALTER TABLE im_user ADD COLUMN 'password' VARCHAR(255); |
|
14 |
|
|
15 |
0.7.10 -> 0.8.0 |
|
16 |
--------------- |
|
17 |
* Upgrade 'public' table in mysql (backend): |
|
18 |
* Run: mysqldump pithosdb public > public-table.sql |
|
19 |
* mysql> drop table public; |
|
20 |
* Update the codebase and run the server so the new public table is created |
|
21 |
* From the sql dump above, take the row: |
|
22 |
|
|
23 |
INSERT INTO `public` VALUES (...); |
|
24 |
|
|
25 |
Rewrite as: |
|
26 |
|
|
27 |
INSERT INTO `public`(`path`) VALUES (...); |
|
28 |
|
|
29 |
And execute in the database |
|
30 |
* Create settings.local with local setting overrides |
|
31 |
* Install python-django-south |
|
32 |
* Setup south: |
|
33 |
python manage.py syncdb |
|
34 |
python manage.py migrate im 0001 --fake |
|
35 |
python manage.py migrate im |
|
36 |
|
|
37 |
0.8.0 -> 0.8.1 |
|
38 |
-------------- |
|
39 |
* Reset 'policy' table in mysql (backend): |
|
40 |
|
|
41 |
mysql> update policy set `value`='auto' where `key`='versioning'; |
|
42 |
|
|
43 |
0.8.1 -> 0.8.2 |
|
44 |
-------------- |
|
45 |
* Add the 'X-Forwarded-Protocol' header directive in the apache configuration, as described in the admin guide |
|
46 |
* Update 'attributes' table in mysql (backend): |
|
47 |
|
|
48 |
mysql> CREATE TABLE `attributes_new` ( |
|
49 |
`serial` int(11) NOT NULL, |
|
50 |
`domain` varchar(255) COLLATE utf8_bin NOT NULL, |
|
51 |
`key` varchar(255) COLLATE utf8_bin NOT NULL, |
|
52 |
`value` varchar(255) COLLATE utf8_bin DEFAULT NULL, |
|
53 |
PRIMARY KEY (`serial`,`domain`,`key`), |
|
54 |
CONSTRAINT FOREIGN KEY (`serial`) REFERENCES `versions` (`serial`) ON DELETE CASCADE ON UPDATE CASCADE |
|
55 |
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; |
|
56 |
mysql> insert into attributes_new select `serial`, 'pithos', `key`, `value` from attributes; |
|
57 |
mysql> drop table attributes; |
|
58 |
mysql> alter table attributes_new rename to attributes; |
|
59 |
|
|
60 |
* Update 'versions' table in mysql (backend): |
|
61 |
|
|
62 |
mysql> create temporary table tmp_uuids as select distinct node, uuid() as `uuid` from versions; |
|
63 |
mysql> alter table versions add column `uuid` varchar(64) DEFAULT '' NOT NULL after `muser`; |
|
64 |
mysql> update versions v, tmp_uuids u set v.`uuid` = u.`uuid` where v.`node` = u.`node`; |
|
65 |
mysql> create index idx_versions_node_uuid on versions(uuid); |
|
2 |
======= |
b/astakos/im/admin/templates/admin_base.html | ||
---|---|---|
14 | 14 |
<li{% ifequal tab "invitations" %} class="active"{% endifequal %}> |
15 | 15 |
<a href="{% url astakos.im.admin.views.invitations_list %}">Invitations</a> |
16 | 16 |
</li> |
17 |
<li{% ifequal tab "logout" %} class="active"{% endifequal %}> |
|
18 |
<a href="{% url django.contrib.auth.views.logout %}">Logout</a> |
|
19 |
</li> |
|
17 | 20 |
</ul> |
18 | 21 |
{% endblock %} |
19 | 22 |
|
b/astakos/im/admin/urls.py | ||
---|---|---|
31 | 31 |
# interpreted as representing official policies, either expressed |
32 | 32 |
# or implied, of GRNET S.A. |
33 | 33 |
|
34 |
from django.conf.urls.defaults import patterns |
|
34 |
from django.conf.urls.defaults import patterns, url
|
|
35 | 35 |
|
36 | 36 |
urlpatterns = patterns('astakos.im.admin.views', |
37 |
(r'^$', 'admin'), |
|
37 |
url(r'^$', 'admin'),
|
|
38 | 38 |
|
39 |
(r'^users/?$', 'users_list'), |
|
40 |
(r'^users/(\d+)/?$', 'users_info'), |
|
41 |
(r'^users/create$', 'users_create'), |
|
42 |
(r'^users/(\d+)/modify/?$', 'users_modify'), |
|
43 |
(r'^users/(\d+)/delete/?$', 'users_delete'), |
|
44 |
(r'^users/export/?$', 'users_export'), |
|
45 |
(r'^users/pending/?$', 'pending_users'), |
|
46 |
(r'^users/activate/(\d+)/?$', 'users_activate'), |
|
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 | 47 |
|
48 |
(r'^invitations/?$', 'invitations_list'), |
|
49 |
(r'^invitations/export/?$', 'invitations_export'), |
|
48 |
url(r'^invitations/?$', 'invitations_list'),
|
|
49 |
url(r'^invitations/export/?$', 'invitations_export'),
|
|
50 | 50 |
) |
b/astakos/im/auth_backends.py | ||
---|---|---|
6 | 6 |
from astakos.im.models import AstakosUser |
7 | 7 |
|
8 | 8 |
class AstakosUserModelCredentialsBackend(ModelBackend): |
9 |
""" |
|
10 |
AuthenticationBackend used to authenticate user creadentials |
|
11 |
""" |
|
9 | 12 |
def authenticate(self, username=None, password=None): |
10 | 13 |
try: |
11 | 14 |
user = AstakosUser.objects.get(username=username) |
... | ... | |
31 | 34 |
# return self._user_class |
32 | 35 |
|
33 | 36 |
class AstakosUserModelTokenBackend(ModelBackend): |
37 |
""" |
|
38 |
AuthenticationBackend used to authenticate using token instead |
|
39 |
""" |
|
34 | 40 |
def authenticate(self, username=None, auth_token=None): |
35 | 41 |
try: |
36 | 42 |
user = AstakosUser.objects.get(username=username) |
b/astakos/im/backends/__init__.py | ||
---|---|---|
86 | 86 |
""" |
87 | 87 |
code = request.GET.get('code', '') |
88 | 88 |
formclass = 'ExtendedUserCreationForm' |
89 |
initial_data = None |
|
89 | 90 |
if request.method == 'GET': |
90 |
initial_data = None |
|
91 | 91 |
if code: |
92 | 92 |
formclass = 'Invited%s' %formclass |
93 | 93 |
self.invitation = Invitation.objects.get(code=code) |
94 | 94 |
if self.invitation.is_consumed: |
95 |
return HttpResponseBadRequest('Invitation has beeen used')
|
|
96 |
initial_data.update({'username':self.invitation.username,
|
|
97 |
'email':self.invitation.username,
|
|
98 |
'realname':self.invitation.realname})
|
|
95 |
raise Exception('Invitation has beeen used')
|
|
96 |
initial_data = {'username':self.invitation.username,
|
|
97 |
'email':self.invitation.username, |
|
98 |
'realname':self.invitation.realname}
|
|
99 | 99 |
inviter = AstakosUser.objects.get(username=self.invitation.inviter) |
100 | 100 |
initial_data['inviter'] = inviter.realname |
101 | 101 |
else: |
102 | 102 |
initial_data = request.POST |
103 |
self.form = globals()[formclass](initial_data) |
|
104 |
return self.form |
|
103 |
return globals()[formclass](initial_data) |
|
105 | 104 |
|
106 | 105 |
def _is_preaccepted(self, user): |
107 | 106 |
""" |
... | ... | |
125 | 124 |
In any other case the method returns the action status and a message. |
126 | 125 |
""" |
127 | 126 |
kwargs = {} |
128 |
form = self.form
|
|
129 |
user = form.save(commit=False)
|
|
127 |
form = self.get_signup_form(request)
|
|
128 |
user = form.save() |
|
130 | 129 |
|
131 | 130 |
try: |
132 | 131 |
if self._is_preaccepted(user): |
133 | 132 |
user.is_active = True |
133 |
user.save() |
|
134 | 134 |
message = _('Registration completed. You can now login.') |
135 | 135 |
next = request.POST.get('next') |
136 | 136 |
if next: |
... | ... | |
177 | 177 |
* DEFAULT_FROM_EMAIL: from email |
178 | 178 |
""" |
179 | 179 |
kwargs = {} |
180 |
form = self.form |
|
181 |
user = form.save(commit=False) |
|
180 |
form = self.get_signup_form(request) |
|
181 |
user = form.save() |
|
182 |
|
|
182 | 183 |
status = messages.SUCCESS |
183 | 184 |
try: |
184 | 185 |
_send_verification(request, user, email_template_name) |
b/astakos/im/fixtures/site.json | ||
---|---|---|
1 |
[ |
|
2 |
{ |
|
3 |
"model": "sites.site", |
|
4 |
"pk": 1, |
|
5 |
"fields": { |
|
6 |
"domain": "localhost:8000/im", |
|
7 |
"name": "Astakos" |
|
8 |
} |
|
9 |
} |
|
10 |
] |
b/astakos/im/forms.py | ||
---|---|---|
58 | 58 |
""" |
59 | 59 |
Extends the built in UserCreationForm in several ways: |
60 | 60 |
|
61 |
* Adds an email field, which uses the custom UniqueUserEmailField, |
|
62 |
that is, the form does not validate if the email address already exists |
|
63 |
in the User table. |
|
64 |
* The username field is generated based on the email, and isn't visible. |
|
61 |
* Adds an email field, which uses the custom UniqueUserEmailField. |
|
62 |
* The username field isn't visible and it is assigned the email value. |
|
65 | 63 |
* first_name and last_name fields are added. |
66 |
* Data not saved by the default behavior of UserCreationForm is saved. |
|
67 | 64 |
""" |
68 | 65 |
|
69 | 66 |
username = forms.CharField(required = False, max_length = 30) |
... | ... | |
71 | 68 |
first_name = forms.CharField(required = False, max_length = 30) |
72 | 69 |
last_name = forms.CharField(required = False, max_length = 30) |
73 | 70 |
|
71 |
class Meta: |
|
72 |
model = AstakosUser |
|
73 |
fields = ("username",) |
|
74 |
|
|
74 | 75 |
def __init__(self, *args, **kwargs): |
75 | 76 |
""" |
76 | 77 |
Changes the order of fields, and removes the username field. |
77 | 78 |
""" |
78 |
super(UserCreationForm, self).__init__(*args, **kwargs) |
|
79 |
super(ExtendedUserCreationForm, self).__init__(*args, **kwargs)
|
|
79 | 80 |
self.fields.keyOrder = ['email', 'first_name', 'last_name', |
80 | 81 |
'password1', 'password2'] |
81 | 82 |
|
... | ... | |
85 | 86 |
""" |
86 | 87 |
cleaned_data = super(UserCreationForm, self).clean(*args, **kwargs) |
87 | 88 |
if cleaned_data.has_key('email'): |
88 |
#cleaned_data['username'] = self.__generate_username( |
|
89 |
# cleaned_data['email']) |
|
90 | 89 |
cleaned_data['username'] = cleaned_data['email'] |
91 | 90 |
return cleaned_data |
92 | 91 |
|
... | ... | |
96 | 95 |
save behavior is complete. |
97 | 96 |
""" |
98 | 97 |
user = super(UserCreationForm, self).save(commit) |
99 |
if user: |
|
100 |
kwargs = {} |
|
101 |
for field in self.fields: |
|
102 |
if hasattr(AstakosUser(), field): |
|
103 |
kwargs[field] = self.cleaned_data[field] |
|
104 |
user = get_or_create_user(username=self.cleaned_data['email'], **kwargs) |
|
105 | 98 |
return user |
106 | 99 |
|
107 | 100 |
class InvitedExtendedUserCreationForm(ExtendedUserCreationForm): |
... | ... | |
112 | 105 |
""" |
113 | 106 |
inviter = forms.CharField(widget=forms.TextInput(), |
114 | 107 |
label=_('Inviter Real Name')) |
108 |
class Meta: |
|
109 |
model = AstakosUser |
|
110 |
fields = ("username",) |
|
115 | 111 |
|
116 | 112 |
def __init__(self, *args, **kwargs): |
117 |
super(RegisterForm, self).__init__(*args, **kwargs)
|
|
113 |
super(InvitedExtendedUserCreationForm, self).__init__(*args, **kwargs)
|
|
118 | 114 |
|
119 | 115 |
#set readonly form fields |
120 | 116 |
self.fields['inviter'].widget.attrs['readonly'] = True |
117 |
self.fields['email'].widget.attrs['readonly'] = True |
|
118 |
self.fields['username'].widget.attrs['readonly'] = True |
|
121 | 119 |
|
122 | 120 |
class ProfileForm(forms.ModelForm): |
123 | 121 |
""" |
b/astakos/im/migrations/0001_initial.py | ||
---|---|---|
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 'AstakosUser' |
|
12 |
db.create_table('im_astakosuser', ( |
|
13 |
('user_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True, primary_key=True)), |
|
14 |
('affiliation', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), |
|
15 |
('provider', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), |
|
16 |
('level', self.gf('django.db.models.fields.IntegerField')(default=4)), |
|
17 |
('invitations', self.gf('django.db.models.fields.IntegerField')(default=0)), |
|
18 |
('auth_token', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), |
|
19 |
('auth_token_created', self.gf('django.db.models.fields.DateTimeField')(null=True)), |
|
20 |
('auth_token_expires', self.gf('django.db.models.fields.DateTimeField')(null=True)), |
|
21 |
('updated', self.gf('django.db.models.fields.DateTimeField')()), |
|
22 |
('is_verified', self.gf('django.db.models.fields.BooleanField')(default=False)), |
|
23 |
)) |
|
24 |
db.send_create_signal('im', ['AstakosUser']) |
|
25 |
|
|
26 |
# Adding model 'Invitation' |
|
27 |
db.create_table('im_invitation', ( |
|
28 |
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
|
29 |
('inviter', self.gf('django.db.models.fields.related.ForeignKey')(related_name='invitations_sent', null=True, to=orm['im.AstakosUser'])), |
|
30 |
('realname', self.gf('django.db.models.fields.CharField')(max_length=255)), |
|
31 |
('username', self.gf('django.db.models.fields.CharField')(max_length=255)), |
|
32 |
('code', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)), |
|
33 |
('is_accepted', self.gf('django.db.models.fields.BooleanField')(default=False)), |
|
34 |
('is_consumed', self.gf('django.db.models.fields.BooleanField')(default=False)), |
|
35 |
('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), |
|
36 |
('accepted', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), |
|
37 |
('consumed', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), |
|
38 |
)) |
|
39 |
db.send_create_signal('im', ['Invitation']) |
|
40 |
|
|
41 |
|
|
42 |
def backwards(self, orm): |
|
43 |
|
|
44 |
# Deleting model 'AstakosUser' |
|
45 |
db.delete_table('im_astakosuser') |
|
46 |
|
|
47 |
# Deleting model 'Invitation' |
|
48 |
db.delete_table('im_invitation') |
|
49 |
|
|
50 |
|
|
51 |
models = { |
|
52 |
'auth.group': { |
|
53 |
'Meta': {'object_name': 'Group'}, |
|
54 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
55 |
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
|
56 |
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
|
57 |
}, |
|
58 |
'auth.permission': { |
|
59 |
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
|
60 |
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
|
61 |
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
|
62 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
63 |
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
|
64 |
}, |
|
65 |
'auth.user': { |
|
66 |
'Meta': {'object_name': 'User'}, |
|
67 |
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
|
68 |
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
|
69 |
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
|
70 |
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
|
71 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
72 |
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
|
73 |
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
74 |
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
75 |
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
|
76 |
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
|
77 |
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
|
78 |
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
|
79 |
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
|
80 |
}, |
|
81 |
'contenttypes.contenttype': { |
|
82 |
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
|
83 |
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
|
84 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
85 |
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
|
86 |
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
|
87 |
}, |
|
88 |
'im.astakosuser': { |
|
89 |
'Meta': {'object_name': 'AstakosUser', '_ormbases': ['auth.User']}, |
|
90 |
'affiliation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
91 |
'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
|
92 |
'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
93 |
'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
94 |
'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
95 |
'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
96 |
'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}), |
|
97 |
'provider': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
98 |
'updated': ('django.db.models.fields.DateTimeField', [], {}), |
|
99 |
'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}) |
|
100 |
}, |
|
101 |
'im.invitation': { |
|
102 |
'Meta': {'object_name': 'Invitation'}, |
|
103 |
'accepted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
|
104 |
'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), |
|
105 |
'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
|
106 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
107 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
108 |
'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}), |
|
109 |
'is_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
110 |
'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
111 |
'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
|
112 |
'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
|
113 |
} |
|
114 |
} |
|
115 |
|
|
116 |
complete_apps = ['im'] |
b/astakos/im/target/util.py | ||
---|---|---|
66 | 66 |
parts[3] = urlencode({'user': user.username, 'token': auth_token}) |
67 | 67 |
next = urlunsplit(parts) |
68 | 68 |
|
69 |
if settings.FORCE_PROFILE_UPDATE and not user.is_verified: |
|
69 |
if settings.FORCE_PROFILE_UPDATE and not user.is_verified and not user.is_superuser:
|
|
70 | 70 |
params = '' |
71 | 71 |
if next: |
72 | 72 |
params = '?' + urlencode({'next': next}) |
b/astakos/im/templates/index.html | ||
---|---|---|
12 | 12 |
<div class="span4"> |
13 | 13 |
<h4>Local account</h4> |
14 | 14 |
<form action="{% url astakos.im.target.local.login %}" method="post" class="form-stacked">{% csrf_token %} |
15 |
{{ form.as_p }} |
|
15 |
{{ form.non_field_errors }} |
|
16 |
{% for field in form %} |
|
17 |
<div class="field-wrapper"> |
|
18 |
<div class="label-wrapper"> |
|
19 |
{% if field.name == "username" %} |
|
20 |
Email address or Username |
|
21 |
{% else %} |
|
22 |
{{ field.label_tag }} |
|
23 |
{% endif %} |
|
24 |
</div> |
|
25 |
<div class="value-wrapper"> |
|
26 |
{{ field }} |
|
27 |
{{ field.errors }} |
|
28 |
</div> |
|
29 |
</div> |
|
30 |
{% endfor %} |
|
16 | 31 |
<div> |
17 | 32 |
<a href="{% url django.contrib.auth.views.password_reset %}">Forgot your password?</a> |
18 | 33 |
</div> |
b/astakos/im/urls.py | ||
---|---|---|
32 | 32 |
# or implied, of GRNET S.A. |
33 | 33 |
|
34 | 34 |
from django.conf import settings |
35 |
from django.conf.urls.defaults import patterns, include |
|
35 |
from django.conf.urls.defaults import patterns, include, url
|
|
36 | 36 |
from django.core.urlresolvers import reverse |
37 | 37 |
|
38 | 38 |
urlpatterns = patterns('astakos.im.views', |
39 |
(r'^$', 'index'), |
|
40 |
(r'^login/?$', 'index'), |
|
41 |
(r'^profile/?$', 'edit_profile'), |
|
42 |
(r'^feedback/?$', 'send_feedback'), |
|
43 |
(r'^signup/?$', 'signup'), |
|
44 |
(r'^admin/', include('astakos.im.admin.urls')), |
|
39 |
url(r'^$', 'index'),
|
|
40 |
url(r'^login/?$', 'index'),
|
|
41 |
url(r'^profile/?$', 'edit_profile'),
|
|
42 |
url(r'^feedback/?$', 'send_feedback'),
|
|
43 |
url(r'^signup/?$', 'signup'),
|
|
44 |
url(r'^admin/', include('astakos.im.admin.urls')),
|
|
45 | 45 |
) |
46 | 46 |
|
47 | 47 |
urlpatterns += patterns('django.contrib.auth.views', |
48 |
(r'^logout/?$', 'logout'), |
|
49 |
(r'^password/?$', 'password_change', {'post_change_redirect':'admin'}), |
|
48 |
url(r'^logout/?$', 'logout'),
|
|
49 |
url(r'^password/?$', 'password_change', {'post_change_redirect':'admin'}),
|
|
50 | 50 |
) |
51 | 51 |
|
52 | 52 |
urlpatterns += patterns('astakos.im.target', |
53 |
(r'^login/dummy/?$', 'dummy.login') |
|
53 |
url(r'^login/dummy/?$', 'dummy.login')
|
|
54 | 54 |
) |
55 | 55 |
|
56 | 56 |
urlpatterns += patterns('', |
57 |
(r'^static/(?P<path>.*)$', 'django.views.static.serve', |
|
57 |
url(r'^static/(?P<path>.*)$', 'django.views.static.serve',
|
|
58 | 58 |
{'document_root': settings.PROJECT_PATH + '/im/static'}) |
59 | 59 |
) |
60 | 60 |
|
61 | 61 |
if 'local' in settings.IM_MODULES: |
62 | 62 |
urlpatterns += patterns('astakos.im.target', |
63 |
(r'^local/?$', 'local.login'), |
|
64 |
(r'^local/activate/?$', 'local.activate'), |
|
63 |
url(r'^local/?$', 'local.login'),
|
|
64 |
url(r'^local/activate/?$', 'local.activate'),
|
|
65 | 65 |
) |
66 | 66 |
urlpatterns += patterns('django.contrib.auth.views', |
67 |
(r'^local/password_reset/?$', 'password_reset', |
|
67 |
url(r'^local/password_reset/?$', 'password_reset',
|
|
68 | 68 |
{'email_template_name':'registration/password_email.txt'}), |
69 |
(r'^local/password_reset_done/?$', 'password_reset_done'), |
|
70 |
(r'^local/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', |
|
69 |
url(r'^local/password_reset_done/?$', 'password_reset_done'),
|
|
70 |
url(r'^local/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
|
|
71 | 71 |
'password_reset_confirm'), |
72 |
(r'^local/password/reset/complete/$', 'password_reset_complete') |
|
72 |
url(r'^local/password/reset/complete/$', 'password_reset_complete')
|
|
73 | 73 |
) |
74 | 74 |
|
75 | 75 |
if settings.INVITATIONS_ENABLED: |
76 | 76 |
urlpatterns += patterns('astakos.im.views', |
77 |
(r'^invite/?$', 'invite'), |
|
77 |
url(r'^invite/?$', 'invite'),
|
|
78 | 78 |
) |
79 | 79 |
urlpatterns += patterns('astakos.im.target', |
80 |
(r'^login/invitation/?$', 'invitation.login') |
|
80 |
url(r'^login/invitation/?$', 'invitation.login')
|
|
81 | 81 |
) |
82 | 82 |
|
83 | 83 |
if 'shibboleth' in settings.IM_MODULES: |
84 | 84 |
urlpatterns += patterns('astakos.im.target', |
85 |
(r'^login/shibboleth/?$', 'shibboleth.login') |
|
85 |
url(r'^login/shibboleth/?$', 'shibboleth.login')
|
|
86 | 86 |
) |
87 | 87 |
|
88 | 88 |
if 'twitter' in settings.IM_MODULES: |
89 | 89 |
urlpatterns += patterns('astakos.im.target', |
90 |
(r'^login/twitter/?$', 'twitter.login'), |
|
91 |
(r'^login/twitter/authenticated/?$', 'twitter.authenticated') |
|
90 |
url(r'^login/twitter/?$', 'twitter.login'),
|
|
91 |
url(r'^login/twitter/authenticated/?$', 'twitter.authenticated')
|
|
92 | 92 |
) |
93 | 93 |
|
94 | 94 |
urlpatterns += patterns('astakos.im.api', |
95 |
(r'^authenticate/?$', 'authenticate') |
|
95 |
url(r'^authenticate/?$', 'authenticate')
|
|
96 | 96 |
) |
97 | 97 |
|
b/astakos/im/views.py | ||
---|---|---|
111 | 111 |
except Invitation.DoesNotExist: |
112 | 112 |
return code |
113 | 113 |
|
114 |
def _send_invitation(baseurl, inv): |
|
115 |
url = settings.SIGNUP_TARGET % (baseurl, inv.code, quote(baseurl)) |
|
116 |
subject = _('Invitation to Pithos') |
|
114 |
def _send_invitation(request, baseurl, inv): |
|
115 |
subject = _('Invitation to Astakos') |
|
117 | 116 |
site = get_current_site(request) |
117 |
url = settings.SIGNUP_TARGET % (baseurl, inv.code, site.domain) |
|
118 | 118 |
message = render_to_string('invitation.txt', { |
119 | 119 |
'invitation': inv, |
120 | 120 |
'url': url, |
121 | 121 |
'baseurl': baseurl, |
122 |
'service': site_name,
|
|
122 |
'service': site.name,
|
|
123 | 123 |
'support': settings.DEFAULT_CONTACT_EMAIL}) |
124 | 124 |
sender = settings.DEFAULT_FROM_EMAIL |
125 | 125 |
send_mail(subject, message, sender, [inv.username]) |
... | ... | |
178 | 178 |
defaults={'code': code, 'realname': realname}) |
179 | 179 |
|
180 | 180 |
try: |
181 |
_send_invitation(request.build_absolute_uri('/').rstrip('/'), invitation) |
|
181 |
baseurl = request.build_absolute_uri('/').rstrip('/') |
|
182 |
_send_invitation(request, baseurl, invitation) |
|
182 | 183 |
if created: |
183 | 184 |
inviter.invitations = max(0, inviter.invitations - 1) |
184 | 185 |
inviter.save() |
... | ... | |
301 | 302 |
if next: |
302 | 303 |
return redirect(next) |
303 | 304 |
messages.add_message(request, status, message) |
304 |
except (Invitation.DoesNotExist), e: |
|
305 |
except (Invitation.DoesNotExist, Exception), e:
|
|
305 | 306 |
messages.add_message(request, messages.ERROR, e) |
306 | 307 |
return render_response(template_name, |
307 | 308 |
form = form if 'form' in locals() else UserCreationForm(), |
b/docs/source/adminguide.rst | ||
---|---|---|
2 | 2 |
=================== |
3 | 3 |
manage syncdb |
4 | 4 |
change django_site record |
5 |
SITE_ID setting |
|
5 | 6 |
migrate |
6 |
create twitter application |
|
7 |
create twitter application |
|
8 |
|
|
9 |
Administrator Guide |
|
10 |
=================== |
|
11 |
|
|
12 |
Simple Setup |
|
13 |
------------ |
|
14 |
|
|
15 |
Assuming a clean debian squeeze (stable) installation, use the following steps to run the software. |
|
16 |
|
|
17 |
Install packages:: |
|
18 |
|
|
19 |
apt-get install git python-django python-django-south python-setuptools python-sphinx python-httplib2 |
|
20 |
apt-get install apache2 libapache2-mod-wsgi |
|
21 |
|
|
22 |
Get the source:: |
|
23 |
|
|
24 |
cd / |
|
25 |
git clone https://code.grnet.gr/git/astakos |
|
26 |
|
|
27 |
Setup the files:: |
|
28 |
|
|
29 |
cd /astakos/astakos |
|
30 |
python manage.py syncdb (At this point you will prompt to create a superuser) |
|
31 |
python manage.py migrate im 0001 --fake |
|
32 |
python manage.py migrate im |
|
33 |
python loaddata im/fixtures/admin_user.json (Load additional information for the newly created superuser) |
|
34 |
edit im/fixtures/site.json (Change the ``domain`` and ``name`` values according to your site information) |
|
35 |
python loaddata im/fixtures/site.json |
|
36 |
cd /astakos |
|
37 |
python setup.py build_sphinx |
|
38 |
python manage runserver |
|
39 |
|
|
40 |
It is advised that you create a ``settings.local`` file to place any configuration overrides (at least change ``SECRET_KEY``). |
|
41 |
|
|
42 |
Twitter Setup |
|
43 |
------------- |
Also available in: Unified diff