commit 618d7e056007a468866f97194c81310fbe137bf6
Author: Sofia Papagiannaki <papagian@gmail.com>
Date:   Fri Nov 25 13:34:17 2011 +0200

    fix erroneous commit

diff --git a/pithos/im/views.py b/pithos/im/views.py
index ad8f8f3..36d5aae 100644
--- a/pithos/im/views.py
+++ b/pithos/im/views.py
@@ -34,6 +34,7 @@
 import json
 import logging
 import socket
+import csv
 
 from datetime import datetime
 from functools import wraps
@@ -75,7 +76,6 @@ def requires_login(func):
         return func(request, *args)
     return wrapper
 
-
 def requires_admin(func):
     @wraps(func)
     def wrapper(request, *args):
@@ -91,7 +91,11 @@ def requires_admin(func):
 
 
 def index(request):
-    return render_response('index.html', next=request.GET.get('next', ''))
+    kwargs = {'standard_modules':settings.IM_STANDARD_MODULES,
+              'other_modules':settings.IM_OTHER_MODULES}
+    return render_response('index.html',
+                           next=request.GET.get('next', ''),
+                           **kwargs)
 
 
 @requires_admin
@@ -134,21 +138,6 @@ def users_list(request):
                             page=page,
                             prev=prev,
                             next=next)
-    
-@requires_admin
-def users_create(request):
-    if request.method == 'GET':
-        return render_response('users_create.html')
-    if request.method == 'POST':
-        user = User()
-        user.uniq = request.POST.get('uniq')
-        user.realname = request.POST.get('realname')
-        user.is_admin = True if request.POST.get('admin') else False
-        user.affiliation = request.POST.get('affiliation')
-        user.quota = int(request.POST.get('quota') or 0) * (1024 ** 3)  # In GiB
-        user.renew_token()
-        user.save()
-        return redirect(users_info, user.id)
 
 @requires_admin
 def users_info(request, user_id):
@@ -229,7 +218,7 @@ def invite(request):
                 defaults={'code': code, 'realname': realname})
             
             try:
-                send_invitation(request.get_host(), invitation)
+                send_invitation(request.build_absolute_uri('/').rstrip('/'), invitation)
                 if created:
                     inviter.invitations = max(0, inviter.invitations - 1)
                     inviter.save()
@@ -257,10 +246,9 @@ def invite(request):
     return HttpResponse(html)
 
 def send_verification(baseurl, user):
