Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / functions.py @ 272cf735

History | View | Annotate | Download (24.6 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
def get_join_policy(str_policy):
371
    return PROJECT_MEMBER_JOIN_POLICIES.get(str_policy)
372

    
373
def get_leave_policy(str_policy):
374
    return PROJECT_MEMBER_LEAVE_POLICIES.get(str_policy)
375

    
376
_auto_accept_join = None
377
def get_auto_accept_join_policy():
378
    global _auto_accept_join
379
    if _auto_accept_join is not None:
380
        return _auto_accept_join
381
    _auto_accept = get_join_policy('auto_accept')
382
    return _auto_accept
383

    
384
_closed_join = None
385
def get_closed_join_policy():
386
    global _closed_join
387
    if _closed_join is not None:
388
        return _closed_join
389
    _closed_join = get_join_policy('closed')
390
    return _closed_join
391

    
392
_auto_accept_leave = None
393
def get_auto_accept_leave_policy():
394
    global _auto_accept_leave
395
    if _auto_accept_leave is not None:
396
        return _auto_accept_leave
397
    _auto_accept_leave = get_leave_policy('auto_accept')
398
    return _auto_accept_leave
399

    
400
_closed_leave = None
401
def get_closed_leave_policy():
402
    global _closed_leave
403
    if _closed_leave is not None:
404
        return _closed_leave
405
    _closed_leave = get_leave_policy('closed')
406
    return _closed_leave
407

    
408
def get_project_by_application_id(project_application_id):
409
    try:
410
        return Project.objects.get(application__id=project_application_id)
411
    except Project.DoesNotExist:
412
        raise IOError(
413
            _(astakos_messages.UNKNOWN_PROJECT_APPLICATION_ID) % project_application_id)
414

    
415
def get_project_id_of_application_id(project_application_id):
416
    try:
417
        return Project.objects.get(application__id=project_application_id).id
418
    except Project.DoesNotExist:
419
        raise IOError(
420
            _(astakos_messages.UNKNOWN_PROJECT_APPLICATION_ID) % project_application_id)
421

    
422
def get_project_by_id(project_id):
423
    try:
424
        return Project.objects.get(id=project_id)
425
    except Project.DoesNotExist:
426
        raise IOError(
427
            _(astakos_messages.UNKNOWN_PROJECT_ID) % project_id)
428

    
429
def get_project_for_update(project_id):
430
    try:
431
        return Project.objects.select_for_update().get(id=project_id)
432
    except Project.DoesNotExist:
433
        raise IOError(
434
            _(astakos_messages.UNKNOWN_PROJECT_ID) % project_id)
435

    
436
def get_user_by_id(user_id):
437
    try:
438
        return AstakosUser.objects.get(id=user_id)
439
    except AstakosUser.DoesNotExist:
440
        raise IOError(_(astakos_messages.UNKNOWN_USER_ID) % user_id)
441

    
442
def create_membership(project, user):
443
    if isinstance(project, int):
444
        project = get_project_by_id(project)
445
    if isinstance(user, int):
446
        user = get_user_by_id(user)
447
    m = ProjectMembership(
448
        project=project,
449
        person=user,
450
        request_date=datetime.now())
451
    try:
452
        m.save()
453
    except IntegrityError, e:
454
        raise IOError(_(astakos_messages.MEMBERSHIP_REQUEST_EXISTS))
455
    else:
456
        return m
457

    
458
def get_membership_for_update(project, user):
459
    if isinstance(project, int):
460
        project = get_project_by_id(project)
461
    if isinstance(user, int):
462
        user = get_user_by_id(user)
463
    try:
464
        return ProjectMembership.objects.select_for_update().get(
465
            project=project,
466
            person=user)
467
    except ProjectMembership.DoesNotExist:
468
        raise IOError(_(astakos_messages.NOT_MEMBERSHIP_REQUEST))
469

    
470
def accept_membership(project_application_id, user, request_user=None):
471
    """
472
        Raises:
473
            django.core.exceptions.PermissionDenied
474
            IOError
475
    """
476
    project_id = get_project_id_of_application_id(project_application_id)
477
    return do_accept_membership(project_id, user, request_user)
478

    
479
def do_accept_membership(project_id, user, request_user=None):
480
    project = get_project_for_update(project_id)
481

    
482
    if request_user and \
483
        (not project.application.owner == request_user and \
484
            not request_user.is_superuser):
485
        raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
486
    if not project.is_alive:
487
        raise PermissionDenied(
488
            _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
489
    if project.violates_members_limit(adding=1):
490
        raise PermissionDenied(_(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED))
491

    
492
    membership = get_membership_for_update(project, user)
493
    membership.accept()
494
    trigger_sync()
495

    
496
    try:
497
        notification = build_notification(
498
            settings.SERVER_EMAIL,
499
            [membership.person.email],
500
            _(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % project.__dict__,
501
            template='im/projects/project_membership_change_notification.txt',
502
            dictionary={'object':project.application, 'action':'accepted'})
503
        notification.send()
504
    except NotificationError, e:
505
        logger.error(e.message)
506
    return membership
507

    
508
def reject_membership(project_application_id, user, request_user=None):
509
    """
510
        Raises:
511
            django.core.exceptions.PermissionDenied
512
            IOError
513
    """
514
    project_id = get_project_id_of_application_id(project_application_id)
515
    return do_reject_membership(project_id, user, request_user)
516

    
517
def do_reject_membership(project_id, user, request_user=None):
518
    project = get_project_for_update(project_id)
519

    
520
    if request_user and \
521
        (not project.application.owner == request_user and \
522
            not request_user.is_superuser):
523
        raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
524
    if not project.is_alive:
525
        raise PermissionDenied(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
526

    
527
    membership = get_membership_for_update(project, user)
528
    membership.reject()
529

    
530
    try:
531
        notification = build_notification(
532
            settings.SERVER_EMAIL,
533
            [membership.person.email],
534
            _(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % project.__dict__,
535
            template='im/projects/project_membership_change_notification.txt',
536
            dictionary={'object':project.application, 'action':'rejected'})
537
        notification.send()
538
    except NotificationError, e:
539
        logger.error(e.message)
540
    return membership
541

    
542
def remove_membership(project_application_id, user, request_user=None):
543
    """
544
        Raises:
545
            django.core.exceptions.PermissionDenied
546
            IOError
547
    """
548
    project_id = get_project_id_of_application_id(project_application_id)
549
    return do_remove_membership(project_id, user, request_user)
550

    
551
def do_remove_membership(project_id, user, request_user=None):
552
    project = get_project_for_update(project_id)
553

    
554
    if request_user and \
555
        (not project.application.owner == request_user and \
556
            not request_user.is_superuser):
557
        raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
558
    if not project.is_alive:
559
        raise PermissionDenied(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
560

    
561
    membership = get_membership_for_update(project, user)
562
    membership.remove()
563
    trigger_sync()
564

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

    
577
def enroll_member(project_application_id, user, request_user=None):
578
    project_id = get_project_id_of_application_id(project_application_id)
579
    return do_enroll_member(project_id, user, request_user)
580

    
581
def do_enroll_member(project_id, user, request_user=None):
582
    membership = create_membership(project_id, user)
583
    return do_accept_membership(project_id, user, request_user)
584

    
585
def leave_project(project_application_id, user_id):
586
    """
587
        Raises:
588
            django.core.exceptions.PermissionDenied
589
            IOError
590
    """
591
    project_id = get_project_id_of_application_id(project_application_id)
592
    return do_leave_project(project_id, user_id)
593

    
594
def do_leave_project(project_id, user_id):
595
    project = get_project_for_update(project_id)
596

    
597
    if not project.is_alive:
598
        m = _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__
599
        raise PermissionDenied(m)
600

    
601
    leave_policy = project.application.member_leave_policy
602
    if leave_policy == get_closed_leave_policy():
603
        raise PermissionDenied(_(astakos_messages.MEMBER_LEAVE_POLICY_CLOSED))
604

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

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

    
623
def do_join_project(project_id, user_id):
624
    project = get_project_for_update(project_id)
625

    
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 == get_closed_join_policy():
632
        raise PermissionDenied(_(astakos_messages.MEMBER_JOIN_POLICY_CLOSED))
633

    
634
    membership = create_membership(project, user_id)
635

    
636
    if (join_policy == get_auto_accept_join_policy() and
637
        not project.violates_members_limit(adding=1)):
638
        membership.accept()
639
        trigger_sync()
640
    return membership
641

    
642
def submit_application(
643
    application, resource_policies, applicant, comments, precursor_application=None):
644

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

    
660
def update_application(app_id, **kw):
661
    app = ProjectApplication.objects.get(id=app_id)
662
    app.id = None
663
    app.state = app.PENDING
664
    app.precursor_application_id = app_id
665
    app.issue_date = datetime.now()
666

    
667
    resource_policies = kw.pop('resource_policies', None)
668
    for k, v in kw:
669
        setattr(app, k, v)
670
    app.save()
671
    app.resource_policies = resource_policies
672
    return app.id
673

    
674
def approve_application(app):
675

    
676
    app_id = app if isinstance(app, int) else app.id
677

    
678
    try:
679
        objects = ProjectApplication.objects.select_for_update()
680
        application = objects.get(id=app_id)
681
    except ProjectApplication.DoesNotExist:
682
        raise PermissionDenied()
683

    
684
    application.approve()
685
    trigger_sync()
686

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

    
698
def terminate(project_id):
699
    project = get_project_for_update(project_id)
700

    
701
    if not project.is_alive:
702
        m = _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__
703
        raise PermissionDenied(m)
704

    
705
    project.terminate()
706
    trigger_sync()
707

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

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