Several minor changes:
authorSofia Papagiannaki <papagian@gmail.com>
Mon, 23 Jan 2012 14:06:09 +0000 (16:06 +0200)
committerSofia Papagiannaki <papagian@gmail.com>
Mon, 23 Jan 2012 14:06:09 +0000 (16:06 +0200)
- create im/migrations (for south use)
- fixture for django_site
- fix ExtendedUserCreationForm: use AstakosUser instead of django.contrib.auth.models.User
- do not redirect to profile page after login if request.user is superuser

15 files changed:
README
README.upgrade
astakos/im/admin/templates/admin_base.html
astakos/im/admin/urls.py
astakos/im/auth_backends.py
astakos/im/backends/__init__.py
astakos/im/fixtures/site.json [new file with mode: 0644]
astakos/im/forms.py
astakos/im/migrations/0001_initial.py [new file with mode: 0644]
astakos/im/migrations/__init__.py [new file with mode: 0644]
astakos/im/target/util.py
astakos/im/templates/index.html
astakos/im/urls.py
astakos/im/views.py
docs/source/adminguide.rst

diff --git a/README b/README
index 7aacf52..3fad5ae 100644 (file)
--- a/README
+++ b/README
@@ -29,7 +29,7 @@ Running the server
 ------------------
 
 Make sure you have all required packages installed:
-    apt-get install python-django python-setuptools python-sphinx python-httplib2
+    apt-get install python-django python-django-south python-setuptools python-sphinx python-httplib2
 
 Then run:
     python manage.py syncdb
index 3560795..6f9928b 100644 (file)
@@ -1,65 +1,2 @@
 UPGRADE
