add welcome_email template
[astakos] / astakos / im / views.py
index ca45e5f..aae8927 100644 (file)
 import logging
 import socket
 
-from random import randint
 from smtplib import SMTPException
 from urllib import quote
 from functools import wraps
 
-from django.conf import settings
 from django.core.mail import send_mail
 from django.http import HttpResponse
 from django.shortcuts import redirect
@@ -55,10 +53,14 @@ from django.http import HttpResponseRedirect
 
 from astakos.im.models import AstakosUser, Invitation
 from astakos.im.backends import get_backend
-from astakos.im.util import get_context, get_current_site, prepare_response
+from astakos.im.util import get_context, prepare_response, set_cookie
 from astakos.im.forms import *
+from astakos.im.settings import DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, COOKIE_NAME, COOKIE_DOMAIN, IM_MODULES, SITENAME, BASEURL
+from astakos.im.functions import invite as invite_func
 
-def render_response(template, tab=None, status=200, context_instance=None, **kwargs):
+logger = logging.getLogger(__name__)
+
+def render_response(template, tab=None, status=200, reset_cookie=False, context_instance=None, **kwargs):
     """
     Calls ``django.template.loader.render_to_string`` with an additional ``tab``
     keyword argument and returns an ``django.http.HttpResponse`` with the
@@ -68,7 +70,10 @@ def render_response(template, tab=None, status=200, context_instance=None, **kwa
         tab = template.partition('_')[0].partition('.html')[0]
     kwargs.setdefault('tab', tab)
     html = render_to_string(template, kwargs, context_instance=context_instance)
-    return HttpResponse(html, status=status)
+    response = HttpResponse(html, status=status)
+    if reset_cookie:
+        set_cookie(response, context_instance['request'].user)
+    return response
 
 
 def requires_anonymous(func):
@@ -85,7 +90,7 @@ def requires_anonymous(func):
         return func(request, *args)
     return wrapper
 
-def index(request, login_template_name='login.html', profile_template_name='profile.html', extra_context={}):
+def index(request, login_template_name='im/login.html', profile_template_name='im/profile.html', extra_context={}):
     """
     If there is logged on user renders the profile page otherwise renders login page.
     