-    next = quote('http://%s' % baseurl)
     url = settings.ACTIVATION_LOGIN_TARGET % (baseurl,
                                               quote(user.auth_token),
-                                              next)
+                                              quote(baseurl))
     message = render_to_string('activation.txt', {
             'user': user,
             'url': url,
@@ -309,7 +297,7 @@ def local_create(request):
                 user.level = 1
                 user.renew_token()
                 try:
-                    send_verification(request.get_host(), user)
+                    send_verification(request.build_absolute_uri('/').rstrip('/'), user)
                     message = _('Verification sent to %s' % user.email)
                     user.save()
                 except (SMTPException, socket.error) as e:
@@ -324,10 +312,9 @@ def local_create(request):
         return response
 
 def send_password(baseurl, user):
-    next = quote('http://%s' % baseurl)
     url = settings.PASSWORD_RESET_TARGET % (baseurl,
                                             quote(user.uniq),
-                                            next)
+                                            quote(baseurl))
     message = render_to_string('password.txt', {
             'user': user,
             'url': url,
@@ -347,7 +334,7 @@ def reclaim_password(request):
         try:
             user = User.objects.get(uniq=username)
             try:
-                send_password(request.get_host(), user)
+                send_password(request.build_absolute_uri('/').rstrip('/'), user)
                 status = 'success'
                 message = _('Password reset sent to %s' % user.email)
                 user.status = 'UNVERIFIED'
@@ -364,3 +351,136 @@ def reclaim_password(request):
                 'status': status,
                 'message': message})
         return HttpResponse(html)
+
+@requires_admin
+def invitations_list(request):
+    invitations = Invitation.objects.order_by('id')
+    
+    filter = request.GET.get('filter', '')
+    if filter:
+        if filter.startswith('-'):
+            invitations = invitations.exclude(uniq__icontains=filter[1:])
+        else:
+            invitations = invitations.filter(uniq__icontains=filter)
+    
+    try:
+        page = int(request.GET.get('page', 1))
+    except ValueError:
+        page = 1
+    offset = max(0, page - 1) * settings.ADMIN_PAGE_LIMIT
+    limit = offset + settings.ADMIN_PAGE_LIMIT
+    
+    npages = int(ceil(1.0 * invitations.count() / settings.ADMIN_PAGE_LIMIT))
+    prev = page - 1 if page > 1 else None
+    next = page + 1 if page < npages else None
+    return render_response('invitations_list.html',
+                            invitations=invitations[offset:limit],
+                            filter=filter,
+                            pages=range(1, npages + 1),
+                            page=page,
+                            prev=prev,
+                            next=next)
+
+@requires_admin
+def invitations_export(request):
+    # Create the HttpResponse object with the appropriate CSV header.
+    response = HttpResponse(mimetype='text/csv')
+    response['Content-Disposition'] = 'attachment; filename=invitations.csv'
+
+    writer = csv.writer(response)
+    writer.writerow(['ID',
+                     'Uniq',
+                     'Real Name',
+                     'Code',
+                     'Inviter Uniq',
+                     'Inviter Real Name',
+                     'Is_accepted',
+                     'Created',
+                     'Accepted',])
+    invitations = Invitation.objects.order_by('id')
+    for inv in invitations:
+        writer.writerow([inv.id,
+                         inv.uniq.encode("utf-8"),
+                         inv.realname.encode("utf-8"),
+                         inv.code,
+                         inv.inviter.uniq.encode("utf-8"),
+                         inv.inviter.realname.encode("utf-8"),
+                         inv.is_accepted,
+                         inv.created,
+                         inv.accepted])
+
+    return response
+
+
+@requires_admin
+def users_export(request):
+    # Create the HttpResponse object with the appropriate CSV header.
+    response = HttpResponse(mimetype='text/csv')
+    response['Content-Disposition'] = 'attachment; filename=users.csv'
+
+    writer = csv.writer(response)
+    writer.writerow(['ID',
+                     'Uniq',
+                     'Real Name',
+                     'Admin',
+                     'Affiliation',
+                     'State',
+                     'Quota (GiB)',
+                     'Updated',])
+    users = User.objects.order_by('id')
+    for u in users:
+        writer.writerow([u.id,
+                         u.uniq.encode("utf-8"),
+                         u.realname.encode("utf-8"),
+                         u.is_admin,
+                         u.affiliation.encode("utf-8"),
+                         u.state.encode("utf-8"),
+                         u.quota,
+                         u.updated])
+
+    return response
+
+@requires_admin
+def users_create(request):
+    if request.method == 'GET':
+        return render_response('users_create.html')
+    if request.method == 'POST':
+        user = User()
+        user.uniq = request.POST.get('uniq')
+        user.realname = request.POST.get('realname')
+        user.is_admin = True if request.POST.get('admin') else False
+        user.affiliation = request.POST.get('affiliation')
+        user.quota = int(request.POST.get('quota') or 0) * (1024 ** 3)  # In GiB
+        user.renew_token()
+        user.save()
+        return redirect(users_info, user.id)
+
+@requires_login
+def users_profile(request):
+    next = request.GET.get('next')
+    user = User.objects.get(uniq=request.user)
+    states = [x[0] for x in User.ACCOUNT_STATE]
+    return render_response('users_profile.html',
+                            user=user,
+                            states=states,
+                            next=next)
+
+@requires_login
+def users_edit(request):
+    user = User.objects.get(uniq=request.user)
+    user.realname = request.POST.get('realname')
+    user.affiliation = request.POST.get('affiliation')
+    user.is_verified = True
+    user.save()
+    next = request.POST.get('next')
+    if next:
+        return redirect(next)
+    
+    status = 'success'
+    message = _('Profile has been updated')
+    html = render_to_string('users_profile.html', {
+            'user': user,
+            'status': status,
+            'message': message})
+    return HttpResponse(html)
+    
diff --git a/pithos/settings.py.dist b/pithos/settings.py.dist
index 2ca81ed..0cc86fe 100644
--- a/pithos/settings.py.dist
+++ b/pithos/settings.py.dist
@@ -74,7 +74,6 @@ BACKEND_DB_CONNECTION = 'sqlite:///' + os.path.join(PROJECT_PATH, 'backend.db')
 BACKEND_BLOCK_MODULE = 'pithos.backends.lib.hashfiler'
 BACKEND_BLOCK_PATH = os.path.join(PROJECT_PATH, 'data/')
 
-
 # Bypass authentication for user administration.
 BYPASS_ADMIN_AUTH = False
 
@@ -131,6 +130,7 @@ MIDDLEWARE_CLASSES = (
 #    'django.middleware.csrf.CsrfViewMiddleware',
 #    'django.contrib.auth.middleware.AuthenticationMiddleware',
 #    'django.contrib.messages.middleware.MessageMiddleware',
+    'pithos.middleware.URLEncodedHeadersMiddleware',
     'pithos.middleware.LoggingConfigMiddleware',
     'pithos.middleware.AuthMiddleware'
 )
@@ -153,8 +153,8 @@ INSTALLED_APPS = (
 #    'django.contrib.admindocs',
     'pithos.im',
     'pithos.api',
-    'pithos.public',
-    'pithos.ui'
+    'pithos.ui',
+    'south'
 )
 
 # Set the expiration time of newly created auth tokens
@@ -187,25 +187,35 @@ INVITATIONS_PER_LEVEL = {
     4   :   0
 }
 
-SERVICE_NAME = 'Pithos+'
+SERVICE_NAME = 'Pithos'
 
 # Where users should login with their invitation code
-INVITATION_LOGIN_TARGET = 'http://%s/im/login/invitation' \
+INVITATION_LOGIN_TARGET = '%s/im/login/invitation' \
                             '?code=%d' \
                             '&next=%s'
 
 # Where users should activate their local account
-ACTIVATION_LOGIN_TARGET = 'http://%s/im/local/activate/' \
+ACTIVATION_LOGIN_TARGET = '%s/im/local/activate/' \
                             '?auth=%s' \
                             '&next=%s'
 
 # Where users should reset their local password
-PASSWORD_RESET_TARGET = 'http://%s/im/local/reset/' \
+PASSWORD_RESET_TARGET = '%s/im/local/reset/' \
                             '?username=%s' \
                             '&next=%s'
 
+# Force user profile verification
+FORCE_PROFILE_UPDATE = False
+
 # The server is behind a proy (apache and gunicorn setup).
 USE_X_FORWARDED_HOST = False
 
+# Set umask (needed for gunicorn setup).
+#os.umask(0077)
+
 # Use to log to a file.
 LOGFILE = None
+
+# Identity Management enabled modules
+IM_STANDARD_MODULES = ['local', 'invitation']
+IM_OTHER_MODULES = ['twitter', 'shibboleth']