-=======
-
-0.7.9 -> 0.7.10
----------------
-* Update settings.py (BACKEND_*, SERVICE_NAME, *_EMAIL, *_TARGET, IM_*)
-* Update 'attributes' table in mysql (backend):
-    
-    mysql> update attributes set `key`='ETag' where `key`='hash';
-
-* Upgrade 'im_user' table (im app):
-    
-    ALTER TABLE im_user ADD COLUMN 'password' VARCHAR(255);
-
-0.7.10 -> 0.8.0
----------------
-* Upgrade 'public' table in mysql (backend):
-       * Run: mysqldump pithosdb public > public-table.sql
-       * mysql> drop table public;
-       * Update the codebase and run the server so the new public table is created
-       * From the sql dump above, take the row:
-           
-               INSERT INTO `public` VALUES (...);
-      
-          Rewrite as:
-           
-               INSERT INTO `public`(`path`) VALUES (...);
-         
-         And execute in the database
-* Create settings.local with local setting overrides
-* Install python-django-south
-* Setup south:
-    python manage.py syncdb
-    python manage.py migrate im 0001 --fake
-    python manage.py migrate im
-
-0.8.0 -> 0.8.1
---------------
-* Reset 'policy' table in mysql (backend):
-    
-    mysql> update policy set `value`='auto' where `key`='versioning';
-
-0.8.1 -> 0.8.2
---------------
-* Add the 'X-Forwarded-Protocol' header directive in the apache configuration, as described in the admin guide
-* Update 'attributes' table in mysql (backend):
-    
-    mysql> CREATE TABLE `attributes_new` (
-               `serial` int(11) NOT NULL,
-               `domain` varchar(255) COLLATE utf8_bin NOT NULL,
-               `key` varchar(255) COLLATE utf8_bin NOT NULL,
-               `value` varchar(255) COLLATE utf8_bin DEFAULT NULL,
-               PRIMARY KEY (`serial`,`domain`,`key`),
-               CONSTRAINT FOREIGN KEY (`serial`) REFERENCES `versions` (`serial`) ON DELETE CASCADE ON UPDATE CASCADE
-           ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-    mysql> insert into attributes_new select `serial`, 'pithos', `key`, `value` from attributes;
-    mysql> drop table attributes;
-    mysql> alter table attributes_new rename to attributes;
-
-* Update 'versions' table in mysql (backend):
-    
-    mysql> create temporary table tmp_uuids as select distinct node, uuid() as `uuid` from versions;
-    mysql> alter table versions add column `uuid` varchar(64) DEFAULT '' NOT NULL after `muser`;
-    mysql> update versions v, tmp_uuids u set v.`uuid` = u.`uuid` where v.`node` = u.`node`;
-    mysql> create index idx_versions_node_uuid on versions(uuid);
+=======
\ No newline at end of file
index 5fcb957..6b7ade7 100644 (file)
@@ -14,6 +14,9 @@
   <li{% ifequal tab "invitations" %} class="active"{% endifequal %}>
     <a href="{% url astakos.im.admin.views.invitations_list %}">Invitations</a>
   </li>
+  <li{% ifequal tab "logout" %} class="active"{% endifequal %}>
+    <a href="{% url django.contrib.auth.views.logout %}">Logout</a>
+  </li>
 </ul>
 {% endblock %}
 
index c580104..a239837 100644 (file)
 # interpreted as representing official policies, either expressed
 # or implied, of GRNET S.A.
 
-from django.conf.urls.defaults import patterns
+from django.conf.urls.defaults import patterns, url
 
 urlpatterns = patterns('astakos.im.admin.views',
-    (r'^$', 'admin'),
+    url(r'^$', 'admin'),
     
-    (r'^users/?$', 'users_list'),
-    (r'^users/(\d+)/?$', 'users_info'),
-    (r'^users/create$', 'users_create'),
-    (r'^users/(\d+)/modify/?$', 'users_modify'),
-    (r'^users/(\d+)/delete/?$', 'users_delete'),
-    (r'^users/export/?$', 'users_export'),
-    (r'^users/pending/?$', 'pending_users'),
-    (r'^users/activate/(\d+)/?$', 'users_activate'),
+    url(r'^users/?$', 'users_list'),
+    url(r'^users/(\d+)/?$', 'users_info'),
+    url(r'^users/create$', 'users_create'),
+    url(r'^users/(\d+)/modify/?$', 'users_modify'),
+    url(r'^users/(\d+)/delete/?$', 'users_delete'),
+    url(r'^users/export/?$', 'users_export'),
+    url(r'^users/pending/?$', 'pending_users'),
+    url(r'^users/activate/(\d+)/?$', 'users_activate'),
     
-    (r'^invitations/?$', 'invitations_list'),
-    (r'^invitations/export/?$', 'invitations_export'),
+    url(r'^invitations/?$', 'invitations_list'),
+    url(r'^invitations/export/?$', 'invitations_export'),
 )
\ No newline at end of file
index 96e546a..c474741 100644 (file)
@@ -6,6 +6,9 @@ from django.contrib.auth.backends import ModelBackend
 from astakos.im.models import AstakosUser
 
 class AstakosUserModelCredentialsBackend(ModelBackend):
+    """
+    AuthenticationBackend used to authenticate user creadentials
+    """
     def authenticate(self, username=None, password=None):
         try:
             user = AstakosUser.objects.get(username=username)
@@ -31,6 +34,9 @@ class AstakosUserModelCredentialsBackend(ModelBackend):
     #    return self._user_class
 
 class AstakosUserModelTokenBackend(ModelBackend):
+    """
+    AuthenticationBackend used to authenticate using token instead
+    """
     def authenticate(self, username=None, auth_token=None):
         try:
             user = AstakosUser.objects.get(username=username)
index 07e97bd..6fb7487 100644 (file)
@@ -86,22 +86,21 @@ class InvitationsBackend(object):
         """
         code = request.GET.get('code', '')
         formclass = 'ExtendedUserCreationForm'
+        initial_data = None
         if request.method == 'GET':
-            initial_data = None
             if code:
                 formclass = 'Invited%s' %formclass
                 self.invitation = Invitation.objects.get(code=code)
                 if self.invitation.is_consumed:
-                    return HttpResponseBadRequest('Invitation has beeen used')
-                initial_data.update({'username':self.invitation.username,
-                                       'email':self.invitation.username,
-                                       'realname':self.invitation.realname})
+                    raise Exception('Invitation has beeen used')
+                initial_data = {'username':self.invitation.username,
+                                'email':self.invitation.username,
+                                'realname':self.invitation.realname}
                 inviter = AstakosUser.objects.get(username=self.invitation.inviter)
                 initial_data['inviter'] = inviter.realname
         else:
             initial_data = request.POST
-        self.form = globals()[formclass](initial_data)
-        return self.form
+        return globals()[formclass](initial_data)
     
     def _is_preaccepted(self, user):
         """
@@ -125,12 +124,13 @@ class InvitationsBackend(object):
         In any other case the method returns the action status and a message.
         """
         kwargs = {}
-        form = self.form
-        user = form.save(commit=False)
+        form = self.get_signup_form(request)
+        user = form.save()
         
         try:
             if self._is_preaccepted(user):
                 user.is_active = True
+                user.save()
                 message = _('Registration completed. You can now login.')
                 next = request.POST.get('next')
                 if next:
@@ -177,8 +177,9 @@ class SimpleBackend(object):
         * DEFAULT_FROM_EMAIL: from email
         """
         kwargs = {}
-        form = self.form
-        user = form.save(commit=False)
+        form = self.get_signup_form(request)
+        user = form.save()
+        
         status = messages.SUCCESS
         try:
             _send_verification(request, user, email_template_name)
diff --git a/astakos/im/fixtures/site.json b/astakos/im/fixtures/site.json
new file mode 100644 (file)
index 0000000..24cc580
--- /dev/null
@@ -0,0 +1,10 @@
+[
+    {
+        "model": "sites.site",
+        "pk": 1,
+        "fields": {
+            "domain": "localhost:8000/im",
+            "name": "Astakos"
+           }
+    }
+]
index a869324..64c996e 100644 (file)
@@ -58,12 +58,9 @@ class ExtendedUserCreationForm(UserCreationForm):
     """
     Extends the built in UserCreationForm in several ways:
     
-    * Adds an email field, which uses the custom UniqueUserEmailField,
-      that is, the form does not validate if the email address already exists
-      in the User table.
-    * The username field is generated based on the email, and isn't visible.
+    * Adds an email field, which uses the custom UniqueUserEmailField.
+    * The username field isn't visible and it is assigned the email value.
     * first_name and last_name fields are added.
-    * Data not saved by the default behavior of UserCreationForm is saved.
     """
     
     username = forms.CharField(required = False, max_length = 30)
@@ -71,11 +68,15 @@ class ExtendedUserCreationForm(UserCreationForm):
     first_name = forms.CharField(required = False, max_length = 30)
     last_name = forms.CharField(required = False, max_length = 30)
     
+    class Meta:
+        model = AstakosUser
+        fields = ("username",)
+    
     def __init__(self, *args, **kwargs):
         """
         Changes the order of fields, and removes the username field.
         """
-        super(UserCreationForm, self).__init__(*args, **kwargs)
+        super(ExtendedUserCreationForm, self).__init__(*args, **kwargs)
         self.fields.keyOrder = ['email', 'first_name', 'last_name',
                                 'password1', 'password2']
     
@@ -85,8 +86,6 @@ class ExtendedUserCreationForm(UserCreationForm):
         """
         cleaned_data = super(UserCreationForm, self).clean(*args, **kwargs)
         if cleaned_data.has_key('email'):
-            #cleaned_data['username'] = self.__generate_username(
-            #                                            cleaned_data['email'])
             cleaned_data['username'] = cleaned_data['email']
         return cleaned_data
         
@@ -96,12 +95,6 @@ class ExtendedUserCreationForm(UserCreationForm):
         save behavior is complete.
         """
         user = super(UserCreationForm, self).save(commit)
-        if user:
-            kwargs = {}
-            for field in self.fields:
-                if hasattr(AstakosUser(), field):
-                    kwargs[field] = self.cleaned_data[field]
-            user = get_or_create_user(username=self.cleaned_data['email'], **kwargs)
         return user
 
 class InvitedExtendedUserCreationForm(ExtendedUserCreationForm):
@@ -112,12 +105,17 @@ class InvitedExtendedUserCreationForm(ExtendedUserCreationForm):
     """
     inviter = forms.CharField(widget=forms.TextInput(),
                                 label=_('Inviter Real Name'))
+    class Meta:
+        model = AstakosUser
+        fields = ("username",)
     
     def __init__(self, *args, **kwargs):
-        super(RegisterForm, self).__init__(*args, **kwargs)
+        super(InvitedExtendedUserCreationForm, self).__init__(*args, **kwargs)
         
         #set readonly form fields
         self.fields['inviter'].widget.attrs['readonly'] = True
+        self.fields['email'].widget.attrs['readonly'] = True
+        self.fields['username'].widget.attrs['readonly'] = True
 
 class ProfileForm(forms.ModelForm):
     """
diff --git a/astakos/im/migrations/0001_initial.py b/astakos/im/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..1ab9845
--- /dev/null
@@ -0,0 +1,116 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding model 'AstakosUser'
+        db.create_table('im_astakosuser', (
+            ('user_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True, primary_key=True)),
+            ('affiliation', self.gf('django.db.models.fields.CharField')(default='', max_length=255)),
+            ('provider', self.gf('django.db.models.fields.CharField')(default='', max_length=255)),
+            ('level', self.gf('django.db.models.fields.IntegerField')(default=4)),
+            ('invitations', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('auth_token', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)),
+            ('auth_token_created', self.gf('django.db.models.fields.DateTimeField')(null=True)),
+            ('auth_token_expires', self.gf('django.db.models.fields.DateTimeField')(null=True)),
+            ('updated', self.gf('django.db.models.fields.DateTimeField')()),
+            ('is_verified', self.gf('django.db.models.fields.BooleanField')(default=False)),
+        ))
+        db.send_create_signal('im', ['AstakosUser'])
+
+        # Adding model 'Invitation'
+        db.create_table('im_invitation', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('inviter', self.gf('django.db.models.fields.related.ForeignKey')(related_name='invitations_sent', null=True, to=orm['im.AstakosUser'])),
+            ('realname', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('username', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('code', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+            ('is_accepted', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('is_consumed', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+            ('accepted', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+            ('consumed', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('im', ['Invitation'])
+
+
+    def backwards(self, orm):
+        
+        # Deleting model 'AstakosUser'
+        db.delete_table('im_astakosuser')
+
+        # Deleting model 'Invitation'
+        db.delete_table('im_invitation')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'im.astakosuser': {
+            'Meta': {'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+            'affiliation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}),
+            'provider': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'im.invitation': {
+            'Meta': {'object_name': 'Invitation'},
+            'accepted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+            'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+            'is_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+
+    complete_apps = ['im']
diff --git a/astakos/im/migrations/__init__.py b/astakos/im/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
index c33f234..9316f3a 100644 (file)
@@ -66,7 +66,7 @@ def prepare_response(request, user, next='', renew=False):
             parts[3] = urlencode({'user': user.username, 'token': auth_token})
             next = urlunsplit(parts)
     
-    if settings.FORCE_PROFILE_UPDATE and not user.is_verified:
+    if settings.FORCE_PROFILE_UPDATE and not user.is_verified and not user.is_superuser:
         params = ''
         if next:
             params = '?' + urlencode({'next': next})
index 1a3cf7a..ac8d029 100644 (file)
       <div class="span4">
         <h4>Local account</h4>
         <form action="{% url astakos.im.target.local.login %}" method="post" class="form-stacked">{% csrf_token %}
-          {{ form.as_p }}
+          {{ form.non_field_errors }}
+          {% for field in form %}
+            <div class="field-wrapper">
+              <div class="label-wrapper">
+                {% if field.name == "username" %}
+                  Email address or Username
+                {% else %}
+                  {{ field.label_tag }}
+                {% endif %}
+              </div>
+              <div class="value-wrapper">
+                {{ field }}
+                {{ field.errors }}
+              </div>
+            </div>
+          {% endfor %}
          <div>
             <a href="{% url django.contrib.auth.views.password_reset %}">Forgot your password?</a>
          </div>
index b7b1b4f..7076b5a 100644 (file)
 # or implied, of GRNET S.A.
 
 from django.conf import settings
-from django.conf.urls.defaults import patterns, include
+from django.conf.urls.defaults import patterns, include, url
 from django.core.urlresolvers import reverse
 
 urlpatterns = patterns('astakos.im.views',
-    (r'^$', 'index'),
-    (r'^login/?$', 'index'),
-    (r'^profile/?$', 'edit_profile'),
-    (r'^feedback/?$', 'send_feedback'),
-    (r'^signup/?$', 'signup'),
-    (r'^admin/', include('astakos.im.admin.urls')),
+    url(r'^$', 'index'),
+    url(r'^login/?$', 'index'),
+    url(r'^profile/?$', 'edit_profile'),
+    url(r'^feedback/?$', 'send_feedback'),
+    url(r'^signup/?$', 'signup'),
+    url(r'^admin/', include('astakos.im.admin.urls')),
 )
 
 urlpatterns += patterns('django.contrib.auth.views',
-    (r'^logout/?$', 'logout'),
-    (r'^password/?$', 'password_change', {'post_change_redirect':'admin'}),
+    url(r'^logout/?$', 'logout'),
+    url(r'^password/?$', 'password_change', {'post_change_redirect':'admin'}),
 )
 
 urlpatterns += patterns('astakos.im.target',
-    (r'^login/dummy/?$', 'dummy.login')
+    url(r'^login/dummy/?$', 'dummy.login')
 )
 
 urlpatterns += patterns('',
-    (r'^static/(?P<path>.*)$', 'django.views.static.serve',
+    url(r'^static/(?P<path>.*)$', 'django.views.static.serve',
                                 {'document_root': settings.PROJECT_PATH + '/im/static'})
 )
 
 if 'local' in settings.IM_MODULES:
     urlpatterns += patterns('astakos.im.target',
-        (r'^local/?$', 'local.login'),
-        (r'^local/activate/?$', 'local.activate'),
+        url(r'^local/?$', 'local.login'),
+        url(r'^local/activate/?$', 'local.activate'),
     )
     urlpatterns += patterns('django.contrib.auth.views',
-        (r'^local/password_reset/?$', 'password_reset',
+        url(r'^local/password_reset/?$', 'password_reset',
          {'email_template_name':'registration/password_email.txt'}),
-        (r'^local/password_reset_done/?$', 'password_reset_done'),
-        (r'^local/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
+        url(r'^local/password_reset_done/?$', 'password_reset_done'),
+        url(r'^local/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
          'password_reset_confirm'),
-        (r'^local/password/reset/complete/$', 'password_reset_complete')
+        url(r'^local/password/reset/complete/$', 'password_reset_complete')
     )
 
 if settings.INVITATIONS_ENABLED:
     urlpatterns += patterns('astakos.im.views',
-        (r'^invite/?$', 'invite'),
+        url(r'^invite/?$', 'invite'),
     )
     urlpatterns += patterns('astakos.im.target',
-        (r'^login/invitation/?$', 'invitation.login')
+        url(r'^login/invitation/?$', 'invitation.login')
     )
 
 if 'shibboleth' in settings.IM_MODULES:
     urlpatterns += patterns('astakos.im.target',
-        (r'^login/shibboleth/?$', 'shibboleth.login')
+        url(r'^login/shibboleth/?$', 'shibboleth.login')
     )
 
 if 'twitter' in settings.IM_MODULES:
     urlpatterns += patterns('astakos.im.target',
-        (r'^login/twitter/?$', 'twitter.login'),
-        (r'^login/twitter/authenticated/?$', 'twitter.authenticated')
+        url(r'^login/twitter/?$', 'twitter.login'),
+        url(r'^login/twitter/authenticated/?$', 'twitter.authenticated')
     )
 
 urlpatterns += patterns('astakos.im.api',
-    (r'^authenticate/?$', 'authenticate')
+    url(r'^authenticate/?$', 'authenticate')
 )
     
index ba563ec..c72c2a5 100644 (file)
@@ -111,15 +111,15 @@ def _generate_invitation_code():
         except Invitation.DoesNotExist:
             return code
 
-def _send_invitation(baseurl, inv):
-    url = settings.SIGNUP_TARGET % (baseurl, inv.code, quote(baseurl))
-    subject = _('Invitation to Pithos')
+def _send_invitation(request, baseurl, inv):
+    subject = _('Invitation to Astakos')
     site = get_current_site(request)
+    url = settings.SIGNUP_TARGET % (baseurl, inv.code, site.domain)
     message = render_to_string('invitation.txt', {
                 'invitation': inv,
                 'url': url,
                 'baseurl': baseurl,
-                'service': site_name,
+                'service': site.name,
                 'support': settings.DEFAULT_CONTACT_EMAIL})
     sender = settings.DEFAULT_FROM_EMAIL
     send_mail(subject, message, sender, [inv.username])
@@ -178,7 +178,8 @@ def invite(request, template_name='invitations.html', extra_context={}):
                 defaults={'code': code, 'realname': realname})
             
             try:
-                _send_invitation(request.build_absolute_uri('/').rstrip('/'), invitation)
+                baseurl = request.build_absolute_uri('/').rstrip('/')
+                _send_invitation(request, baseurl, invitation)
                 if created:
                     inviter.invitations = max(0, inviter.invitations - 1)
                     inviter.save()
@@ -301,7 +302,7 @@ def signup(request, template_name='signup.html', extra_context={}, backend=None)
                 if next:
                     return redirect(next)
                 messages.add_message(request, status, message)
-    except (Invitation.DoesNotExist), e:
+    except (Invitation.DoesNotExist, Exception), e:
         messages.add_message(request, messages.ERROR, e)
     return render_response(template_name,
                            form = form if 'form' in locals() else UserCreationForm(),
index 334fa46..bc55618 100644 (file)
@@ -2,5 +2,42 @@ Administrator Guide
 ===================
 manage syncdb
 change django_site record
+SITE_ID setting
 migrate
-create twitter application
\ No newline at end of file
+create twitter application
+
+Administrator Guide
+===================
+
+Simple Setup
+------------
+
+Assuming a clean debian squeeze (stable) installation, use the following steps to run the software.
+
+Install packages::
+
+  apt-get install git python-django python-django-south python-setuptools python-sphinx python-httplib2
+  apt-get install apache2 libapache2-mod-wsgi
+
+Get the source::
+
+  cd /
+  git clone https://code.grnet.gr/git/astakos
+
+Setup the files::
+
+  cd /astakos/astakos
+  python manage.py syncdb (At this point you will prompt to create a superuser)
+  python manage.py migrate im 0001 --fake
+  python manage.py migrate im
+  python loaddata im/fixtures/admin_user.json (Load additional information for the newly created superuser)
+  edit im/fixtures/site.json (Change the ``domain`` and ``name`` values according to your site information)
+  python loaddata im/fixtures/site.json
+  cd /astakos
+  python setup.py build_sphinx
+  python manage runserver
+
+It is advised that you create a ``settings.local`` file to place any configuration overrides (at least change ``SECRET_KEY``).
+
+Twitter Setup
+-------------
\ No newline at end of file