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