Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / functions.py @ ee4aa6eb

History | View | Annotate | Download (25.3 kB)

1
# Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import logging
35
import socket
36

    
37
from django.utils.translation import ugettext as _
38
from django.template.loader import render_to_string
39
from django.core.mail import send_mail
40
from django.core.urlresolvers import reverse
41
from django.template import Context, loader
42
from django.contrib.auth import (
43
    login as auth_login,
44
    logout as auth_logout)
45
from django.conf import settings
46
from django.contrib.auth.models import AnonymousUser
47
from django.core.exceptions import PermissionDenied
48
from django.db import IntegrityError
49

    
50
from urllib import quote
51
from urlparse import urljoin
52
from smtplib import SMTPException
53
from datetime import datetime
54
from functools import wraps
55

    
56
from astakos.im.settings import (
57
    DEFAULT_CONTACT_EMAIL, SITENAME, BASEURL, LOGGING_LEVEL,
58
    VERIFICATION_EMAIL_SUBJECT, ACCOUNT_CREATION_SUBJECT,
59
    GROUP_CREATION_SUBJECT, HELPDESK_NOTIFICATION_EMAIL_SUBJECT,
60
    INVITATION_EMAIL_SUBJECT, GREETING_EMAIL_SUBJECT, FEEDBACK_EMAIL_SUBJECT,
61
    EMAIL_CHANGE_EMAIL_SUBJECT,
62
    PROJECT_CREATION_SUBJECT, PROJECT_APPROVED_SUBJECT,
63
    PROJECT_TERMINATION_SUBJECT, PROJECT_SUSPENSION_SUBJECT,
64
    PROJECT_MEMBERSHIP_CHANGE_SUBJECT,
65
    PROJECT_MEMBER_JOIN_POLICIES, PROJECT_MEMBER_LEAVE_POLICIES)
66
from astakos.im.notifications import build_notification, NotificationError
67
from astakos.im.models import (
68
    AstakosUser, ProjectMembership, ProjectApplication, Project,
69
    trigger_sync)
70
from astakos.im.models import submit_application as models_submit_application
71

    
72
import astakos.im.messages as astakos_messages
73

    
74
logger = logging.getLogger(__name__)
75

    
76

    
77
def logged(func, msg):
78
    @wraps(func)
79
    def with_logging(*args, **kwargs):
80
        email = ''
81
        user = None
82
        try:
83
            request = args[0]
84
            email = request.user.email
85
        except (KeyError, AttributeError), e:
86
            email = ''
87
        r = func(*args, **kwargs)
88
        if LOGGING_LEVEL:
89
            logger.log(LOGGING_LEVEL, msg % email)
90
        return r
91
    return with_logging
92

    
93

    
94
def login(request, user):
95
    auth_login(request, user)
96
    from astakos.im.models import SessionCatalog
97
    SessionCatalog(
98
        session_key=request.session.session_key,
99
        user=user
100
    ).save()
101

    
102
login = logged(login, '%s logged in.')
103
logout = logged(auth_logout, '%s logged out.')
104

    
105

    
106
def send_verification(user, template_name='im/activation_email.txt'):
107
    """
108
    Send email to user to verify his/her email and activate his/her account.
109

110
    Raises SendVerificationError
111
    """
112
    url = '%s?auth=%s&next=%s' % (urljoin(BASEURL, reverse('activate')),
113
                                  quote(user.auth_token),
114
                                  quote(urljoin(BASEURL, reverse('index'))))
115
    message = render_to_string(template_name, {
116
                               'user': user,
117
                               'url': url,
118
                               'baseurl': BASEURL,
119
                               'site_name': SITENAME,
120
                               'support': DEFAULT_CONTACT_EMAIL})
121
    sender = settings.SERVER_EMAIL
122
    try:
123
        send_mail(_(VERIFICATION_EMAIL_SUBJECT), message, sender, [user.email])
124
    except (SMTPException, socket.error) as e:
125
        logger.exception(e)
126
        raise SendVerificationError()
127
    else:
128
        msg = 'Sent activation %s' % user.email
129
        logger.log(LOGGING_LEVEL, msg)
130

    
131

    
132
def send_activation(user, template_name='im/activation_email.txt'):
133
    send_verification(user, template_name)
134
    user.activation_sent = datetime.now()
135
    user.save()
136

    
137

    
138
def _send_admin_notification(template_name,
139
                             dictionary=None,
140
                             subject='alpha2 testing notification',):
