Revision 890b0eaf astakos/im/backends/__init__.py

b/astakos/im/backends/__init__.py
33 33

  
34 34
from django.conf import settings
35 35
from django.utils.importlib import import_module
36
from django.core.exceptions import ImproperlyConfigured
37
from django.core.mail import send_mail
38
from django.template.loader import render_to_string
39
from django.utils.translation import ugettext as _
40
from django.contrib.auth.forms import UserCreationForm
41
from django.contrib import messages
42

  
43
from smtplib import SMTPException
44
from urllib import quote
45

  
46
from astakos.im.util import get_or_create_user
47
from astakos.im.models import AstakosUser, Invitation
48
from astakos.im.forms import ExtendedUserCreationForm, InvitedExtendedUserCreationForm
36 49

  
37 50
def get_backend():
38 51
    """
39 52
    Return an instance of a registration backend,
40
    according to the INVITATIONS_ENABLED setting.
41

  
53
    according to the INVITATIONS_ENABLED setting
54
    (if True returns ``astakos.im.backends.InvitationsBackend`` and if False
55
    returns ``astakos.im.backends.SimpleBackend``).
56
    
57
    If the backend cannot be located ``django.core.exceptions.ImproperlyConfigured``
58
    is raised.
42 59
    """
43
    module = 'invitations' if settings.INVITATIONS_ENABLED else 'simple'
44
    module = 'astakos.im.backends.%s' %module
45
    backend_class_name = 'Backend'
60
    module = 'astakos.im.backends'
61
    prefix = 'Invitations' if settings.INVITATIONS_ENABLED else 'Simple'
62
    backend_class_name = '%sBackend' %prefix
46 63
    try:
47 64
        mod = import_module(module)
48 65
    except ImportError, e:
......
51 68
        backend_class = getattr(mod, backend_class_name)
52 69
    except AttributeError:
53 70
        raise ImproperlyConfigured('Module "%s" does not define a registration backend named "%s"' % (module, attr))
54
    return backend_class()
71
    return backend_class()
72

  
73
class InvitationsBackend(object):
74
    """
75
    A registration backend which implements the following workflow: a user
76
    supplies the necessary registation information, if the request contains a valid
77
    inivation code the user is automatically activated otherwise an inactive user
78
    account is created and the user is going to receive an email as soon as an
79
    administrator activates his/her account.
80
    """
81
    def get_signup_form(self, request):
82
        """
83
        Returns the necassary registration form depending the user is invited or not
84
        
85
        Throws Invitation.DoesNotExist in case ``code`` is not valid.
86
        """
87
        code = request.GET.get('code', '')
88
        formclass = 'ExtendedUserCreationForm'
89
        if request.method == 'GET':
90
            initial_data = None
91
            if code:
92
                formclass = 'Invited%s' %formclass
93
                self.invitation = Invitation.objects.get(code=code)
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})
99
                inviter = AstakosUser.objects.get(username=self.invitation.inviter)
100
                initial_data['inviter'] = inviter.realname
101
        else:
102
            initial_data = request.POST
103
        self.form = globals()[formclass](initial_data)
104
        return self.form
105
    
106
    def _is_preaccepted(self, user):
107
        """
108
        If there is a valid, not-consumed invitation code for the specific user
109
        returns True else returns False.
110
        
111
        It should be called after ``get_signup_form`` which sets invitation if exists.
112
        """
113
        invitation = getattr(self, 'invitation') if hasattr(self, 'invitation') else None
114
        if not invitation:
115
            return False
116
        if invitation.username == user.username and not invitation.is_consumed:
117
            return True
118
        return False
119
    
120
    def signup(self, request):
121
        """
122
        Creates a incative user account. If the user is preaccepted (has a valid
123
        invitation code) the user is activated and if the request param ``next``
124
        is present redirects to it.
125
        In any other case the method returns the action status and a message.
126
        """
127
        kwargs = {}
128
        form = self.form
129
        user = form.save(commit=False)
130
        
131
        try:
132
            if self._is_preaccepted(user):
133
                user.is_active = True
134
                message = _('Registration completed. You can now login.')
135
                next = request.POST.get('next')
136
                if next:
137
                    return redirect(next)
138
            else:
139
                message = _('Registration completed. You will receive an email upon your account\'s activation')
140
            status = messages.SUCCESS
141
        except Invitation.DoesNotExist, e:
142
            status = messages.ERROR
143
            message = _('Invalid invitation code')
144
        return status, message
145

  
146
class SimpleBackend(object):
147
    """
148
    A registration backend which implements the following workflow: a user
149
    supplies the necessary registation information, an incative user account is
150
    created and receives an email in order to activate his/her account.
151
    """
152
    def get_signup_form(self, request):
153
        """
154
        Returns the UserCreationForm
155
        """
156
        initial_data = request.POST if request.method == 'POST' else None
157
        return UserCreationForm(initial_data)
158
    
159
    def signup(self, request, email_template_name='activation_email.txt'):
160
        """
161
        Creates an inactive user account and sends a verification email.
162
        
163
        ** Arguments **
164
        
165
        ``email_template_name``
166
            A custom template for the verification email body to use. This is
167
            optional; if not specified, this will default to
168
            ``activation_email.txt``.
169
        
170
        ** Templates **
171
            activation_email.txt or ``email_template_name`` keyword argument
172
        
173
        ** Settings **
174
        
175
        * ACTIVATION_LOGIN_TARGET: Where users should activate their local account
176
        * DEFAULT_CONTACT_EMAIL: service support email
177
        * DEFAULT_FROM_EMAIL: from email
178
        """
179
        kwargs = {}
180
        form = self.form
181
        user = form.save(commit=False)
182
        status = messages.SUCCESS
183
        try:
184
            _send_verification(request, user, email_template_name)
185
            message = _('Verification sent to %s' % user.email)
186
        except (SMTPException, socket.error) as e:
187
            status = messages.ERROR
188
            name = 'strerror'
189
            message = getattr(e, name) if hasattr(e, name) else e
190
        return status, message
191

  
192
    def _send_verification(request, user, template_name):
193
        site = get_current_site(request)
194
        baseurl = request.build_absolute_uri('/').rstrip('/')
195
        url = settings.ACTIVATION_LOGIN_TARGET % (baseurl,
196
                                                  quote(user.auth_token),
197
                                                  quote(baseurl))
198
        message = render_to_string(template_name, {
199
                'user': user,
200
                'url': url,
201
                'baseurl': baseurl,
202
                'site_name': site.name,
203
                'support': settings.DEFAULT_CONTACT_EMAIL})
204
        sender = settings.DEFAULT_FROM_EMAIL
205
        send_mail('Pithos account activation', message, sender, [user.email])
206
        logging.info('Sent activation %s', user)

Also available in: Unified diff