@@ -93,18 +98,18 @@ def index(request, login_template_name='login.html', profile_template_name='prof
     
     ``login_template_name``
         A custom login template to use. This is optional; if not specified,
-        this will default to ``login.html``.
+        this will default to ``im/login.html``.
     
     ``profile_template_name``
         A custom profile template to use. This is optional; if not specified,
-        this will default to ``profile.html``.
+        this will default to ``im/profile.html``.
     
     ``extra_context``
         An dictionary of variables to add to the template context.
     
     **Template:**
     
-    profile.html or login.html or ``template_name`` keyword argument.
+    im/profile.html or im/login.html or ``template_name`` keyword argument.
     
     """
     template_name = login_template_name
@@ -118,33 +123,9 @@ def index(request, login_template_name='login.html', profile_template_name='prof
                            form = globals()[formclass](**kwargs),
                            context_instance = get_context(request, extra_context))
 
-def _generate_invitation_code():
-    while True:
-        code = randint(1, 2L**63 - 1)
-        try:
-            Invitation.objects.get(code=code)
-            # An invitation with this code already exists, try again
-        except Invitation.DoesNotExist:
-            return code
-
-def _send_invitation(request, baseurl, inv):
-    sitename, sitedomain = get_current_site(request, use_https=request.is_secure())
-    subject = _('Invitation to %s' % sitename)
-    baseurl = request.build_absolute_uri('/').rstrip('/')
-    url = '%s%s?code=%d' % (baseurl, reverse('astakos.im.views.signup'), inv.code)
-    message = render_to_string('invitation.txt', {
-                'invitation': inv,
-                'url': url,
-                'baseurl': baseurl,
-                'service': sitename,
-                'support': settings.DEFAULT_CONTACT_EMAIL % sitename.lower()})
-    sender = settings.DEFAULT_FROM_EMAIL % sitename
-    send_mail(subject, message, sender, [inv.username])
-    logging.info('Sent invitation %s', inv)
-
 @login_required
 @transaction.commit_manually
-def invite(request, template_name='invitations.html', extra_context={}):
+def invite(request, template_name='im/invitations.html', extra_context={}):
     """
     Allows a user to invite somebody else.
     
@@ -161,23 +142,22 @@ def invite(request, template_name='invitations.html', extra_context={}):
     
     ``template_name``
         A custom template to use. This is optional; if not specified,
-        this will default to ``invitations.html``.
+        this will default to ``im/invitations.html``.
     
     ``extra_context``
         An dictionary of variables to add to the template context.
     
     **Template:**
     
-    invitations.html or ``template_name`` keyword argument.
+    im/invitations.html or ``template_name`` keyword argument.
     
     **Settings:**
     
     The view expectes the following settings are defined:
     
     * LOGIN_URL: login uri
-    * SIGNUP_TARGET: Where users should signup with their invitation code
-    * DEFAULT_CONTACT_EMAIL: service support email
-    * DEFAULT_FROM_EMAIL: from email
+    * ASTAKOS_DEFAULT_CONTACT_EMAIL: service support email
+    * ASTAKOS_DEFAULT_FROM_EMAIL: from email
     """
     status = None
     message = None
@@ -188,18 +168,8 @@ def invite(request, template_name='invitations.html', extra_context={}):
         realname = request.POST.get('realname')
         
         if inviter.invitations > 0:
-            code = _generate_invitation_code()
-            invitation = Invitation(inviter=inviter,
-                                    username=username,
-                                    code=code,
-                                    realname=realname)
-            invitation.save()
-            
             try:
-                baseurl = request.build_absolute_uri('/').rstrip('/')
-                _send_invitation(request, baseurl, invitation)
-                inviter.invitations = max(0, inviter.invitations - 1)
-                inviter.save()
+                invite_func(inviter, username, realname)
                 status = messages.SUCCESS
                 message = _('Invitation sent to %s' % username)
                 transaction.commit()
@@ -223,7 +193,7 @@ def invite(request, template_name='invitations.html', extra_context={}):
                            context_instance = context)
 
 @login_required
-def edit_profile(request, template_name='profile.html', extra_context={}):
+def edit_profile(request, template_name='im/profile.html', extra_context={}):
     """
     Allows a user to edit his/her profile.
     
@@ -231,42 +201,53 @@ def edit_profile(request, template_name='profile.html', extra_context={}):
     In case of POST updates the user informantion and redirects to ``next``
     url parameter if exists.
     
-    If the user isn't logged in, redirects to settings.LOGIN_URL.  
+    If the user isn't logged in, redirects to settings.LOGIN_URL.
     
     **Arguments**
     
     ``template_name``
         A custom template to use. This is optional; if not specified,
-        this will default to ``profile.html``.
+        this will default to ``im/profile.html``.
     
     ``extra_context``
         An dictionary of variables to add to the template context.
     
     **Template:**
     
-    profile.html or ``template_name`` keyword argument.
+    im/profile.html or ``template_name`` keyword argument.
+    
+    **Settings:**
+    
+    The view expectes the following settings are defined:
+    
+    * LOGIN_URL: login uri
     """
     form = ProfileForm(instance=request.user)
     extra_context['next'] = request.GET.get('next')
+    reset_cookie = False
     if request.method == 'POST':
         form = ProfileForm(request.POST, instance=request.user)
         if form.is_valid():
             try:
-                form.save()
+                prev_token = request.user.auth_token
+                user = form.save()
+                reset_cookie = user.auth_token != prev_token
+                form = ProfileForm(instance=user)
+                next = request.POST.get('next')
+                if next:
+                    return redirect(next)
                 msg = _('Profile has been updated successfully')
                 messages.add_message(request, messages.SUCCESS, msg)
             except ValueError, ve:
                 messages.add_message(request, messages.ERROR, ve)
-        next = request.POST.get('next')
-        if next:
-            return redirect(next)
     return render_response(template_name,
+                           reset_cookie = reset_cookie,
                            form = form,
                            context_instance = get_context(request,
-                                                          extra_context,
-                                                          user=request.user))
+                                                          extra_context))
 
-def signup(request, on_failure='signup.html', on_success='signup_complete.html', extra_context={}, backend=None):
+@requires_anonymous
+def signup(request, on_failure='im/signup.html', on_success='im/signup_complete.html', extra_context={}, backend=None):
     """
     Allows a user to create a local account.
     
@@ -275,7 +256,7 @@ def signup(request, on_failure='signup.html', on_success='signup_complete.html',
     
     The user activation will be delegated to the backend specified by the ``backend`` keyword argument
     if present, otherwise to the ``astakos.im.backends.InvitationBackend``
-    if settings.INVITATIONS_ENABLED is True or ``astakos.im.backends.SimpleBackend`` if not
+    if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.backends.SimpleBackend`` if not
     (see backends);
     
     Upon successful user creation if ``next`` url parameter is present the user is redirected there
@@ -287,25 +268,25 @@ def signup(request, on_failure='signup.html', on_success='signup_complete.html',
     
     ``on_failure``
         A custom template to render in case of failure. This is optional;
-        if not specified, this will default to ``signup.html``.
+        if not specified, this will default to ``im/signup.html``.
     
     
     ``on_success``
         A custom template to render in case of success. This is optional;
-        if not specified, this will default to ``signup_complete.html``.
+        if not specified, this will default to ``im/signup_complete.html``.
     
     ``extra_context``
         An dictionary of variables to add to the template context.
     
     **Template:**
     
-    signup.html or ``on_failure`` keyword argument.
-    signup_complete.html or ``on_success`` keyword argument. 
+    im/signup.html or ``on_failure`` keyword argument.
+    im/signup_complete.html or ``on_success`` keyword argument. 
     """
     try:
         if not backend:
             backend = get_backend(request)
-        for provider in settings.IM_MODULES:
+        for provider in IM_MODULES:
             extra_context['%s_form' % provider] = backend.get_signup_form(provider)
         if request.method == 'POST':
             provider = request.POST.get('provider')
@@ -327,7 +308,7 @@ def signup(request, on_failure='signup.html', on_success='signup_complete.html',
                            context_instance=get_context(request, extra_context))
     except (Invitation.DoesNotExist, ValueError), e:
         messages.add_message(request, messages.ERROR, e)
-        for provider in settings.IM_MODULES:
+        for provider in IM_MODULES:
             main = provider.capitalize() if provider == 'local' else 'ThirdParty'
             formclass = '%sUserCreationForm' % main
             extra_context['%s_form' % provider] = globals()[formclass]()
@@ -335,31 +316,32 @@ def signup(request, on_failure='signup.html', on_success='signup_complete.html',
                            context_instance=get_context(request, extra_context))
 
 @login_required
-def send_feedback(request, template_name='feedback.html', email_template_name='feedback_mail.txt', extra_context={}):
+def send_feedback(request, template_name='im/feedback.html', email_template_name='im/feedback_mail.txt', extra_context={}):
     """
     Allows a user to send feedback.
     
     In case of GET request renders a form for providing the feedback information.
     In case of POST sends an email to support team.
     
-    If the user isn't logged in, redirects to settings.LOGIN_URL.  
+    If the user isn't logged in, redirects to settings.LOGIN_URL.
     
     **Arguments**
     
     ``template_name``
         A custom template to use. This is optional; if not specified,
-        this will default to ``feedback.html``.
+        this will default to ``im/feedback.html``.
     
     ``extra_context``
         An dictionary of variables to add to the template context.
     
     **Template:**
     
-    signup.html or ``template_name`` keyword argument.
+    im/signup.html or ``template_name`` keyword argument.
     
     **Settings:**
     
-    * DEFAULT_CONTACT_EMAIL: List of feedback recipients
+    * LOGIN_URL: login uri
+    * ASTAKOS_DEFAULT_CONTACT_EMAIL: List of feedback recipients
     """
     if request.method == 'GET':
         form = FeedbackForm()
@@ -369,10 +351,9 @@ def send_feedback(request, template_name='feedback.html', email_template_name='f
         
         form = FeedbackForm(request.POST)
         if form.is_valid():
-            sitename, sitedomain = get_current_site(request, use_https=request.is_secure())
-            subject = _("Feedback from %s" % sitename)
+            subject = _("Feedback from %s" % SITENAME)
             from_email = request.user.email
-            recipient_list = [settings.DEFAULT_CONTACT_EMAIL % sitename.lower()]
+            recipient_list = [DEFAULT_CONTACT_EMAIL]
             content = render_to_string(email_template_name, {
                         'message': form.cleaned_data['feedback_msg'],
                         'data': form.cleaned_data['feedback_data'],
@@ -396,14 +377,15 @@ def logout(request, template='registration/logged_out.html', extra_context={}):
     """
     auth_logout(request)
     response = HttpResponse()
-    response.delete_cookie(settings.COOKIE_NAME)
+    response.delete_cookie(COOKIE_NAME, path='/', domain=COOKIE_DOMAIN)
     next = request.GET.get('next')
     if next:
         response['Location'] = next
         response.status_code = 302
         return response
-    html = render_to_string(template, context_instance=get_context(request, extra_context))
-    return HttpResponse(html)
+    context = get_context(request, extra_context)
+    response.write(render_to_string(template, context_instance=context))
+    return response
 
 def activate(request):
     """
@@ -418,4 +400,4 @@ def activate(request):
     
     user.is_active = True
     user.save()
-    return prepare_response(request, user, next, renew=True)
\ No newline at end of file
+    return prepare_response(request, user, next, renew=True)