Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (24.8 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

    
71
import astakos.im.messages as astakos_messages
72

    
73
logger = logging.getLogger(__name__)
74

    
75

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

    
92

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

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

    
104

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

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

    
130

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

    
136

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

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

    
161

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

    
167

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

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

    
192

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

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

    
219

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

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

    
243

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

    
261

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

    
279

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

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

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

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

    
321

    
322
class SendMailError(Exception):
323
    pass
324

    
325

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

    
331

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

    
337

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

    
343

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

    
349

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

    
355

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

    
361

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

    
367

    
368
### PROJECT VIEWS ###
369

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

    
374
POLICIES = [ AUTO_ACCEPT_POLICY, MODERATED_POLICY, CLOSED_POLICY ]
375

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
508
    if not bypass_checks:
509
        do_reject_membership_checks(project, request_user)
510
    
511
    membership = get_membership_for_update(project, user)
512
    membership.reject()
513

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
601
    if not bypass_checks:
602
        do_leave_project_checks(projetc)
603

    
604
    membership = get_membership_for_update(project, user_id)
605
    if leave_policy == AUTO_ACCEPT_POLICY:
606
        membership.remove()
607
        trigger_sync()
608
    else:
609
        membership.leave_request_date = datetime.now()
610
        membership.save()
611
    return membership
612

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

    
622
def do_join_project_checks(project):
623
    if not project.is_alive:
624
        m = _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__
625
        raise PermissionDenied(m)
626

    
627
    join_policy = project.application.member_join_policy
628
    if join_policy == CLOSED_POLICY:
629
        raise PermissionDenied(_(astakos_messages.MEMBER_JOIN_POLICY_CLOSED))
630

    
631
def do_join_project(project_id, user_id, bypass_checks=False):
632
    project = get_project_for_update(project_id)
633

    
634
    if not bypass_checks:
635
        do_join_project_checks(project)
636

    
637
    membership = create_membership(project, user_id)
638

    
639
    if (join_policy == AUTO_ACCEPT_POLICY and
640
        not project.violates_members_limit(adding=1)):
641
        membership.accept()
642
        trigger_sync()
643
    return membership
644

    
645
def submit_application(
646
        application, resource_policies, applicant, comments,
647
        precursor_application=None):
648

    
649
    application.submit(
650
        resource_policies, applicant, comments, precursor_application)
651
    
652
    try:
653
        notification = build_notification(
654
            settings.SERVER_EMAIL,
655
            [i[1] for i in settings.ADMINS],
656
            _(PROJECT_CREATION_SUBJECT) % application.__dict__,
657
            template='im/projects/project_creation_notification.txt',
658
            dictionary={'object':application})
659
        notification.send()
660
    except NotificationError, e:
661
        logger.error(e)
662
    return application
663

    
664
def update_application(app_id, **kw):
665
    app = ProjectApplication.objects.get(id=app_id)
666
    app.id = None
667
    app.state = app.PENDING
668
    app.precursor_application_id = app_id
669
    app.issue_date = datetime.now()
670

    
671
    resource_policies = kw.pop('resource_policies', None)
672
    for k, v in kw.iteritems():
673
        setattr(app, k, v)
674
    app.save()
675
    app.resource_policies = resource_policies
676
    return app.id
677

    
678
def approve_application(app):
679

    
680
    app_id = app if isinstance(app, int) else app.id
681

    
682
    try:
683
        objects = ProjectApplication.objects.select_for_update()
684
        application = objects.get(id=app_id)
685
    except ProjectApplication.DoesNotExist:
686
        raise PermissionDenied()
687

    
688
    application.approve()
689
    trigger_sync()
690

    
691
    try:
692
        notification = build_notification(
693
            settings.SERVER_EMAIL,
694
            [application.owner.email],
695
            _(PROJECT_APPROVED_SUBJECT) % application.__dict__,
696
            template='im/projects/project_approval_notification.txt',
697
            dictionary={'object':application})
698
        notification.send()
699
    except NotificationError, e:
700
        logger.error(e.message)
701

    
702
def terminate(project_id):
703
    project = get_project_for_update(project_id)
704

    
705
    if not project.is_alive:
706
        m = _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__
707
        raise PermissionDenied(m)
708

    
709
    project.terminate()
710
    trigger_sync()
711

    
712
    try:
713
        notification = build_notification(
714
            settings.SERVER_EMAIL,
715
            [project.application.owner.email],
716
            _(PROJECT_TERMINATION_SUBJECT) % project.__dict__,
717
            template='im/projects/project_termination_notification.txt',
718
            dictionary={'object':project.application}
719
        ).send()
720
    except NotificationError, e:
721
        logger.error(e.message)
722

    
723
def suspend(project_id):
724
    project = get_project_by_id(project_id)
725
    project.last_approval_date = None
726
    project.save()
727
    trigger_sync()
728
    
729
    try:
730
        notification = build_notification(
731
            settings.SERVER_EMAIL,
732
            [project.application.owner.email],
733
            _(PROJECT_SUSPENSION_SUBJECT) % project.__dict__,
734
            template='im/projects/project_suspension_notification.txt',
735
            dictionary={'object':project.application}
736
        ).send()
737
    except NotificationError, e:
738
        logger.error(e.message)