Statistics
| Branch: | Tag: | Revision:

root / invitations / invitations.py @ 4f8e7c6d

History | View | Annotate | Download (12 kB)

1 f1bb3880 Georgios Gousios
# vim: set fileencoding=utf-8 :
2 48130e66 Georgios Gousios
# Copyright 2011 GRNET S.A. All rights reserved.
3 48130e66 Georgios Gousios
#
4 48130e66 Georgios Gousios
# Redistribution and use in source and binary forms, with or without
5 48130e66 Georgios Gousios
# modification, are permitted provided that the following conditions
6 48130e66 Georgios Gousios
# are met:
7 48130e66 Georgios Gousios
#
8 48130e66 Georgios Gousios
#   1. Redistributions of source code must retain the above copyright
9 48130e66 Georgios Gousios
#      notice, this list of conditions and the following disclaimer.
10 48130e66 Georgios Gousios
#
11 48130e66 Georgios Gousios
#  2. Redistributions in binary form must reproduce the above copyright
12 48130e66 Georgios Gousios
#     notice, this list of conditions and the following disclaimer in the
13 48130e66 Georgios Gousios
#     documentation and/or other materials provided with the distribution.
14 48130e66 Georgios Gousios
#
15 48130e66 Georgios Gousios
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16 48130e66 Georgios Gousios
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 48130e66 Georgios Gousios
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 48130e66 Georgios Gousios
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19 48130e66 Georgios Gousios
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 48130e66 Georgios Gousios
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 48130e66 Georgios Gousios
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 48130e66 Georgios Gousios
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 48130e66 Georgios Gousios
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 48130e66 Georgios Gousios
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 48130e66 Georgios Gousios
# SUCH DAMAGE.
26 48130e66 Georgios Gousios
#
27 48130e66 Georgios Gousios
# The views and conclusions contained in the software and documentation are
28 48130e66 Georgios Gousios
# those of the authors and should not be interpreted as representing official
29 48130e66 Georgios Gousios
# policies, either expressed or implied, of GRNET S.A.
30 48130e66 Georgios Gousios
31 48130e66 Georgios Gousios
32 e6d6603a Georgios Gousios
from datetime import timedelta
33 0fbab7db Vangelis Koukis
import datetime
34 e6d6603a Georgios Gousios
import base64
35 3b09ff22 Georgios Gousios
import urllib
36 4f8e7c6d Georgios Gousios
import re
37 e6d6603a Georgios Gousios
38 03e70572 Georgios Gousios
from django.conf import settings
39 566cd8b2 Georgios Gousios
from django.core.exceptions import ValidationError
40 03e70572 Georgios Gousios
from django.db import transaction
41 4f8e7c6d Georgios Gousios
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest, HttpResponseServerError
42 a640b50d Georgios Gousios
from django.template.context import RequestContext
43 2a0eee64 Georgios Gousios
from django.template.loader import render_to_string
44 566cd8b2 Georgios Gousios
from django.core.validators import validate_email
45 a640b50d Georgios Gousios
from django.views.decorators.csrf import csrf_protect
46 b6b88056 Kostas Papadimitriou
from django.utils.translation import ugettext as _
47 566cd8b2 Georgios Gousios
48 d028ab18 Georgios Gousios
from synnefo.logic.email_send import send_async
49 03e70572 Georgios Gousios
from synnefo.api.common import method_not_allowed
50 03e70572 Georgios Gousios
from synnefo.db.models import Invitations, SynnefoUser
51 d028ab18 Georgios Gousios
from synnefo.logic import users, log
52 e6d6603a Georgios Gousios
53 e6d6603a Georgios Gousios
from Crypto.Cipher import AES
54 03e70572 Georgios Gousios
55 d028ab18 Georgios Gousios
_logger = log.get_logger("synnefo.invitations")
56 f1bb3880 Georgios Gousios
57 e6d6603a Georgios Gousios
def process_form(request):
58 a640b50d Georgios Gousios
    errors = []
59 566cd8b2 Georgios Gousios
    valid_inv = filter(lambda x: x.startswith("name_"), request.POST.keys())
60 ecb4680f Georgios Gousios
    invitation = None
61 566cd8b2 Georgios Gousios
62 566cd8b2 Georgios Gousios
    for inv in valid_inv:
63 566cd8b2 Georgios Gousios
        (name, inv_id) = inv.split('_')
64 566cd8b2 Georgios Gousios
65 05310288 Georgios Gousios
        email = ""