141
    """
142
    Send notification email to settings.ADMINS.
143

144
    Raises SendNotificationError
145
    """
146
    if not settings.ADMINS:
147
        return
148
    dictionary = dictionary or {}
149
    message = render_to_string(template_name, dictionary)
150
    sender = settings.SERVER_EMAIL
151
    try:
152
        send_mail(subject,
153
                  message, sender, [i[1] for i in settings.ADMINS])
154
    except (SMTPException, socket.error) as e:
155
        logger.exception(e)
156
        raise SendNotificationError()
157
    else:
158
        msg = 'Sent admin notification for user %s' % dictionary.get('email',
159
                                                                     None)
160
        logger.log(LOGGING_LEVEL, msg)
161

    
162

    
163
def send_account_creation_notification(template_name, dictionary=None):
164
    user = dictionary.get('user', AnonymousUser())
165
    subject = _(ACCOUNT_CREATION_SUBJECT) % {'user':user.get('email', '')}
166
    return _send_admin_notification(template_name, dictionary, subject=subject)
167

    
168

    
169
def send_helpdesk_notification(user, template_name='im/helpdesk_notification.txt'):
170
    """
171
    Send email to DEFAULT_CONTACT_EMAIL to notify for a new user activation.
172

173
    Raises SendNotificationError
174
    """
175
    if not DEFAULT_CONTACT_EMAIL:
176
        return
177
    message = render_to_string(
178
        template_name,
179
        {'user': user}
180
    )
181
    sender = settings.SERVER_EMAIL
182
    try:
183
        send_mail(
184
            _(HELPDESK_NOTIFICATION_EMAIL_SUBJECT) % {'user': user.email},
185
            message, sender, [DEFAULT_CONTACT_EMAIL])
186
    except (SMTPException, socket.error) as e:
187
        logger.exception(e)
188
        raise SendNotificationError()
189
    else:
190
        msg = 'Sent helpdesk admin notification for %s' % user.email
191
        logger.log(LOGGING_LEVEL, msg)
192

    
193

    
194
def send_invitation(invitation, template_name='im/invitation.txt'):
195
    """
196
    Send invitation email.
197

198
    Raises SendInvitationError
199
    """
200
    subject = _(INVITATION_EMAIL_SUBJECT)
201
    url = '%s?code=%d' % (urljoin(BASEURL, reverse('index')), invitation.code)
202
    message = render_to_string(template_name, {
203
                               'invitation': invitation,
204
                               'url': url,
205
                               'baseurl': BASEURL,
206
                               'site_name': SITENAME,
207
                               'support': DEFAULT_CONTACT_EMAIL})
208
    sender = settings.SERVER_EMAIL
209
    try:
210
        send_mail(subject, message, sender, [invitation.username])
211
    except (SMTPException, socket.error) as e:
212
        logger.exception(e)
213
        raise SendInvitationError()
214
    else:
215
        msg = 'Sent invitation %s' % invitation
216
        logger.log(LOGGING_LEVEL, msg)
217
        invitation.inviter.invitations = max(0, invitation.inviter.invitations - 1)
218
        invitation.inviter.save()
219

    
220

    
221
def send_greeting(user, email_template_name='im/welcome_email.txt'):
222
    """
223
    Send welcome email.
224

225
    Raises SMTPException, socket.error
226
    """
227
    subject = _(GREETING_EMAIL_SUBJECT)
228
    message = render_to_string(email_template_name, {
229
                               'user': user,
230
                               'url': urljoin(BASEURL, reverse('index')),
231
                               'baseurl': BASEURL,
232
                               'site_name': SITENAME,
233
                               'support': DEFAULT_CONTACT_EMAIL})
234
    sender = settings.SERVER_EMAIL
235
    try:
236
        send_mail(subject, message, sender, [user.email])
237
    except (SMTPException, socket.error) as e:
238
        logger.exception(e)
239
        raise SendGreetingError()
240
    else:
241
        msg = 'Sent greeting %s' % user.email
242
        logger.log(LOGGING_LEVEL, msg)
243

    
244

    
245
def send_feedback(msg, data, user, email_template_name='im/feedback_mail.txt'):
246
    subject = _(FEEDBACK_EMAIL_SUBJECT)
247
    from_email = user.email
248
    recipient_list = [DEFAULT_CONTACT_EMAIL]
249
    content = render_to_string(email_template_name, {
250
        'message': msg,
251
        'data': data,
252
        'user': user})
253
    try:
254
        send_mail(subject, content, from_email, recipient_list)
255
    except (SMTPException, socket.error) as e:
256
        logger.exception(e)
257
        raise SendFeedbackError()
258
    else:
259
        msg = 'Sent feedback from %s' % user.email
260
        logger.log(LOGGING_LEVEL, msg)
261

    
262

    
263
def send_change_email(
264
    ec, request, email_template_name='registration/email_change_email.txt'):
265
    try:
266
        url = ec.get_url()
267
        url = request.build_absolute_uri(url)
268
        t = loader.get_template(email_template_name)
269
        c = {'url': url, 'site_name': SITENAME}
270
        from_email = settings.SERVER_EMAIL
271
        send_mail(_(EMAIL_CHANGE_EMAIL_SUBJECT),
272
                  t.render(Context(c)), from_email, [ec.new_email_address])
273
    except (SMTPException, socket.error) as e:
274
        logger.exception(e)
275
        raise ChangeEmailError()
276
    else:
277
        msg = 'Sent change email for %s' % ec.user.email
278
        logger.log(LOGGING_LEVEL, msg)
279

    
280

    
281
def activate(
282
    user,
283
    email_template_name='im/welcome_email.txt',
284
    helpdesk_email_template_name='im/helpdesk_notification.txt',
285
    verify_email=False):
286
    """
287
    Activates the specific user and sends email.
288

289
    Raises SendGreetingError, ValidationError
290
    """
291
    user.is_active = True
292
    user.email_verified = True
293
    if not user.activation_sent:
294
        user.activation_sent = datetime.now()
295
    user.save()
296
    send_helpdesk_notification(user, helpdesk_email_template_name)
297
    send_greeting(user, email_template_name)
298

    
299
def invite(inviter, email, realname):
300
    inv = Invitation(inviter=inviter, username=email, realname=realname)
301
    inv.save()
302
    send_invitation(inv)
303
    inviter.invitations = max(0, self.invitations - 1)
304
    inviter.save()
305

    
306
def switch_account_to_shibboleth(user, local_user,
307
                                 greeting_template_name='im/welcome_email.txt'):
308
    try:
309
        provider = user.provider
310
    except AttributeError:
311
        return
312
    else:
313
        if not provider == 'shibboleth':
314
            return
315
        user.delete()
316
        local_user.provider = 'shibboleth'
317
        local_user.third_party_identifier = user.third_party_identifier
318
        local_user.save()
319
        send_greeting(local_user, greeting_template_name)
320
        return local_user
321

    
322

    
323
class SendMailError(Exception):
324
    pass
325

    
326

    
327
class SendAdminNotificationError(SendMailError):
328
    def __init__(self):
329
        self.message = _(astakos_messages.ADMIN_NOTIFICATION_SEND_ERR)
330
        super(SendAdminNotificationError, self).__init__()
331

    
332

    
333
class SendVerificationError(SendMailError):
334
    def __init__(self):
335
        self.message = _(astakos_messages.VERIFICATION_SEND_ERR)
336
        super(SendVerificationError, self).__init__()
337

    
338

    
339
class SendInvitationError(SendMailError):
340
    def __init__(self):
341
        self.message = _(astakos_messages.INVITATION_SEND_ERR)
342
        super(SendInvitationError, self).__init__()
343

    
344

    
345
class SendGreetingError(SendMailError):
346
    def __init__(self):
347
        self.message = _(astakos_messages.GREETING_SEND_ERR)
348
        super(SendGreetingError, self).__init__()
349

    
350

    
351
class SendFeedbackError(SendMailError):
352
    def __init__(self):
353
        self.message = _(astakos_messages.FEEDBACK_SEND_ERR)
354
        super(SendFeedbackError, self).__init__()
355

    
356

    
357
class ChangeEmailError(SendMailError):
358
    def __init__(self):
359
        self.message = _(astakos_messages.CHANGE_EMAIL_SEND_ERR)
360
        super(ChangeEmailError, self).__init__()
361

    
362

    
363
class SendNotificationError(SendMailError):
364
    def __init__(self):
365
        self.message = _(astakos_messages.NOTIFICATION_SEND_ERR)
366
        super(SendNotificationError, self).__init__()
367

    
368

    
369
### PROJECT VIEWS ###
370

    
371
AUTO_ACCEPT_POLICY = 1
372
MODERATED_POLICY   = 2
373
CLOSED_POLICY      = 3
374

    
375
POLICIES = [ AUTO_ACCEPT_POLICY, MODERATED_POLICY, CLOSED_POLICY ]
376

    
377
def get_project_by_application_id(project_application_id):
378
    try:
379
        return Project.objects.get(application__id=project_application_id)
380
    except Project.DoesNotExist:
381
        raise IOError(
382
            _(astakos_messages.UNKNOWN_PROJECT_APPLICATION_ID) % project_application_id)
383

    
384
def get_project_id_of_application_id(project_application_id):
385
    try:
386
        return Project.objects.get(application__id=project_application_id).id
387
    except Project.DoesNotExist:
388
        raise IOError(
389
            _(astakos_messages.UNKNOWN_PROJECT_APPLICATION_ID) % project_application_id)
390

    
391
def get_project_by_id(project_id):
392
    try:
393
        return Project.objects.get(id=project_id)
394
    except Project.DoesNotExist:
395
        raise IOError(
396
            _(astakos_messages.UNKNOWN_PROJECT_ID) % project_id)
397

    
398
def get_project_for_update(project_id):
399
    try:
400
        return Project.objects.select_for_update().get(id=project_id)
401
    except Project.DoesNotExist:
402
        raise IOError(
403
            _(astakos_messages.UNKNOWN_PROJECT_ID) % project_id)
404

    
405
def get_user_by_id(user_id):
406
    try:
407
        return AstakosUser.objects.get(id=user_id)
408
    except AstakosUser.DoesNotExist:
409
        raise IOError(_(astakos_messages.UNKNOWN_USER_ID) % user_id)
410

    
411
def create_membership(project, user):
412
    if isinstance(project, int):
413
        project = get_project_by_id(project)
414
    if isinstance(user, int):
415
        user = get_user_by_id(user)
416
    m = ProjectMembership(
417
        project=project,
418
        person=user,
419
        request_date=datetime.now())
420
    try:
421
        m.save()
422
    except IntegrityError, e:
423
        raise IOError(_(astakos_messages.MEMBERSHIP_REQUEST_EXISTS))
424
    else:
425
        return m
426

    
427
def get_membership_for_update(project, user):
428
    if isinstance(project, int):
429
        project = get_project_by_id(project)
430
    if isinstance(user, int):
431
        user = get_user_by_id(user)
432
    try:
433
        return ProjectMembership.objects.select_for_update().get(
434
            project=project,
435
            person=user)
436
    except ProjectMembership.DoesNotExist:
437
        raise IOError(_(astakos_messages.NOT_MEMBERSHIP_REQUEST))
438

    
439
def accept_membership(project_application_id, user, request_user=None):
440
    """
441
        Raises:
442
            django.core.exceptions.PermissionDenied
443
            IOError
444
    """
445
    project_id = get_project_id_of_application_id(project_application_id)
446
    return do_accept_membership(project_id, user, request_user)
447

    
448
def do_accept_membership_checks(project, request_user):
449
    if request_user and \
450
        (not project.application.owner == request_user and \
451
            not request_user.is_superuser):
452
        raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
453
    if not project.is_alive:
454
        raise PermissionDenied(
455
            _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
456

    
457
    join_policy = project.application.member_join_policy
458
    if join_policy == CLOSED_POLICY:
459
        raise PermissionDenied(_(astakos_messages.MEMBER_JOIN_POLICY_CLOSED))
460

    
461
    if project.violates_members_limit(adding=1):
462
        raise PermissionDenied(_(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED))
463

    
464
def do_accept_membership(
465
        project_id, user, request_user=None, bypass_checks=False):
466
    project = get_project_for_update(project_id)
467

    
468
    if not bypass_checks:
469
        do_accept_membership_checks(project, request_user)
470

    
471
    membership = get_membership_for_update(project, user)
472
    membership.accept()
473
    trigger_sync()
474

    
475
    try:
476
        notification = build_notification(
477
            settings.SERVER_EMAIL,
478
            [membership.person.email],
479
            _(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % project.__dict__,
480
            template='im/projects/project_membership_change_notification.txt',
481
            dictionary={'object':project.application, 'action':'accepted'})
482
        notification.send()
483
    except NotificationError, e:
484
        logger.error(e.message)
485
    return membership
486

    
487
def reject_membership(project_application_id, user, request_user=None):
488
    """
489
        Raises:
490
            django.core.exceptions.PermissionDenied
491
            IOError
492
    """
493
    project_id = get_project_id_of_application_id(project_application_id)
494
    return do_reject_membership(project_id, user, request_user)
495

    
496
def do_reject_membership_checks(project, request_user):
497
    if request_user and \
498
        (not project.application.owner == request_user and \
499
            not request_user.is_superuser):
500
        raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
501
    if not project.is_alive:
502
        raise PermissionDenied(
503
            _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
504

    
505
def do_reject_membership(
506
        project_id, user, request_user=None, bypass_checks=False):
507
    project = get_project_for_update(project_id)
508

    
509
    if not bypass_checks:
510
        do_reject_membership_checks(project, request_user)
511

    
512
    membership = get_membership_for_update(project, user)
513
    membership.reject()
514

    
515
    try:
516
        notification = build_notification(
517
            settings.SERVER_EMAIL,
518
            [membership.person.email],
519
            _(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % project.__dict__,
520
            template='im/projects/project_membership_change_notification.txt',
521
            dictionary={'object':project.application, 'action':'rejected'})
522
        notification.send()
523
    except NotificationError, e:
524
        logger.error(e.message)
525
    return membership
526

    
527
def remove_membership(project_application_id, user, request_user=None):
528
    """
529
        Raises:
530
            django.core.exceptions.PermissionDenied
531
            IOError
532
    """
533
    project_id = get_project_id_of_application_id(project_application_id)
534
    return do_remove_membership(project_id, user, request_user)
535

    
536
def do_remove_membership_checks(project, membership, request_user=None):
537
    if request_user and \
538
        (not project.application.owner == request_user and \
539
            not request_user.is_superuser):
540
        raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
541
    if not project.is_alive:
542
        raise PermissionDenied(
543
            _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
544

    
545
def do_remove_membership(
546
        project_id, user, request_user=None, bypass_checks=False):
547
    project = get_project_for_update(project_id)
548

    
549
    if not bypass_checks:
550
        do_remove_membership_checks(project, request_user)
551

    
552
    leave_policy = project.application.member_leave_policy
553
    if leave_policy == CLOSED_POLICY:
554
        raise PermissionDenied(_(astakos_messages.MEMBER_LEAVE_POLICY_CLOSED))
555

    
556
    membership = get_membership_for_update(project, user)
557
    membership.remove()
558
    trigger_sync()
559

    
560
    try:
561
        notification = build_notification(
562
            settings.SERVER_EMAIL,
563
            [membership.person.email],
564
            _(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % project.__dict__,
565
            template='im/projects/project_membership_change_notification.txt',
566
            dictionary={'object':project.application, 'action':'removed'})
567
        notification.send()
568
    except NotificationError, e:
569
        logger.error(e.message)
570
    return membership
571

    
572
def enroll_member(project_application_id, user, request_user=None):
573
    project_id = get_project_id_of_application_id(project_application_id)
574
    return do_enroll_member(project_id, user, request_user)
575

    
576
def do_enroll_member(project_id, user, request_user=None):
577
    membership = create_membership(project_id, user)
578
    return do_accept_membership(
579
        project_id, user, request_user, bypass_checks=True)
580

    
581
def leave_project(project_application_id, user_id):
582
    """
583
        Raises:
584
            django.core.exceptions.PermissionDenied
585
            IOError
586
    """
587
    project_id = get_project_id_of_application_id(project_application_id)
588
    return do_leave_project(project_id, user_id)
589

    
590
def do_leave_project_checks(project):
591
    if not project.is_alive:
592
        m = _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__
593
        raise PermissionDenied(m)
594

    
595
    leave_policy = project.application.member_leave_policy
596
    if leave_policy == CLOSED_POLICY:
597
        raise PermissionDenied(_(astakos_messages.MEMBER_LEAVE_POLICY_CLOSED))
598

    
599
def do_leave_project(project_id, user_id, bypass_checks=False):
600
    project = get_project_for_update(project_id)
601

    
602
    if not bypass_checks:
603
        do_leave_project_checks(project)
604

    
605
    membership = get_membership_for_update(project, user_id)
606

    
607
    leave_policy = project.application.member_leave_policy
608
    if leave_policy == AUTO_ACCEPT_POLICY:
609
        membership.remove()
610
        trigger_sync()
611
    else:
612
        membership.leave_request_date = datetime.now()
613
        membership.save()
614
    return membership
615

    
616
def join_project(project_application_id, user_id):
617
    """
618
        Raises:
619
            django.core.exceptions.PermissionDenied
620
            IOError
621
    """
622
    project_id = get_project_id_of_application_id(project_application_id)
623
    return do_join_project(project_id, user_id)
624

    
625
def do_join_project_checks(project):
626
    if not project.is_alive:
627
        m = _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__
628
        raise PermissionDenied(m)
629

    
630
    join_policy = project.application.member_join_policy
631
    if join_policy == CLOSED_POLICY:
632
        raise PermissionDenied(_(astakos_messages.MEMBER_JOIN_POLICY_CLOSED))
633

    
634
def do_join_project(project_id, user_id, bypass_checks=False):
635
    project = get_project_for_update(project_id)
636

    
637
    if not bypass_checks:
638
        do_join_project_checks(project)
639

    
640
    membership = create_membership(project, user_id)
641

    
642
    join_policy = project.application.member_join_policy
643
    if (join_policy == AUTO_ACCEPT_POLICY and
644
        not project.violates_members_limit(adding=1)):
645
        membership.accept()
646
        trigger_sync()
647
    return membership
648

    
649
def submit_application(kw, request_user=None):
650

    
651
    kw['applicant'] = request_user
652

    
653
    precursor_id = kw.get('precursor_application', None)
654
    if precursor_id is not None:
655
        sfu = ProjectApplication.objects.select_for_update()
656
        precursor = sfu.get(id=precursor_id)
657
        kw['precursor_application'] = precursor
658

    
659
        if request_user and \
660
            (not precursor.owner == request_user and \
661
                not request_user.is_superuser):
662
            raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
663

    
664
    application = models_submit_application(**kw)
665

    
666
    try:
667
        notification = build_notification(
668
            settings.SERVER_EMAIL,
669
            [i[1] for i in settings.ADMINS],
670
            _(PROJECT_CREATION_SUBJECT) % application.__dict__,
671
            template='im/projects/project_creation_notification.txt',
672
            dictionary={'object':application})
673
        notification.send()
674
    except NotificationError, e:
675
        logger.error(e)
676
    return application.id
677

    
678
def update_application(app_id, **kw):
679
    app = ProjectApplication.objects.get(id=app_id)
680
    app.id = None
681
    app.state = app.PENDING
682
    app.precursor_application_id = app_id
683
    app.issue_date = datetime.now()
684

    
685
    resource_policies = kw.pop('resource_policies', None)
686
    for k, v in kw.iteritems():
687
        setattr(app, k, v)
688
    app.save()
689
    app.resource_policies = resource_policies
690
    return app.id
691

    
692
def approve_application(app):
693

    
694
    app_id = app if isinstance(app, int) else app.id
695

    
696
    try:
697
        objects = ProjectApplication.objects.select_for_update()
698
        application = objects.get(id=app_id)
699
    except ProjectApplication.DoesNotExist:
700
        raise PermissionDenied()
701

    
702
    application.approve()
703
    trigger_sync()
704

    
705
    try:
706
        notification = build_notification(
707
            settings.SERVER_EMAIL,
708
            [application.owner.email],
709
            _(PROJECT_APPROVED_SUBJECT) % application.__dict__,
710
            template='im/projects/project_approval_notification.txt',
711
            dictionary={'object':application})
712
        notification.send()
713
    except NotificationError, e:
714
        logger.error(e.message)
715

    
716
def terminate(project_id):
717
    project = get_project_for_update(project_id)
718

    
719
    if not project.is_alive:
720
        m = _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__
721
        raise PermissionDenied(m)
722

    
723
    project.terminate()
724
    trigger_sync()
725

    
726
    try:
727
        notification = build_notification(
728
            settings.SERVER_EMAIL,
729
            [project.application.owner.email],
730
            _(PROJECT_TERMINATION_SUBJECT) % project.__dict__,
731
            template='im/projects/project_termination_notification.txt',
732
            dictionary={'object':project.application}
733
        ).send()
734
    except NotificationError, e:
735
        logger.error(e.message)
736

    
737
def suspend(project_id):
738
    project = get_project_by_id(project_id)
739
    project.last_approval_date = None
740
    project.save()
741
    trigger_sync()
742

    
743
    try:
744
        notification = build_notification(
745
            settings.SERVER_EMAIL,
746
            [project.application.owner.email],
747
            _(PROJECT_SUSPENSION_SUBJECT) % project.__dict__,
748
            template='im/projects/project_suspension_notification.txt',
749
            dictionary={'object':project.application}
750
        ).send()
751
    except NotificationError, e:
752
        logger.error(e.message)