------------------
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
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
<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 %}
# 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
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)
# 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)
"""
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):
"""
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:
* 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)
--- /dev/null
+[
+ {
+ "model": "sites.site",
+ "pk": 1,
+ "fields": {
+ "domain": "localhost:8000/im",
+ "name": "Astakos"
+ }
+ }
+]
"""
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)
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']
"""
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
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):
"""
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):
"""
--- /dev/null
+# 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']
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})
<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>
# 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')
)
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])
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()
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(),
===================
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