66 05310288 Georgios Gousios
        name = ""
67 566cd8b2 Georgios Gousios
        try:
68 566cd8b2 Georgios Gousios
            email = request.POST['email_' + inv_id]
69 566cd8b2 Georgios Gousios
            name = request.POST[inv]
70 566cd8b2 Georgios Gousios
71 566cd8b2 Georgios Gousios
            validate_name(name)
72 a640b50d Georgios Gousios
            validate_email(email)
73 a640b50d Georgios Gousios
74 ecb4680f Georgios Gousios
            invitation = add_invitation(request.user, name, email)
75 ecb4680f Georgios Gousios
            send_invitation(invitation)
76 a640b50d Georgios Gousios
77 d028ab18 Georgios Gousios
        except (InvitationException, ValidationError) as e:
78 d028ab18 Georgios Gousios
            errors += ["Invitation to %s <%s> not sent. Reason: %s" %
79 b6b88056 Kostas Papadimitriou
                       (name, email, e.messages[0])]
80 566cd8b2 Georgios Gousios
        except Exception as e:
81 ecb4680f Georgios Gousios
            remove_invitation(invitation)
82 d028ab18 Georgios Gousios
            _logger.exception(e)
83 ecb4680f Georgios Gousios
            errors += ["Invitation to %s <%s> could not be sent. Reason: %s" %
84 ecb4680f Georgios Gousios
                       (name, email, e)]
85 a640b50d Georgios Gousios
86 a640b50d Georgios Gousios
    respose = None
87 a640b50d Georgios Gousios
    if errors:
88 a640b50d Georgios Gousios
        data = render_to_string('invitations.html',
89 a640b50d Georgios Gousios
                                {'invitations': invitations_for_user(request),
90 8f50f91c Kostas Papadimitriou
                                    'errors': errors,
91 8f50f91c Kostas Papadimitriou
                                    'invitations_left': get_invitations_left(request.user)
92 8f50f91c Kostas Papadimitriou
                                },
93 a640b50d Georgios Gousios
                                context_instance=RequestContext(request))
94 a640b50d Georgios Gousios
        response =  HttpResponse(data)
95 25c36228 Georgios Gousios
        _logger.warn("Error adding invitation %s -> %s: %s"%(request.user.uniq,
96 25c36228 Georgios Gousios
                                                             email, errors))
97 a640b50d Georgios Gousios
    else:
98 848e6d10 Kostas Papadimitriou
        # form submitted
99 848e6d10 Kostas Papadimitriou
        data = render_to_string('invitations.html',
100 848e6d10 Kostas Papadimitriou
                                {'invitations': invitations_for_user(request),
101 848e6d10 Kostas Papadimitriou
                                    'invitations_left': get_invitations_left(request.user)
102 848e6d10 Kostas Papadimitriou
                                },
103 848e6d10 Kostas Papadimitriou
                                context_instance=RequestContext(request))
104 848e6d10 Kostas Papadimitriou
        response = HttpResponse(data)
105 25c36228 Georgios Gousios
        _logger.info("Added invitation %s -> %s"%(request.user.uniq, email))
106 566cd8b2 Georgios Gousios
107 566cd8b2 Georgios Gousios
    return response
108 566cd8b2 Georgios Gousios
109 f1bb3880 Georgios Gousios
110 566cd8b2 Georgios Gousios
def validate_name(name):
111 68a77896 Georgios Gousios
    if name is None or name.strip() == '':
112 a640b50d Georgios Gousios
        raise ValidationError("Name is empty")
113 a640b50d Georgios Gousios
114 a640b50d Georgios Gousios
    if name.find(' ') is -1:
115 b6b88056 Kostas Papadimitriou
        raise ValidationError(_("Name must contain at least one space"))
116 03e70572 Georgios Gousios
117 566cd8b2 Georgios Gousios
    return True
118 566cd8b2 Georgios Gousios
119 f1bb3880 Georgios Gousios
120 566cd8b2 Georgios Gousios
def invitations_for_user(request):
121 566cd8b2 Georgios Gousios
    invitations = []
122 566cd8b2 Georgios Gousios
123 566cd8b2 Georgios Gousios
    for inv in Invitations.objects.filter(source = request.user):
124 e6d6603a Georgios Gousios
        invitation = {}
125 566cd8b2 Georgios Gousios
126 e6d6603a Georgios Gousios
        invitation['sourcename'] = inv.source.realname
127 e6d6603a Georgios Gousios
        invitation['source'] = inv.source.uniq
128 e6d6603a Georgios Gousios
        invitation['targetname'] = inv.target.realname
129 e6d6603a Georgios Gousios
        invitation['target'] = inv.target.uniq
130 e6d6603a Georgios Gousios
        invitation['accepted'] = inv.accepted
131 e6d6603a Georgios Gousios
        invitation['sent'] = inv.created
132 4f8e7c6d Georgios Gousios
        invitation['id'] = inv.id
133 566cd8b2 Georgios Gousios
134 e6d6603a Georgios Gousios
        invitations.append(invitation)
135 566cd8b2 Georgios Gousios
136 e6d6603a Georgios Gousios
    return invitations
137 03e70572 Georgios Gousios
138 f1bb3880 Georgios Gousios
139 a640b50d Georgios Gousios
@csrf_protect
140 03e70572 Georgios Gousios
def inv_demux(request):
141 3b09ff22 Georgios Gousios
142 03e70572 Georgios Gousios
    if request.method == 'GET':
143 566cd8b2 Georgios Gousios
        data = render_to_string('invitations.html',
144 8f50f91c Kostas Papadimitriou
                {'invitations': invitations_for_user(request),
145 8f50f91c Kostas Papadimitriou
                    'invitations_left': get_invitations_left(request.user)
146 8f50f91c Kostas Papadimitriou
                },
147 a640b50d Georgios Gousios
                                context_instance=RequestContext(request))
148 03e70572 Georgios Gousios
        return  HttpResponse(data)
149 03e70572 Georgios Gousios
    elif request.method == 'POST':
150 e6d6603a Georgios Gousios
        return process_form(request)
151 03e70572 Georgios Gousios
    else:
152 03e70572 Georgios Gousios
        method_not_allowed(request)
153 03e70572 Georgios Gousios
154 33e00f02 Georgios Gousios
155 3b09ff22 Georgios Gousios
def login(request):
156 3b09ff22 Georgios Gousios
157 3b09ff22 Georgios Gousios
    if not request.method == 'GET':
158 3b09ff22 Georgios Gousios
        method_not_allowed(request)
159 3b09ff22 Georgios Gousios
160 3b09ff22 Georgios Gousios
    key = request.GET['key']
161 3b09ff22 Georgios Gousios
162 3b09ff22 Georgios Gousios
    if key is None:
163 914502af Georgios Gousios
        return render_login_error("10", "Required key is missing")
164 3b09ff22 Georgios Gousios
165 3b09ff22 Georgios Gousios
    PADDING = '{'
166 3b09ff22 Georgios Gousios
167 68a77896 Georgios Gousios
    try:
168 914502af Georgios Gousios
        DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
169 914502af Georgios Gousios
        cipher = AES.new(settings.INVITATION_ENCR_KEY)
170 914502af Georgios Gousios
        decoded = DecodeAES(cipher, key)
171 914502af Georgios Gousios
    except Exception:
172 914502af Georgios Gousios
        return render_login_error("20", "Required key is invalid")
173 3b09ff22 Georgios Gousios
174 3b09ff22 Georgios Gousios
    users = SynnefoUser.objects.filter(auth_token = decoded)
175 3b09ff22 Georgios Gousios
176 3b09ff22 Georgios Gousios
    if users.count() is 0:
177 914502af Georgios Gousios
        return render_login_error("20", "Required key is invalid")
178 3b09ff22 Georgios Gousios
179 3b09ff22 Georgios Gousios
    user = users[0]
180 3b09ff22 Georgios Gousios
    invitations = Invitations.objects.filter(target = user)
181 3b09ff22 Georgios Gousios
182 3b09ff22 Georgios Gousios
    if invitations.count() is 0:
183 914502af Georgios Gousios
        return render_login_error("30", "Non-existent invitation")
184 f1bb3880 Georgios Gousios
185 3b09ff22 Georgios Gousios
    inv = invitations[0]
186 3b09ff22 Georgios Gousios
187 0fbab7db Vangelis Koukis
    valid = timedelta(days=settings.INVITATION_VALID_DAYS)
188 3b09ff22 Georgios Gousios
    valid_until = inv.created + valid
189 0fbab7db Vangelis Koukis
    now = datetime.datetime.now()
190 3b09ff22 Georgios Gousios
191 0fbab7db Vangelis Koukis
    if now > valid_until:
192 914502af Georgios Gousios
        return render_login_error("40",
193 0fbab7db Vangelis Koukis
                                  "Invitation has expired (was valid until " \
194 0fbab7db Vangelis Koukis
                                  "%s, now is %s" %
195 0fbab7db Vangelis Koukis
                                  (valid_until.strftime('%A, %d %B %Y'),
196 0fbab7db Vangelis Koukis
                                   now.strftime('%A, %d %B %Y')))
197 0fbab7db Vangelis Koukis
198 73d6073f Georgios Gousios
    # Since the invitation is valid, renew the user's auth token. This also
199 73d6073f Georgios Gousios
    # takes care of cases where the user re-uses the invitation to
200 73d6073f Georgios Gousios
    # login when the original token has expired
201 73d6073f Georgios Gousios
    from synnefo.logic import users # redefine 'users'
202 73d6073f Georgios Gousios
    users.set_auth_token_expires(user, valid_until)
203 73d6073f Georgios Gousios
204 914502af Georgios Gousios
    #if inv.accepted == False:
205 914502af Georgios Gousios
    #    return render_login_error("60", "Invitation already accepted")
206 3b09ff22 Georgios Gousios
207 3b09ff22 Georgios Gousios
    inv.accepted = True
208 3b09ff22 Georgios Gousios
    inv.save()
209 3b09ff22 Georgios Gousios
210 25c36228 Georgios Gousios
    _logger.info("Invited user %s logged in"%(inv.target.uniq))
211 25c36228 Georgios Gousios
212 914502af Georgios Gousios
    data = dict()
213 914502af Georgios Gousios
    data['user'] = user.realname
214 914502af Georgios Gousios
    data['url'] = settings.APP_INSTALL_URL
215 914502af Georgios Gousios
216 914502af Georgios Gousios
    welcome = render_to_string('welcome.html', {'data': data})
217 914502af Georgios Gousios
218 914502af Georgios Gousios
    response = HttpResponse(welcome)
219 3b09ff22 Georgios Gousios
220 3b09ff22 Georgios Gousios
    response.set_cookie('X-Auth-Token', value=user.auth_token,
221 3b09ff22 Georgios Gousios
                        expires = valid_until.strftime('%a, %d-%b-%Y %H:%M:%S %Z'),
222 3b09ff22 Georgios Gousios
                        path='/')
223 3b09ff22 Georgios Gousios
    response['X-Auth-Token'] = user.auth_token
224 914502af Georgios Gousios
    return response
225 914502af Georgios Gousios
226 914502af Georgios Gousios
227 914502af Georgios Gousios
def render_login_error(code, text):
228 914502af Georgios Gousios
    error = dict()
229 914502af Georgios Gousios
    error['id'] = code
230 914502af Georgios Gousios
    error['text'] = text
231 914502af Georgios Gousios
232 914502af Georgios Gousios
    data = render_to_string('error.html', {'error': error})
233 914502af Georgios Gousios
234 914502af Georgios Gousios
    response = HttpResponse(data)
235 3b09ff22 Georgios Gousios
    return response
236 3b09ff22 Georgios Gousios
237 3b09ff22 Georgios Gousios
238 3b09ff22 Georgios Gousios
def send_invitation(invitation):
239 e6d6603a Georgios Gousios
    email = {}
240 e6d6603a Georgios Gousios
    email['invitee'] = invitation.target.realname
241 e6d6603a Georgios Gousios
    email['inviter'] = invitation.source.realname
242 e6d6603a Georgios Gousios
243 e6d6603a Georgios Gousios
    valid = timedelta(days = settings.INVITATION_VALID_DAYS)
244 e6d6603a Georgios Gousios
    valid_until = invitation.created + valid
245 e6d6603a Georgios Gousios
    email['valid_until'] = valid_until.strftime('%A, %d %B %Y')
246 e8fe75a8 Georgios Gousios
    email['url'] = enconde_inv_url(invitation)
247 e6d6603a Georgios Gousios
248 e6d6603a Georgios Gousios
    data = render_to_string('invitation.txt', {'email': email})
249 3b09ff22 Georgios Gousios
250 844ff4db Georgios Gousios
    _logger.debug("Invitation URL: %s" % email['url'])
251 844ff4db Georgios Gousios
252 f1bb3880 Georgios Gousios
    send_async(
253 3d3c58d7 Georgios Gousios
        frm = "%s"%(settings.DEFAULT_FROM_EMAIL),
254 f1bb3880 Georgios Gousios
        to = "%s <%s>"%(invitation.target.realname,invitation.target.uniq),
255 5aa0805d Vangelis Koukis
        subject = _('Invitation to ~okeanos IaaS service'),
256 f1bb3880 Georgios Gousios
        body = data
257 f1bb3880 Georgios Gousios
    )
258 f1bb3880 Georgios Gousios
259 e8fe75a8 Georgios Gousios
def enconde_inv_url(invitation):
260 e8fe75a8 Georgios Gousios
    PADDING = '{'
261 e8fe75a8 Georgios Gousios
    pad = lambda s: s + (32 - len(s) % 32) * PADDING
262 e8fe75a8 Georgios Gousios
    EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
263 e8fe75a8 Georgios Gousios
264 e8fe75a8 Georgios Gousios
    cipher = AES.new(settings.INVITATION_ENCR_KEY)
265 e8fe75a8 Georgios Gousios
    encoded = EncodeAES(cipher, invitation.target.auth_token)
266 e8fe75a8 Georgios Gousios
267 e8fe75a8 Georgios Gousios
    url_safe = urllib.urlencode({'key': encoded})
268 e8fe75a8 Georgios Gousios
    url = settings.APP_INSTALL_URL + "/invitations/login?" + url_safe
269 e8fe75a8 Georgios Gousios
270 e8fe75a8 Georgios Gousios
    return url
271 4f8e7c6d Georgios Gousios
272 4f8e7c6d Georgios Gousios
273 4f8e7c6d Georgios Gousios
def resend(request):
274 4f8e7c6d Georgios Gousios
    """
275 4f8e7c6d Georgios Gousios
    Resend an invitation that has been already sent
276 4f8e7c6d Georgios Gousios
    """
277 4f8e7c6d Georgios Gousios
278 4f8e7c6d Georgios Gousios
    if not request.method == 'POST':
279 4f8e7c6d Georgios Gousios
        return method_not_allowed(request)
280 4f8e7c6d Georgios Gousios
281 4f8e7c6d Georgios Gousios
    invid = request.POST["invid"]
282 4f8e7c6d Georgios Gousios
283 4f8e7c6d Georgios Gousios
    matcher = re.compile('^[0-9]+$')
284 4f8e7c6d Georgios Gousios
285 4f8e7c6d Georgios Gousios
    # XXX: Assumes numeric DB keys
286 4f8e7c6d Georgios Gousios
    if not matcher.match(invid):
287 4f8e7c6d Georgios Gousios
        return HttpResponseBadRequest("Invalid content for parameter [invid]")
288 4f8e7c6d Georgios Gousios
289 4f8e7c6d Georgios Gousios
    try:
290 4f8e7c6d Georgios Gousios
        inv = Invitations.objects.get(id = invid)
291 4f8e7c6d Georgios Gousios
    except Exception:
292 4f8e7c6d Georgios Gousios
        return HttpResponseBadRequest("Invitation to resend does not exist")
293 4f8e7c6d Georgios Gousios
294 4f8e7c6d Georgios Gousios
    if not request.user == inv.source:
295 4f8e7c6d Georgios Gousios
        return HttpResponseBadRequest("Invitation does not belong to user")
296 4f8e7c6d Georgios Gousios
297 4f8e7c6d Georgios Gousios
    try:
298 4f8e7c6d Georgios Gousios
        send_invitation(inv)
299 4f8e7c6d Georgios Gousios
    except Exception:
300 4f8e7c6d Georgios Gousios
        return HttpResponseServerError("Error sending invitation email")
301 4f8e7c6d Georgios Gousios
302 4f8e7c6d Georgios Gousios
    return HttpResponse("Invitation has been resent")
303 e8fe75a8 Georgios Gousios
304 9f8ad4c5 Georgios Gousios
def get_invitee_level(source):
305 9f8ad4c5 Georgios Gousios
    return get_user_inv_level(source) + 1
306 9f8ad4c5 Georgios Gousios
307 9f8ad4c5 Georgios Gousios
308 9f8ad4c5 Georgios Gousios
def get_user_inv_level(u):
309 9f8ad4c5 Georgios Gousios
    inv = Invitations.objects.filter(target = u)
310 9f8ad4c5 Georgios Gousios
311 cf8482f2 Giorgos Verigakis
    if not inv:
312 9f8ad4c5 Georgios Gousios
        raise Exception("User without invitation", u)
313 9f8ad4c5 Georgios Gousios
314 9f8ad4c5 Georgios Gousios
    return inv[0].level
315 9f8ad4c5 Georgios Gousios
316 9f8ad4c5 Georgios Gousios
317 03e70572 Georgios Gousios
@transaction.commit_on_success
318 03e70572 Georgios Gousios
def add_invitation(source, name, email):
319 03e70572 Georgios Gousios
    """
320 03e70572 Georgios Gousios
        Adds an invitation, if the source user has not gone over his/her
321 33e00f02 Georgios Gousios
        invitation limit or the target user has not been invited already
322 03e70572 Georgios Gousios
    """
323 03e70572 Georgios Gousios
    num_inv = Invitations.objects.filter(source = source).count()
324 03e70572 Georgios Gousios
325 33e00f02 Georgios Gousios
    if num_inv >= source.max_invitations:
326 d652580d Georgios Gousios
        raise TooManyInvitations("User invitation limit (%d) exhausted" %
327 33e00f02 Georgios Gousios
                                 source.max_invitations)
328 03e70572 Georgios Gousios
329 f1bb3880 Georgios Gousios
    target = SynnefoUser.objects.filter(uniq = email)
330 03e70572 Georgios Gousios
331 03e70572 Georgios Gousios
    if target.count() is not 0:
332 f1bb3880 Georgios Gousios
        raise AlreadyInvited("User with email %s already invited" % (email))
333 03e70572 Georgios Gousios
334 53e6717b Georgios Gousios
    users.register_user(name, email)
335 53e6717b Georgios Gousios
336 53e6717b Georgios Gousios
    target = SynnefoUser.objects.filter(uniq = email)
337 53e6717b Georgios Gousios
338 53e6717b Georgios Gousios
    r = list(target[:1])
339 53e6717b Georgios Gousios
    if not r:
340 bd1548a7 Georgios Gousios
        raise Exception("Invited user cannot be added")
341 03e70572 Georgios Gousios
342 9f8ad4c5 Georgios Gousios
    u = target[0]
343 9f8ad4c5 Georgios Gousios
    invitee_level = get_invitee_level(source)
344 9f8ad4c5 Georgios Gousios
345 9f8ad4c5 Georgios Gousios
    u.max_invitations = settings.INVITATIONS_PER_LEVEL[invitee_level]
346 9f8ad4c5 Georgios Gousios
    u.save()
347 9f8ad4c5 Georgios Gousios
348 03e70572 Georgios Gousios
    inv = Invitations()
349 03e70572 Georgios Gousios
    inv.source = source
350 9f8ad4c5 Georgios Gousios
    inv.target = u
351 9f8ad4c5 Georgios Gousios
    inv.level = invitee_level
352 03e70572 Georgios Gousios
    inv.save()
353 e6d6603a Georgios Gousios
    return inv
354 03e70572 Georgios Gousios
355 8f50f91c Kostas Papadimitriou
def get_invitations_left(user):
356 8f50f91c Kostas Papadimitriou
    """
357 8f50f91c Kostas Papadimitriou
    Get user invitations left
358 8f50f91c Kostas Papadimitriou
    """
359 8f50f91c Kostas Papadimitriou
    num_inv = Invitations.objects.filter(source = user).count()
360 8f50f91c Kostas Papadimitriou
    return user.max_invitations - num_inv
361 8f50f91c Kostas Papadimitriou
362 847a3ecf Georgios Gousios
def remove_invitation(invitation):
363 847a3ecf Georgios Gousios
    """
364 847a3ecf Georgios Gousios
    Removes an invitation and the invited user
365 847a3ecf Georgios Gousios
    """
366 847a3ecf Georgios Gousios
    if invitation is not None:
367 ee60bb36 Georgios Gousios
        if isinstance(invitation, Invitations):
368 ee60bb36 Georgios Gousios
            if invitation.target is not None:
369 ee60bb36 Georgios Gousios
                invitation.target.delete()
370 ee60bb36 Georgios Gousios
            invitation.delete()
371 847a3ecf Georgios Gousios
372 d028ab18 Georgios Gousios
class InvitationException(Exception):
373 a640b50d Georgios Gousios
    def __init__(self, msg):
374 d53bbf68 Georgios Gousios
        self.messages = [msg]
375 03e70572 Georgios Gousios
376 d53bbf68 Georgios Gousios
class TooManyInvitations(InvitationException):
377 d53bbf68 Georgios Gousios
    pass
378 03e70572 Georgios Gousios
379 d028ab18 Georgios Gousios
class AlreadyInvited(InvitationException):
380 d53bbf68 Georgios Gousios
    pass