Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (30.3 kB)

1 abd8467c Giorgos Korfiatis
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
2 5ce3ce4f Sofia Papagiannaki
#
3 f36af44a Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 f36af44a Antony Chazapis
# without modification, are permitted provided that the following
5 f36af44a Antony Chazapis
# conditions are met:
6 5ce3ce4f Sofia Papagiannaki
#
7 f36af44a Antony Chazapis
#   1. Redistributions of source code must retain the above
8 f36af44a Antony Chazapis
#      copyright notice, this list of conditions and the following
9 f36af44a Antony Chazapis
#      disclaimer.
10 5ce3ce4f Sofia Papagiannaki
#
11 f36af44a Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 f36af44a Antony Chazapis
#      copyright notice, this list of conditions and the following
13 f36af44a Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 f36af44a Antony Chazapis
#      provided with the distribution.
15 5ce3ce4f Sofia Papagiannaki
#
16 f36af44a Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 f36af44a Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 f36af44a Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 f36af44a Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 f36af44a Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 f36af44a Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 f36af44a Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 f36af44a Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 f36af44a Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 f36af44a Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 f36af44a Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 f36af44a Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 5ce3ce4f Sofia Papagiannaki
#
29 f36af44a Antony Chazapis
# The views and conclusions contained in the software and
30 f36af44a Antony Chazapis
# documentation are those of the authors and should not be
31 f36af44a Antony Chazapis
# interpreted as representing official policies, either expressed
32 f36af44a Antony Chazapis
# or implied, of GRNET S.A.
33 f36af44a Antony Chazapis
34 f36af44a Antony Chazapis
import logging
35 8f5a3a06 Sofia Papagiannaki
import socket
36 f36af44a Antony Chazapis
37 f36af44a Antony Chazapis
from django.utils.translation import ugettext as _
38 f36af44a Antony Chazapis
from django.template.loader import render_to_string
39 f9224cc0 Sofia Papagiannaki
from django.core.mail import send_mail, get_connection
40 f36af44a Antony Chazapis
from django.core.urlresolvers import reverse
41 49790d9d Sofia Papagiannaki
from django.template import Context, loader
42 bf0c6de5 Sofia Papagiannaki
from django.contrib.auth import (
43 bf0c6de5 Sofia Papagiannaki
    login as auth_login,
44 73fbaec4 Sofia Papagiannaki
    logout as auth_logout)
45 9a06d96f Olga Brani
from django.contrib.auth.models import AnonymousUser
46 73fbaec4 Sofia Papagiannaki
from django.core.exceptions import PermissionDenied
47 974ee6a6 Sofia Papagiannaki
from django.db import IntegrityError
48 ff67242a Giorgos Korfiatis
from django.http import Http404
49 27e26a41 Sofia Papagiannaki
50 683cf244 Sofia Papagiannaki
from urllib import quote
51 f36af44a Antony Chazapis
from urlparse import urljoin
52 8f5a3a06 Sofia Papagiannaki
from smtplib import SMTPException
53 751d24cf Sofia Papagiannaki
from datetime import datetime
54 111f3da6 Sofia Papagiannaki
from functools import wraps
55 f36af44a Antony Chazapis
56 bf0c6de5 Sofia Papagiannaki
from astakos.im.settings import (
57 31bc3a62 Kostas Papadimitriou
    CONTACT_EMAIL, SITENAME, BASEURL, LOGGING_LEVEL,
58 c0b26605 Sofia Papagiannaki
    VERIFICATION_EMAIL_SUBJECT, ACCOUNT_CREATION_SUBJECT,
59 c0b26605 Sofia Papagiannaki
    GROUP_CREATION_SUBJECT, HELPDESK_NOTIFICATION_EMAIL_SUBJECT,
60 c0b26605 Sofia Papagiannaki
    INVITATION_EMAIL_SUBJECT, GREETING_EMAIL_SUBJECT, FEEDBACK_EMAIL_SUBJECT,
61 73fbaec4 Sofia Papagiannaki
    EMAIL_CHANGE_EMAIL_SUBJECT,
62 73fbaec4 Sofia Papagiannaki
    PROJECT_CREATION_SUBJECT, PROJECT_APPROVED_SUBJECT,
63 73fbaec4 Sofia Papagiannaki
    PROJECT_TERMINATION_SUBJECT, PROJECT_SUSPENSION_SUBJECT,
64 272cf735 Sofia Papagiannaki
    PROJECT_MEMBERSHIP_CHANGE_SUBJECT,
65 0a7a4104 Kostas Papadimitriou
    PROJECT_MEMBER_JOIN_POLICIES, PROJECT_MEMBER_LEAVE_POLICIES)
66 73fbaec4 Sofia Papagiannaki
from astakos.im.notifications import build_notification, NotificationError
67 73fbaec4 Sofia Papagiannaki
from astakos.im.models import (
68 e30537f1 Sofia Papagiannaki
    AstakosUser, Invitation, ProjectMembership, ProjectApplication, Project,
69 c7c0ec58 Giorgos Korfiatis
    UserSetting,
70 aa27f246 Giorgos Korfiatis
    get_resource_names, new_chain)
71 9770ba6c Giorgos Korfiatis
from astakos.im.quotas import (qh_sync_user, qh_sync_users,
72 9770ba6c Giorgos Korfiatis
                               register_pending_apps, resolve_pending_serial)
73 a3eb3a95 Giorgos Korfiatis
from astakos.im.project_notif import (
74 466cc12c Giorgos Korfiatis
    membership_change_notify, membership_enroll_notify,
75 bb6a4465 Giorgos Korfiatis
    membership_request_notify, membership_leave_request_notify,
76 a3eb3a95 Giorgos Korfiatis
    application_submit_notify, application_approve_notify,
77 19eb3ee6 Giorgos Korfiatis
    application_deny_notify,
78 a3eb3a95 Giorgos Korfiatis
    project_termination_notify, project_suspension_notify)
79 0a7a4104 Kostas Papadimitriou
from astakos.im import settings
80 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
81 ec5e00aa Giorgos Korfiatis
from astakos.quotaholder_app.exception import NoCapacityError
82 f36af44a Antony Chazapis
83 f36af44a Antony Chazapis
logger = logging.getLogger(__name__)
84 f36af44a Antony Chazapis
85 5ce3ce4f Sofia Papagiannaki
86 bf0c6de5 Sofia Papagiannaki
def login(request, user):
87 bf0c6de5 Sofia Papagiannaki
    auth_login(request, user)
88 c3c2212a Sofia Papagiannaki
    from astakos.im.models import SessionCatalog
89 c3c2212a Sofia Papagiannaki
    SessionCatalog(
90 c0b26605 Sofia Papagiannaki
        session_key=request.session.session_key,
91 c3c2212a Sofia Papagiannaki
        user=user
92 c3c2212a Sofia Papagiannaki
    ).save()
93 5df4c364 Kostas Papadimitriou
    logger.info('%s logged in.', user.log_display)
94 5df4c364 Kostas Papadimitriou
95 bf0c6de5 Sofia Papagiannaki
96 5df4c364 Kostas Papadimitriou
def logout(request, *args, **kwargs):
97 5df4c364 Kostas Papadimitriou
    user = request.user
98 5df4c364 Kostas Papadimitriou
    auth_logout(request, *args, **kwargs)
99 5df4c364 Kostas Papadimitriou
    logger.info('%s logged out.', user.log_display)
100 111f3da6 Sofia Papagiannaki
101 5ce3ce4f Sofia Papagiannaki
102 683cf244 Sofia Papagiannaki
def send_verification(user, template_name='im/activation_email.txt'):
103 f36af44a Antony Chazapis
    """
104 683cf244 Sofia Papagiannaki
    Send email to user to verify his/her email and activate his/her account.
105 5ce3ce4f Sofia Papagiannaki

106 8f5a3a06 Sofia Papagiannaki
    Raises SendVerificationError
107 f36af44a Antony Chazapis
    """
108 6ff7a7ca Sofia Papagiannaki
    url = '%s?auth=%s&next=%s' % (urljoin(BASEURL, reverse('activate')),
109 5ce3ce4f Sofia Papagiannaki
                                  quote(user.auth_token),
110 5ce3ce4f Sofia Papagiannaki
                                  quote(urljoin(BASEURL, reverse('index'))))
111 683cf244 Sofia Papagiannaki
    message = render_to_string(template_name, {
112 5ce3ce4f Sofia Papagiannaki
                               'user': user,
113 5ce3ce4f Sofia Papagiannaki
                               'url': url,
114 5ce3ce4f Sofia Papagiannaki
                               'baseurl': BASEURL,
115 5ce3ce4f Sofia Papagiannaki
                               'site_name': SITENAME,
116 31bc3a62 Kostas Papadimitriou
                               'support': CONTACT_EMAIL})
117 1cbce16f Sofia Papagiannaki
    sender = settings.SERVER_EMAIL
118 8f5a3a06 Sofia Papagiannaki
    try:
119 f9224cc0 Sofia Papagiannaki
        send_mail(_(VERIFICATION_EMAIL_SUBJECT), message, sender, [user.email],
120 f9224cc0 Sofia Papagiannaki
                  connection=get_connection())
121 f9224cc0 Sofia Papagiannaki
122 8f5a3a06 Sofia Papagiannaki
    except (SMTPException, socket.error) as e:
123 8f5a3a06 Sofia Papagiannaki
        logger.exception(e)
124 8f5a3a06 Sofia Papagiannaki
        raise SendVerificationError()
125 8f5a3a06 Sofia Papagiannaki
    else:
126 111f3da6 Sofia Papagiannaki
        msg = 'Sent activation %s' % user.email
127 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
128 683cf244 Sofia Papagiannaki
129 5ce3ce4f Sofia Papagiannaki
130 751d24cf Sofia Papagiannaki
def send_activation(user, template_name='im/activation_email.txt'):
131 751d24cf Sofia Papagiannaki
    send_verification(user, template_name)
132 751d24cf Sofia Papagiannaki
    user.activation_sent = datetime.now()
133 751d24cf Sofia Papagiannaki
    user.save()
134 683cf244 Sofia Papagiannaki
135 5ce3ce4f Sofia Papagiannaki
136 9a06d96f Olga Brani
def _send_admin_notification(template_name,
137 9a06d96f Olga Brani
                             dictionary=None,
138 9a06d96f Olga Brani
                             subject='alpha2 testing notification',):
139 683cf244 Sofia Papagiannaki
    """
140 31bc3a62 Kostas Papadimitriou
    Send notification email to settings.HELPDESK + settings.MANAGERS.
141 5ce3ce4f Sofia Papagiannaki

142 8f5a3a06 Sofia Papagiannaki
    Raises SendNotificationError
143 683cf244 Sofia Papagiannaki
    """
144 aab4d540 Sofia Papagiannaki
    dictionary = dictionary or {}
145 3abf6c78 Sofia Papagiannaki
    message = render_to_string(template_name, dictionary)
146 1cbce16f Sofia Papagiannaki
    sender = settings.SERVER_EMAIL
147 0a7a4104 Kostas Papadimitriou
    recipient_list = [e[1] for e in settings.HELPDESK + settings.MANAGERS]
148 8f5a3a06 Sofia Papagiannaki
    try:
149 31bc3a62 Kostas Papadimitriou
        send_mail(subject, message, sender, recipient_list,
150 f9224cc0 Sofia Papagiannaki
                  connection=get_connection())
151 8f5a3a06 Sofia Papagiannaki
    except (SMTPException, socket.error) as e:
152 8f5a3a06 Sofia Papagiannaki
        logger.exception(e)
153 8f5a3a06 Sofia Papagiannaki
        raise SendNotificationError()
154 8f5a3a06 Sofia Papagiannaki
    else:
155 5d5ce247 Kostas Papadimitriou
        user = dictionary.get('user')
156 5d5ce247 Kostas Papadimitriou
        msg = 'Sent admin notification for user %s' % user.log_display
157 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
158 683cf244 Sofia Papagiannaki
159 5ce3ce4f Sofia Papagiannaki
160 9a06d96f Olga Brani
def send_account_creation_notification(template_name, dictionary=None):
161 5d5ce247 Kostas Papadimitriou
    user = dictionary.get('user')
162 5d5ce247 Kostas Papadimitriou
    subject = _(ACCOUNT_CREATION_SUBJECT) % {'user': user.email}
163 9a06d96f Olga Brani
    return _send_admin_notification(template_name, dictionary, subject=subject)
164 9a06d96f Olga Brani
165 9a06d96f Olga Brani
166 f9aea9c8 Sofia Papagiannaki
def send_helpdesk_notification(user, template_name='im/helpdesk_notification.txt'):
167 a0be6a0c Sofia Papagiannaki
    """
168 31bc3a62 Kostas Papadimitriou
    Send email to settings.HELPDESK list to notify for a new user activation.
169 5ce3ce4f Sofia Papagiannaki

170 a0be6a0c Sofia Papagiannaki
    Raises SendNotificationError
171 a0be6a0c Sofia Papagiannaki
    """
172 3abf6c78 Sofia Papagiannaki
    message = render_to_string(
173 3abf6c78 Sofia Papagiannaki
        template_name,
174 3abf6c78 Sofia Papagiannaki
        {'user': user}
175 3abf6c78 Sofia Papagiannaki
    )
176 1cbce16f Sofia Papagiannaki
    sender = settings.SERVER_EMAIL
177 0a7a4104 Kostas Papadimitriou
    recipient_list = [e[1] for e in settings.HELPDESK + settings.MANAGERS]
178 a0be6a0c Sofia Papagiannaki
    try:
179 f9224cc0 Sofia Papagiannaki
        send_mail(_(HELPDESK_NOTIFICATION_EMAIL_SUBJECT) % {'user': user.email},
180 31bc3a62 Kostas Papadimitriou
                  message, sender, recipient_list, connection=get_connection())
181 a0be6a0c Sofia Papagiannaki
    except (SMTPException, socket.error) as e:
182 a0be6a0c Sofia Papagiannaki
        logger.exception(e)
183 a0be6a0c Sofia Papagiannaki
        raise SendNotificationError()
184 a0be6a0c Sofia Papagiannaki
    else:
185 3abf6c78 Sofia Papagiannaki
        msg = 'Sent helpdesk admin notification for %s' % user.email
186 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
187 a0be6a0c Sofia Papagiannaki
188 5ce3ce4f Sofia Papagiannaki
189 683cf244 Sofia Papagiannaki
def send_invitation(invitation, template_name='im/invitation.txt'):
190 683cf244 Sofia Papagiannaki
    """
191 683cf244 Sofia Papagiannaki
    Send invitation email.
192 5ce3ce4f Sofia Papagiannaki

193 8f5a3a06 Sofia Papagiannaki
    Raises SendInvitationError
194 683cf244 Sofia Papagiannaki
    """
195 1fcf4a99 Kostas Papadimitriou
    subject = _(INVITATION_EMAIL_SUBJECT)
196 6ff7a7ca Sofia Papagiannaki
    url = '%s?code=%d' % (urljoin(BASEURL, reverse('index')), invitation.code)
197 aab4d540 Sofia Papagiannaki
    message = render_to_string(template_name, {
198 5ce3ce4f Sofia Papagiannaki
                               'invitation': invitation,
199 5ce3ce4f Sofia Papagiannaki
                               'url': url,
200 5ce3ce4f Sofia Papagiannaki
                               'baseurl': BASEURL,
201 5ce3ce4f Sofia Papagiannaki
                               'site_name': SITENAME,
202 31bc3a62 Kostas Papadimitriou
                               'support': CONTACT_EMAIL})
203 1cbce16f Sofia Papagiannaki
    sender = settings.SERVER_EMAIL
204 8f5a3a06 Sofia Papagiannaki
    try:
205 f9224cc0 Sofia Papagiannaki
        send_mail(subject, message, sender, [invitation.username],
206 f9224cc0 Sofia Papagiannaki
                  connection=get_connection())
207 8f5a3a06 Sofia Papagiannaki
    except (SMTPException, socket.error) as e:
208 8f5a3a06 Sofia Papagiannaki
        logger.exception(e)
209 8f5a3a06 Sofia Papagiannaki
        raise SendInvitationError()
210 8f5a3a06 Sofia Papagiannaki
    else:
211 111f3da6 Sofia Papagiannaki
        msg = 'Sent invitation %s' % invitation
212 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
213 f9224cc0 Sofia Papagiannaki
        inviter_invitations = invitation.inviter.invitations
214 f9224cc0 Sofia Papagiannaki
        invitation.inviter.invitations = max(0, inviter_invitations - 1)
215 9a06d96f Olga Brani
        invitation.inviter.save()
216 683cf244 Sofia Papagiannaki
217 5ce3ce4f Sofia Papagiannaki
218 683cf244 Sofia Papagiannaki
def send_greeting(user, email_template_name='im/welcome_email.txt'):
219 683cf244 Sofia Papagiannaki
    """
220 683cf244 Sofia Papagiannaki
    Send welcome email.
221 5ce3ce4f Sofia Papagiannaki

222 f36af44a Antony Chazapis
    Raises SMTPException, socket.error
223 f36af44a Antony Chazapis
    """
224 4f78c22c Sofia Papagiannaki
    subject = _(GREETING_EMAIL_SUBJECT)
225 f36af44a Antony Chazapis
    message = render_to_string(email_template_name, {
226 5ce3ce4f Sofia Papagiannaki
                               'user': user,
227 5ce3ce4f Sofia Papagiannaki
                               'url': urljoin(BASEURL, reverse('index')),
228 5ce3ce4f Sofia Papagiannaki
                               'baseurl': BASEURL,
229 5ce3ce4f Sofia Papagiannaki
                               'site_name': SITENAME,
230 31bc3a62 Kostas Papadimitriou
                               'support': CONTACT_EMAIL})
231 1cbce16f Sofia Papagiannaki
    sender = settings.SERVER_EMAIL
232 8f5a3a06 Sofia Papagiannaki
    try:
233 f9224cc0 Sofia Papagiannaki
        send_mail(subject, message, sender, [user.email],
234 f9224cc0 Sofia Papagiannaki
                  connection=get_connection())
235 8f5a3a06 Sofia Papagiannaki
    except (SMTPException, socket.error) as e:
236 8f5a3a06 Sofia Papagiannaki
        logger.exception(e)
237 8f5a3a06 Sofia Papagiannaki
        raise SendGreetingError()
238 8f5a3a06 Sofia Papagiannaki
    else:
239 5d5ce247 Kostas Papadimitriou
        msg = 'Sent greeting %s' % user.log_display
240 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
241 8f5a3a06 Sofia Papagiannaki
242 5ce3ce4f Sofia Papagiannaki
243 8f5a3a06 Sofia Papagiannaki
def send_feedback(msg, data, user, email_template_name='im/feedback_mail.txt'):
244 4f78c22c Sofia Papagiannaki
    subject = _(FEEDBACK_EMAIL_SUBJECT)
245 7d145e78 Sofia Papagiannaki
    from_email = settings.SERVER_EMAIL
246 0a7a4104 Kostas Papadimitriou
    recipient_list = [e[1] for e in settings.HELPDESK]
247 8f5a3a06 Sofia Papagiannaki
    content = render_to_string(email_template_name, {
248 8f5a3a06 Sofia Papagiannaki
        'message': msg,
249 8f5a3a06 Sofia Papagiannaki
        'data': data,
250 8f5a3a06 Sofia Papagiannaki
        'user': user})
251 8f5a3a06 Sofia Papagiannaki
    try:
252 f9224cc0 Sofia Papagiannaki
        send_mail(subject, content, from_email, recipient_list,
253 f9224cc0 Sofia Papagiannaki
                  connection=get_connection())
254 8f5a3a06 Sofia Papagiannaki
    except (SMTPException, socket.error) as e:
255 8f5a3a06 Sofia Papagiannaki
        logger.exception(e)
256 8f5a3a06 Sofia Papagiannaki
        raise SendFeedbackError()
257 8f5a3a06 Sofia Papagiannaki
    else:
258 5d5ce247 Kostas Papadimitriou
        msg = 'Sent feedback from %s' % user.log_display
259 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
260 f36af44a Antony Chazapis
261 5ce3ce4f Sofia Papagiannaki
262 73fbaec4 Sofia Papagiannaki
def send_change_email(
263 73fbaec4 Sofia Papagiannaki
    ec, request, email_template_name='registration/email_change_email.txt'):
264 49790d9d Sofia Papagiannaki
    try:
265 34a76cdb Kostas Papadimitriou
        url = ec.get_url()
266 49790d9d Sofia Papagiannaki
        url = request.build_absolute_uri(url)
267 49790d9d Sofia Papagiannaki
        t = loader.get_template(email_template_name)
268 9301c7f9 Sofia Papagiannaki
        c = {'url': url, 'site_name': SITENAME,
269 31bc3a62 Kostas Papadimitriou
             'support': CONTACT_EMAIL, 'ec': ec}
270 1cbce16f Sofia Papagiannaki
        from_email = settings.SERVER_EMAIL
271 f9224cc0 Sofia Papagiannaki
        send_mail(_(EMAIL_CHANGE_EMAIL_SUBJECT), t.render(Context(c)),
272 f9224cc0 Sofia Papagiannaki
                  from_email, [ec.new_email_address],
273 f9224cc0 Sofia Papagiannaki
                  connection=get_connection())
274 49790d9d Sofia Papagiannaki
    except (SMTPException, socket.error) as e:
275 49790d9d Sofia Papagiannaki
        logger.exception(e)
276 49790d9d Sofia Papagiannaki
        raise ChangeEmailError()
277 49790d9d Sofia Papagiannaki
    else:
278 5d5ce247 Kostas Papadimitriou
        msg = 'Sent change email for %s' % ec.user.log_display
279 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
280 f36af44a Antony Chazapis
281 5ce3ce4f Sofia Papagiannaki
282 f9aea9c8 Sofia Papagiannaki
def activate(
283 f9aea9c8 Sofia Papagiannaki
    user,
284 f9aea9c8 Sofia Papagiannaki
    email_template_name='im/welcome_email.txt',
285 f9aea9c8 Sofia Papagiannaki
    helpdesk_email_template_name='im/helpdesk_notification.txt',
286 73fbaec4 Sofia Papagiannaki
    verify_email=False):
287 683cf244 Sofia Papagiannaki
    """
288 683cf244 Sofia Papagiannaki
    Activates the specific user and sends email.
289 5ce3ce4f Sofia Papagiannaki

290 27e26a41 Sofia Papagiannaki
    Raises SendGreetingError, ValidationError
291 683cf244 Sofia Papagiannaki
    """
292 683cf244 Sofia Papagiannaki
    user.is_active = True
293 4bdd7e3d Kostas Papadimitriou
    user.email_verified = True
294 f1ab6639 Kostas Papadimitriou
    if not user.activation_sent:
295 f1ab6639 Kostas Papadimitriou
        user.activation_sent = datetime.now()
296 683cf244 Sofia Papagiannaki
    user.save()
297 44104cd3 Giorgos Korfiatis
    qh_sync_user(user.id)
298 23b9b72f Sofia Papagiannaki
    send_helpdesk_notification(user, helpdesk_email_template_name)
299 683cf244 Sofia Papagiannaki
    send_greeting(user, email_template_name)
300 683cf244 Sofia Papagiannaki
301 21e0fdad Giorgos Korfiatis
def deactivate(user):
302 21e0fdad Giorgos Korfiatis
    user.is_active = False
303 21e0fdad Giorgos Korfiatis
    user.save()
304 21e0fdad Giorgos Korfiatis
305 73fbaec4 Sofia Papagiannaki
def invite(inviter, email, realname):
306 73fbaec4 Sofia Papagiannaki
    inv = Invitation(inviter=inviter, username=email, realname=realname)
307 73fbaec4 Sofia Papagiannaki
    inv.save()
308 73fbaec4 Sofia Papagiannaki
    send_invitation(inv)
309 e30537f1 Sofia Papagiannaki
    inviter.invitations = max(0, inviter.invitations - 1)
310 73fbaec4 Sofia Papagiannaki
    inviter.save()
311 5ce3ce4f Sofia Papagiannaki
312 9a06d96f Olga Brani
def switch_account_to_shibboleth(user, local_user,
313 9a06d96f Olga Brani
                                 greeting_template_name='im/welcome_email.txt'):
314 9a06d96f Olga Brani
    try:
315 9a06d96f Olga Brani
        provider = user.provider
316 9a06d96f Olga Brani
    except AttributeError:
317 23b9b72f Sofia Papagiannaki
        return
318 9a06d96f Olga Brani
    else:
319 9a06d96f Olga Brani
        if not provider == 'shibboleth':
320 9a06d96f Olga Brani
            return
321 9a06d96f Olga Brani
        user.delete()
322 9a06d96f Olga Brani
        local_user.provider = 'shibboleth'
323 9a06d96f Olga Brani
        local_user.third_party_identifier = user.third_party_identifier
324 9a06d96f Olga Brani
        local_user.save()
325 9a06d96f Olga Brani
        send_greeting(local_user, greeting_template_name)
326 9a06d96f Olga Brani
        return local_user
327 59f598f1 Sofia Papagiannaki
328 5ce3ce4f Sofia Papagiannaki
329 8f5a3a06 Sofia Papagiannaki
class SendMailError(Exception):
330 18ffbee1 Sofia Papagiannaki
    pass
331 8f5a3a06 Sofia Papagiannaki
332 5ce3ce4f Sofia Papagiannaki
333 8f5a3a06 Sofia Papagiannaki
class SendAdminNotificationError(SendMailError):
334 8f5a3a06 Sofia Papagiannaki
    def __init__(self):
335 ae497612 Olga Brani
        self.message = _(astakos_messages.ADMIN_NOTIFICATION_SEND_ERR)
336 18ffbee1 Sofia Papagiannaki
        super(SendAdminNotificationError, self).__init__()
337 8f5a3a06 Sofia Papagiannaki
338 5ce3ce4f Sofia Papagiannaki
339 18ffbee1 Sofia Papagiannaki
class SendVerificationError(SendMailError):
340 8f5a3a06 Sofia Papagiannaki
    def __init__(self):
341 ae497612 Olga Brani
        self.message = _(astakos_messages.VERIFICATION_SEND_ERR)
342 18ffbee1 Sofia Papagiannaki
        super(SendVerificationError, self).__init__()
343 8f5a3a06 Sofia Papagiannaki
344 5ce3ce4f Sofia Papagiannaki
345 18ffbee1 Sofia Papagiannaki
class SendInvitationError(SendMailError):
346 8f5a3a06 Sofia Papagiannaki
    def __init__(self):
347 ae497612 Olga Brani
        self.message = _(astakos_messages.INVITATION_SEND_ERR)
348 18ffbee1 Sofia Papagiannaki
        super(SendInvitationError, self).__init__()
349 8f5a3a06 Sofia Papagiannaki
350 5ce3ce4f Sofia Papagiannaki
351 18ffbee1 Sofia Papagiannaki
class SendGreetingError(SendMailError):
352 8f5a3a06 Sofia Papagiannaki
    def __init__(self):
353 ae497612 Olga Brani
        self.message = _(astakos_messages.GREETING_SEND_ERR)
354 18ffbee1 Sofia Papagiannaki
        super(SendGreetingError, self).__init__()
355 8f5a3a06 Sofia Papagiannaki
356 5ce3ce4f Sofia Papagiannaki
357 18ffbee1 Sofia Papagiannaki
class SendFeedbackError(SendMailError):
358 8f5a3a06 Sofia Papagiannaki
    def __init__(self):
359 ae497612 Olga Brani
        self.message = _(astakos_messages.FEEDBACK_SEND_ERR)
360 49790d9d Sofia Papagiannaki
        super(SendFeedbackError, self).__init__()
361 49790d9d Sofia Papagiannaki
362 5ce3ce4f Sofia Papagiannaki
363 49790d9d Sofia Papagiannaki
class ChangeEmailError(SendMailError):
364 49790d9d Sofia Papagiannaki
    def __init__(self):
365 ccab6eb5 Sofia Papagiannaki
        self.message = _(astakos_messages.CHANGE_EMAIL_SEND_ERR)
366 35f8ccf1 Sofia Papagiannaki
        super(ChangeEmailError, self).__init__()
367 a0be6a0c Sofia Papagiannaki
368 5ce3ce4f Sofia Papagiannaki
369 a0be6a0c Sofia Papagiannaki
class SendNotificationError(SendMailError):
370 a0be6a0c Sofia Papagiannaki
    def __init__(self):
371 ae497612 Olga Brani
        self.message = _(astakos_messages.NOTIFICATION_SEND_ERR)
372 a0be6a0c Sofia Papagiannaki
        super(SendNotificationError, self).__init__()
373 73fbaec4 Sofia Papagiannaki
374 73fbaec4 Sofia Papagiannaki
375 84a3f701 Giorgos Korfiatis
def get_quota(users):
376 6cc50d6a Giorgos Korfiatis
    pass
377 0514bcc7 Giorgos Korfiatis
378 0514bcc7 Giorgos Korfiatis
379 7a08e179 Giorgos Korfiatis
### PROJECT FUNCTIONS ###
380 fcc1e93f Sofia Papagiannaki
381 b0686c16 Giorgos Korfiatis
AUTO_ACCEPT_POLICY = 1
382 7a08e179 Giorgos Korfiatis
MODERATED_POLICY = 2
383 7a08e179 Giorgos Korfiatis
CLOSED_POLICY = 3
384 7a08e179 Giorgos Korfiatis
385 7a08e179 Giorgos Korfiatis
POLICIES = [AUTO_ACCEPT_POLICY, MODERATED_POLICY, CLOSED_POLICY]
386 b0686c16 Giorgos Korfiatis
387 73fbaec4 Sofia Papagiannaki
388 73fbaec4 Sofia Papagiannaki
def get_project_by_application_id(project_application_id):
389 73fbaec4 Sofia Papagiannaki
    try:
390 73fbaec4 Sofia Papagiannaki
        return Project.objects.get(application__id=project_application_id)
391 73fbaec4 Sofia Papagiannaki
    except Project.DoesNotExist:
392 7a08e179 Giorgos Korfiatis
        m = (_(astakos_messages.UNKNOWN_PROJECT_APPLICATION_ID) %
393 7a08e179 Giorgos Korfiatis
             project_application_id)
394 7a08e179 Giorgos Korfiatis
        raise IOError(m)
395 7a08e179 Giorgos Korfiatis
396 73fbaec4 Sofia Papagiannaki
397 a75dbd7b Giorgos Korfiatis
def get_related_project_id(application_id):
398 a75dbd7b Giorgos Korfiatis
    try:
399 a75dbd7b Giorgos Korfiatis
        app = ProjectApplication.objects.get(id=application_id)
400 a75dbd7b Giorgos Korfiatis
        chain = app.chain
401 7a08e179 Giorgos Korfiatis
        Project.objects.get(id=chain)
402 a75dbd7b Giorgos Korfiatis
        return chain
403 7a08e179 Giorgos Korfiatis
    except ProjectApplication.DoesNotExist, Project.DoesNotExist:
404 a75dbd7b Giorgos Korfiatis
        return None
405 a75dbd7b Giorgos Korfiatis
406 7a08e179 Giorgos Korfiatis
407 0932ac43 Giorgos Korfiatis
def get_chain_of_application_id(application_id):
408 0932ac43 Giorgos Korfiatis
    try:
409 0932ac43 Giorgos Korfiatis
        app = ProjectApplication.objects.get(id=application_id)
410 0932ac43 Giorgos Korfiatis
        chain = app.chain
411 0932ac43 Giorgos Korfiatis
        return chain.chain
412 7a08e179 Giorgos Korfiatis
    except ProjectApplication.DoesNotExist:
413 0932ac43 Giorgos Korfiatis
        return None
414 0932ac43 Giorgos Korfiatis
415 7a08e179 Giorgos Korfiatis
416 eb9ff37a Sofia Papagiannaki
def get_project_by_id(project_id):
417 eb9ff37a Sofia Papagiannaki
    try:
418 eb9ff37a Sofia Papagiannaki
        return Project.objects.get(id=project_id)
419 eb9ff37a Sofia Papagiannaki
    except Project.DoesNotExist:
420 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.UNKNOWN_PROJECT_ID) % project_id
421 7a08e179 Giorgos Korfiatis
        raise IOError(m)
422 7a08e179 Giorgos Korfiatis
423 eb9ff37a Sofia Papagiannaki
424 c10f1cf5 Giorgos Korfiatis
def get_project_by_name(name):
425 c10f1cf5 Giorgos Korfiatis
    try:
426 c10f1cf5 Giorgos Korfiatis
        return Project.objects.get(name=name)
427 c10f1cf5 Giorgos Korfiatis
    except Project.DoesNotExist:
428 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.UNKNOWN_PROJECT_ID) % name
429 7a08e179 Giorgos Korfiatis
        raise IOError(m)
430 c10f1cf5 Giorgos Korfiatis
431 c10f1cf5 Giorgos Korfiatis
432 8dcaa6ac Giorgos Korfiatis
def get_project_for_update(project_id):
433 8dcaa6ac Giorgos Korfiatis
    try:
434 ea1e5d9f Giorgos Korfiatis
        return Project.objects.get_for_update(id=project_id)
435 8dcaa6ac Giorgos Korfiatis
    except Project.DoesNotExist:
436 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.UNKNOWN_PROJECT_ID) % project_id
437 7a08e179 Giorgos Korfiatis
        raise IOError(m)
438 7a08e179 Giorgos Korfiatis
439 8dcaa6ac Giorgos Korfiatis
440 19eb3ee6 Giorgos Korfiatis
def get_application_for_update(application_id):
441 19eb3ee6 Giorgos Korfiatis
    try:
442 ea1e5d9f Giorgos Korfiatis
        return ProjectApplication.objects.get_for_update(id=application_id)
443 19eb3ee6 Giorgos Korfiatis
    except ProjectApplication.DoesNotExist:
444 19eb3ee6 Giorgos Korfiatis
        m = _(astakos_messages.UNKNOWN_PROJECT_APPLICATION_ID) % application_id
445 19eb3ee6 Giorgos Korfiatis
        raise IOError(m)
446 19eb3ee6 Giorgos Korfiatis
447 7a08e179 Giorgos Korfiatis
448 73fbaec4 Sofia Papagiannaki
def get_user_by_id(user_id):
449 73fbaec4 Sofia Papagiannaki
    try:
450 974ee6a6 Sofia Papagiannaki
        return AstakosUser.objects.get(id=user_id)
451 73fbaec4 Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
452 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.UNKNOWN_USER_ID) % user_id
453 7a08e179 Giorgos Korfiatis
        raise IOError(m)
454 7a08e179 Giorgos Korfiatis
455 73fbaec4 Sofia Papagiannaki
456 3c049f6d Giorgos Korfiatis
def get_user_by_uuid(uuid):
457 3c049f6d Giorgos Korfiatis
    try:
458 3c049f6d Giorgos Korfiatis
        return AstakosUser.objects.get(uuid=uuid)
459 3c049f6d Giorgos Korfiatis
    except AstakosUser.DoesNotExist:
460 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.UNKNOWN_USER_ID) % uuid
461 7a08e179 Giorgos Korfiatis
        raise IOError(m)
462 3c049f6d Giorgos Korfiatis
463 97e93991 Kostas Papadimitriou
464 d07ce657 Giorgos Korfiatis
def get_membership_for_update(project_id, user_id):
465 73fbaec4 Sofia Papagiannaki
    try:
466 ea1e5d9f Giorgos Korfiatis
        objs = ProjectMembership.objects
467 d07ce657 Giorgos Korfiatis
        return objs.get_for_update(project__id=project_id,
468 d07ce657 Giorgos Korfiatis
                                   person__id=user_id)
469 73fbaec4 Sofia Papagiannaki
    except ProjectMembership.DoesNotExist:
470 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.NOT_MEMBERSHIP_REQUEST)
471 7a08e179 Giorgos Korfiatis
        raise IOError(m)
472 7a08e179 Giorgos Korfiatis
473 73fbaec4 Sofia Papagiannaki
474 d07ce657 Giorgos Korfiatis
def checkAllowed(entity, request_user, admin_only=False):
475 3c638f72 Giorgos Korfiatis
    if isinstance(entity, Project):
476 3c638f72 Giorgos Korfiatis
        application = entity.application
477 3c638f72 Giorgos Korfiatis
    elif isinstance(entity, ProjectApplication):
478 3c638f72 Giorgos Korfiatis
        application = entity
479 3c638f72 Giorgos Korfiatis
    else:
480 3c638f72 Giorgos Korfiatis
        m = "%s not a Project nor a ProjectApplication" % (entity,)
481 3c638f72 Giorgos Korfiatis
        raise ValueError(m)
482 3c638f72 Giorgos Korfiatis
483 d07ce657 Giorgos Korfiatis
    if not request_user or request_user.is_project_admin():
484 d07ce657 Giorgos Korfiatis
        return
485 d07ce657 Giorgos Korfiatis
486 d07ce657 Giorgos Korfiatis
    if not admin_only and application.owner == request_user:
487 d07ce657 Giorgos Korfiatis
        return
488 d07ce657 Giorgos Korfiatis
489 d07ce657 Giorgos Korfiatis
    m = _(astakos_messages.NOT_ALLOWED)
490 d07ce657 Giorgos Korfiatis
    raise PermissionDenied(m)
491 d07ce657 Giorgos Korfiatis
492 907f15db Giorgos Korfiatis
493 907f15db Giorgos Korfiatis
def checkAlive(project):
494 907f15db Giorgos Korfiatis
    if not project.is_alive:
495 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__
496 7a08e179 Giorgos Korfiatis
        raise PermissionDenied(m)
497 7a08e179 Giorgos Korfiatis
498 907f15db Giorgos Korfiatis
499 ff67242a Giorgos Korfiatis
def accept_membership_checks(project, request_user):
500 907f15db Giorgos Korfiatis
    checkAllowed(project, request_user)
501 907f15db Giorgos Korfiatis
    checkAlive(project)
502 b0686c16 Giorgos Korfiatis
503 b0686c16 Giorgos Korfiatis
    join_policy = project.application.member_join_policy
504 b0686c16 Giorgos Korfiatis
    if join_policy == CLOSED_POLICY:
505 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.MEMBER_JOIN_POLICY_CLOSED)
506 7a08e179 Giorgos Korfiatis
        raise PermissionDenied(m)
507 b0686c16 Giorgos Korfiatis
508 8dcaa6ac Giorgos Korfiatis
    if project.violates_members_limit(adding=1):
509 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED)
510 7a08e179 Giorgos Korfiatis
        raise PermissionDenied(m)
511 73fbaec4 Sofia Papagiannaki
512 d07ce657 Giorgos Korfiatis
513 d07ce657 Giorgos Korfiatis
def accept_membership(project_id, user_id, request_user=None):
514 e47fb17a Sofia Papagiannaki
    project = get_project_for_update(project_id)
515 ff67242a Giorgos Korfiatis
    accept_membership_checks(project, request_user)
516 14f7f6a5 Giorgos Korfiatis
517 d07ce657 Giorgos Korfiatis
    membership = get_membership_for_update(project_id, user_id)
518 14f7f6a5 Giorgos Korfiatis
    if not membership.can_accept():
519 14f7f6a5 Giorgos Korfiatis
        m = _(astakos_messages.NOT_MEMBERSHIP_REQUEST)
520 14f7f6a5 Giorgos Korfiatis
        raise PermissionDenied(m)
521 d4fc6292 Giorgos Korfiatis
522 73fbaec4 Sofia Papagiannaki
    membership.accept()
523 d07ce657 Giorgos Korfiatis
    qh_sync_user(user_id)
524 f30f0170 Giorgos Korfiatis
    logger.info("User %s has been accepted in %s." %
525 f30f0170 Giorgos Korfiatis
                (membership.person.log_display, project))
526 73fbaec4 Sofia Papagiannaki
527 a3eb3a95 Giorgos Korfiatis
    membership_change_notify(project, membership.person, 'accepted')
528 73fbaec4 Sofia Papagiannaki
    return membership
529 73fbaec4 Sofia Papagiannaki
530 7a08e179 Giorgos Korfiatis
531 ff67242a Giorgos Korfiatis
def reject_membership_checks(project, request_user):
532 907f15db Giorgos Korfiatis
    checkAllowed(project, request_user)
533 907f15db Giorgos Korfiatis
    checkAlive(project)
534 e47fb17a Sofia Papagiannaki
535 d07ce657 Giorgos Korfiatis
536 d07ce657 Giorgos Korfiatis
def reject_membership(project_id, user_id, request_user=None):
537 e47fb17a Sofia Papagiannaki
    project = get_project_for_update(project_id)
538 ff67242a Giorgos Korfiatis
    reject_membership_checks(project, request_user)
539 d07ce657 Giorgos Korfiatis
    membership = get_membership_for_update(project_id, user_id)
540 14f7f6a5 Giorgos Korfiatis
    if not membership.can_reject():
541 14f7f6a5 Giorgos Korfiatis
        m = _(astakos_messages.NOT_MEMBERSHIP_REQUEST)
542 14f7f6a5 Giorgos Korfiatis
        raise PermissionDenied(m)
543 d4fc6292 Giorgos Korfiatis
544 73fbaec4 Sofia Papagiannaki
    membership.reject()
545 f30f0170 Giorgos Korfiatis
    logger.info("Request of user %s for %s has been rejected." %
546 f30f0170 Giorgos Korfiatis
                (membership.person.log_display, project))
547 73fbaec4 Sofia Papagiannaki
548 a3eb3a95 Giorgos Korfiatis
    membership_change_notify(project, membership.person, 'rejected')
549 73fbaec4 Sofia Papagiannaki
    return membership
550 73fbaec4 Sofia Papagiannaki
551 7a08e179 Giorgos Korfiatis
552 aad0e329 Giorgos Korfiatis
def cancel_membership_checks(project):
553 aad0e329 Giorgos Korfiatis
    checkAlive(project)
554 aad0e329 Giorgos Korfiatis
555 d07ce657 Giorgos Korfiatis
556 d07ce657 Giorgos Korfiatis
def cancel_membership(project_id, request_user):
557 aad0e329 Giorgos Korfiatis
    project = get_project_for_update(project_id)
558 aad0e329 Giorgos Korfiatis
    cancel_membership_checks(project)
559 d07ce657 Giorgos Korfiatis
    membership = get_membership_for_update(project_id, request_user.id)
560 aad0e329 Giorgos Korfiatis
    if not membership.can_cancel():
561 aad0e329 Giorgos Korfiatis
        m = _(astakos_messages.NOT_MEMBERSHIP_REQUEST)
562 aad0e329 Giorgos Korfiatis
        raise PermissionDenied(m)
563 aad0e329 Giorgos Korfiatis
564 aad0e329 Giorgos Korfiatis
    membership.cancel()
565 f30f0170 Giorgos Korfiatis
    logger.info("Request of user %s for %s has been cancelled." %
566 f30f0170 Giorgos Korfiatis
                (membership.person.log_display, project))
567 aad0e329 Giorgos Korfiatis
568 7a08e179 Giorgos Korfiatis
569 ff67242a Giorgos Korfiatis
def remove_membership_checks(project, request_user=None):
570 907f15db Giorgos Korfiatis
    checkAllowed(project, request_user)
571 907f15db Giorgos Korfiatis
    checkAlive(project)
572 73fbaec4 Sofia Papagiannaki
573 b0686c16 Giorgos Korfiatis
    leave_policy = project.application.member_leave_policy
574 b0686c16 Giorgos Korfiatis
    if leave_policy == CLOSED_POLICY:
575 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.MEMBER_LEAVE_POLICY_CLOSED)
576 7a08e179 Giorgos Korfiatis
        raise PermissionDenied(m)
577 7a08e179 Giorgos Korfiatis
578 b0686c16 Giorgos Korfiatis
579 d07ce657 Giorgos Korfiatis
def remove_membership(project_id, user_id, request_user=None):
580 907f15db Giorgos Korfiatis
    project = get_project_for_update(project_id)
581 ff67242a Giorgos Korfiatis
    remove_membership_checks(project, request_user)
582 d07ce657 Giorgos Korfiatis
    membership = get_membership_for_update(project_id, user_id)
583 14f7f6a5 Giorgos Korfiatis
    if not membership.can_remove():
584 14f7f6a5 Giorgos Korfiatis
        m = _(astakos_messages.NOT_ACCEPTED_MEMBERSHIP)
585 14f7f6a5 Giorgos Korfiatis
        raise PermissionDenied(m)
586 d4fc6292 Giorgos Korfiatis
587 73fbaec4 Sofia Papagiannaki
    membership.remove()
588 d07ce657 Giorgos Korfiatis
    qh_sync_user(user_id)
589 f30f0170 Giorgos Korfiatis
    logger.info("User %s has been removed from %s." %
590 f30f0170 Giorgos Korfiatis
                (membership.person.log_display, project))
591 73fbaec4 Sofia Papagiannaki
592 a3eb3a95 Giorgos Korfiatis
    membership_change_notify(project, membership.person, 'removed')
593 73fbaec4 Sofia Papagiannaki
    return membership
594 73fbaec4 Sofia Papagiannaki
595 7a08e179 Giorgos Korfiatis
596 ff67242a Giorgos Korfiatis
def enroll_member(project_id, user, request_user=None):
597 907f15db Giorgos Korfiatis
    project = get_project_for_update(project_id)
598 ff67242a Giorgos Korfiatis
    accept_membership_checks(project, request_user)
599 e05c541e Giorgos Korfiatis
600 e05c541e Giorgos Korfiatis
    membership, created = ProjectMembership.objects.get_or_create(
601 e05c541e Giorgos Korfiatis
        project=project,
602 e05c541e Giorgos Korfiatis
        person=user)
603 14f7f6a5 Giorgos Korfiatis
604 14f7f6a5 Giorgos Korfiatis
    if not membership.can_accept():
605 14f7f6a5 Giorgos Korfiatis
        m = _(astakos_messages.NOT_MEMBERSHIP_REQUEST)
606 14f7f6a5 Giorgos Korfiatis
        raise PermissionDenied(m)
607 d4fc6292 Giorgos Korfiatis
608 907f15db Giorgos Korfiatis
    membership.accept()
609 e05c541e Giorgos Korfiatis
    qh_sync_user(user.id)
610 f30f0170 Giorgos Korfiatis
    logger.info("User %s has been enrolled in %s." %
611 f30f0170 Giorgos Korfiatis
                (membership.person.log_display, project))
612 570015d2 Giorgos Korfiatis
613 466cc12c Giorgos Korfiatis
    membership_enroll_notify(project, membership.person)
614 907f15db Giorgos Korfiatis
    return membership
615 8dcaa6ac Giorgos Korfiatis
616 7a08e179 Giorgos Korfiatis
617 ff67242a Giorgos Korfiatis
def leave_project_checks(project):
618 907f15db Giorgos Korfiatis
    checkAlive(project)
619 8dcaa6ac Giorgos Korfiatis
620 03869281 Sofia Papagiannaki
    leave_policy = project.application.member_leave_policy
621 b0686c16 Giorgos Korfiatis
    if leave_policy == CLOSED_POLICY:
622 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.MEMBER_LEAVE_POLICY_CLOSED)
623 7a08e179 Giorgos Korfiatis
        raise PermissionDenied(m)
624 7a08e179 Giorgos Korfiatis
625 73fbaec4 Sofia Papagiannaki
626 d4660e00 Giorgos Korfiatis
def can_leave_request(project, user):
627 d4660e00 Giorgos Korfiatis
    leave_policy = project.application.member_leave_policy
628 d4660e00 Giorgos Korfiatis
    if leave_policy == CLOSED_POLICY:
629 d4660e00 Giorgos Korfiatis
        return False
630 d4660e00 Giorgos Korfiatis
    m = user.get_membership(project)
631 d4660e00 Giorgos Korfiatis
    if m is None:
632 d4660e00 Giorgos Korfiatis
        return False
633 d4660e00 Giorgos Korfiatis
    if m.state != ProjectMembership.ACCEPTED:
634 d4660e00 Giorgos Korfiatis
        return False
635 d4660e00 Giorgos Korfiatis
    return True
636 d4660e00 Giorgos Korfiatis
637 d07ce657 Giorgos Korfiatis
638 d07ce657 Giorgos Korfiatis
def leave_project(project_id, request_user):
639 e47fb17a Sofia Papagiannaki
    project = get_project_for_update(project_id)
640 ff67242a Giorgos Korfiatis
    leave_project_checks(project)
641 d07ce657 Giorgos Korfiatis
    membership = get_membership_for_update(project_id, request_user.id)
642 14f7f6a5 Giorgos Korfiatis
    if not membership.can_leave():
643 14f7f6a5 Giorgos Korfiatis
        m = _(astakos_messages.NOT_ACCEPTED_MEMBERSHIP)
644 14f7f6a5 Giorgos Korfiatis
        raise PermissionDenied(m)
645 4e057833 Sofia Papagiannaki
646 bb6a4465 Giorgos Korfiatis
    auto_accepted = False
647 4e057833 Sofia Papagiannaki
    leave_policy = project.application.member_leave_policy
648 b0686c16 Giorgos Korfiatis
    if leave_policy == AUTO_ACCEPT_POLICY:
649 73fbaec4 Sofia Papagiannaki
        membership.remove()
650 d07ce657 Giorgos Korfiatis
        qh_sync_user(request_user.id)
651 f30f0170 Giorgos Korfiatis
        logger.info("User %s has left %s." %
652 f30f0170 Giorgos Korfiatis
                    (membership.person.log_display, project))
653 bb6a4465 Giorgos Korfiatis
        auto_accepted = True
654 73fbaec4 Sofia Papagiannaki
    else:
655 c1007621 Giorgos Korfiatis
        membership.leave_request()
656 f30f0170 Giorgos Korfiatis
        logger.info("User %s requested to leave %s." %
657 f30f0170 Giorgos Korfiatis
                    (membership.person.log_display, project))
658 bb6a4465 Giorgos Korfiatis
        membership_leave_request_notify(project, membership.person)
659 bb6a4465 Giorgos Korfiatis
    return auto_accepted
660 73fbaec4 Sofia Papagiannaki
661 7a08e179 Giorgos Korfiatis
662 ff67242a Giorgos Korfiatis
def join_project_checks(project):
663 907f15db Giorgos Korfiatis
    checkAlive(project)
664 8dcaa6ac Giorgos Korfiatis
665 974ee6a6 Sofia Papagiannaki
    join_policy = project.application.member_join_policy
666 b0686c16 Giorgos Korfiatis
    if join_policy == CLOSED_POLICY:
667 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.MEMBER_JOIN_POLICY_CLOSED)
668 7a08e179 Giorgos Korfiatis
        raise PermissionDenied(m)
669 7a08e179 Giorgos Korfiatis
670 73fbaec4 Sofia Papagiannaki
671 d4660e00 Giorgos Korfiatis
def can_join_request(project, user):
672 d4660e00 Giorgos Korfiatis
    join_policy = project.application.member_join_policy
673 d4660e00 Giorgos Korfiatis
    if join_policy == CLOSED_POLICY:
674 d4660e00 Giorgos Korfiatis
        return False
675 d4660e00 Giorgos Korfiatis
    m = user.get_membership(project)
676 d4660e00 Giorgos Korfiatis
    if m:
677 d4660e00 Giorgos Korfiatis
        return False
678 d4660e00 Giorgos Korfiatis
    return True
679 d4660e00 Giorgos Korfiatis
680 d07ce657 Giorgos Korfiatis
681 d07ce657 Giorgos Korfiatis
def join_project(project_id, request_user):
682 e47fb17a Sofia Papagiannaki
    project = get_project_for_update(project_id)
683 ff67242a Giorgos Korfiatis
    join_project_checks(project)
684 d07ce657 Giorgos Korfiatis
685 d07ce657 Giorgos Korfiatis
    membership, created = ProjectMembership.objects.get_or_create(
686 d07ce657 Giorgos Korfiatis
        project=project,
687 d07ce657 Giorgos Korfiatis
        person=request_user)
688 d07ce657 Giorgos Korfiatis
689 d07ce657 Giorgos Korfiatis
    if not created:
690 d07ce657 Giorgos Korfiatis
        msg = _(astakos_messages.MEMBERSHIP_REQUEST_EXISTS)
691 d07ce657 Giorgos Korfiatis
        raise PermissionDenied(msg)
692 73fbaec4 Sofia Papagiannaki
693 bb6a4465 Giorgos Korfiatis
    auto_accepted = False
694 4e057833 Sofia Papagiannaki
    join_policy = project.application.member_join_policy
695 7a08e179 Giorgos Korfiatis
    if (join_policy == AUTO_ACCEPT_POLICY and (
696 7a08e179 Giorgos Korfiatis
            not project.violates_members_limit(adding=1))):
697 73fbaec4 Sofia Papagiannaki
        membership.accept()
698 d07ce657 Giorgos Korfiatis
        qh_sync_user(request_user.id)
699 f30f0170 Giorgos Korfiatis
        logger.info("User %s joined %s." %
700 f30f0170 Giorgos Korfiatis
                    (membership.person.log_display, project))
701 bb6a4465 Giorgos Korfiatis
        auto_accepted = True
702 bb6a4465 Giorgos Korfiatis
    else:
703 bb6a4465 Giorgos Korfiatis
        membership_request_notify(project, membership.person)
704 f30f0170 Giorgos Korfiatis
        logger.info("User %s requested to join %s." %
705 f30f0170 Giorgos Korfiatis
                    (membership.person.log_display, project))
706 bb6a4465 Giorgos Korfiatis
    return auto_accepted
707 8dcaa6ac Giorgos Korfiatis
708 7a08e179 Giorgos Korfiatis
709 ee4aa6eb Giorgos Korfiatis
def submit_application(kw, request_user=None):
710 ee4aa6eb Giorgos Korfiatis
711 ee4aa6eb Giorgos Korfiatis
    kw['applicant'] = request_user
712 3e3743f2 Giorgos Korfiatis
    resource_policies = kw.pop('resource_policies', None)
713 69c822cc Giorgos Korfiatis
714 3e3743f2 Giorgos Korfiatis
    precursor = None
715 15ca2bea Giorgos Korfiatis
    precursor_id = kw.get('precursor_application', None)
716 15ca2bea Giorgos Korfiatis
    if precursor_id is not None:
717 ea1e5d9f Giorgos Korfiatis
        objs = ProjectApplication.objects
718 ea1e5d9f Giorgos Korfiatis
        precursor = objs.get_for_update(id=precursor_id)
719 15ca2bea Giorgos Korfiatis
        kw['precursor_application'] = precursor
720 73fbaec4 Sofia Papagiannaki
721 3e3743f2 Giorgos Korfiatis
        if (request_user and
722 3e3743f2 Giorgos Korfiatis
            (not precursor.owner == request_user and
723 beda5f0f Georgios D. Tsoukalas
             not request_user.is_superuser
724 beda5f0f Georgios D. Tsoukalas
             and not request_user.is_project_admin())):
725 3e3743f2 Giorgos Korfiatis
            m = _(astakos_messages.NOT_ALLOWED)
726 3e3743f2 Giorgos Korfiatis
            raise PermissionDenied(m)
727 ee4aa6eb Giorgos Korfiatis
728 db472f3d Giorgos Korfiatis
    owner = kw['owner']
729 9770ba6c Giorgos Korfiatis
    force = request_user.is_project_admin()
730 9770ba6c Giorgos Korfiatis
    ok, limit = qh_add_pending_app(owner, precursor, force)
731 9770ba6c Giorgos Korfiatis
    if not ok:
732 c7c0ec58 Giorgos Korfiatis
        m = _(astakos_messages.REACHED_PENDING_APPLICATION_LIMIT) % limit
733 c7c0ec58 Giorgos Korfiatis
        raise PermissionDenied(m)
734 c7c0ec58 Giorgos Korfiatis
735 3e3743f2 Giorgos Korfiatis
    application = ProjectApplication(**kw)
736 1352dabb Kostas Papadimitriou
737 3e3743f2 Giorgos Korfiatis
    if precursor is None:
738 3e3743f2 Giorgos Korfiatis
        application.chain = new_chain()
739 3e3743f2 Giorgos Korfiatis
    else:
740 3e3743f2 Giorgos Korfiatis
        chain = precursor.chain
741 3e3743f2 Giorgos Korfiatis
        application.chain = chain
742 ea1e5d9f Giorgos Korfiatis
        objs = ProjectApplication.objects
743 ea1e5d9f Giorgos Korfiatis
        q = objs.filter(chain=chain, state=ProjectApplication.PENDING)
744 ea1e5d9f Giorgos Korfiatis
        pending = q.select_for_update()
745 3e3743f2 Giorgos Korfiatis
        for app in pending:
746 3e3743f2 Giorgos Korfiatis
            app.state = ProjectApplication.REPLACED
747 3e3743f2 Giorgos Korfiatis
            app.save()
748 3e3743f2 Giorgos Korfiatis
749 3e3743f2 Giorgos Korfiatis
    application.save()
750 3e3743f2 Giorgos Korfiatis
    application.resource_policies = resource_policies
751 f30f0170 Giorgos Korfiatis
    logger.info("User %s submitted %s." %
752 f30f0170 Giorgos Korfiatis
                (request_user.log_display, application.log_display))
753 a3eb3a95 Giorgos Korfiatis
    application_submit_notify(application)
754 a3eb3a95 Giorgos Korfiatis
    return application
755 73fbaec4 Sofia Papagiannaki
756 7a08e179 Giorgos Korfiatis
757 3c638f72 Giorgos Korfiatis
def cancel_application(application_id, request_user=None):
758 3c638f72 Giorgos Korfiatis
    application = get_application_for_update(application_id)
759 3c638f72 Giorgos Korfiatis
    checkAllowed(application, request_user)
760 3c638f72 Giorgos Korfiatis
761 01bdbd17 Giorgos Korfiatis
    if not application.can_cancel():
762 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.APPLICATION_CANNOT_CANCEL %
763 7a08e179 Giorgos Korfiatis
              (application.id, application.state_display()))
764 01bdbd17 Giorgos Korfiatis
        raise PermissionDenied(m)
765 3c638f72 Giorgos Korfiatis
766 9770ba6c Giorgos Korfiatis
    qh_release_pending_app(application.owner)
767 9770ba6c Giorgos Korfiatis
768 3c638f72 Giorgos Korfiatis
    application.cancel()
769 f30f0170 Giorgos Korfiatis
    logger.info("%s has been cancelled." % (application.log_display))
770 3c638f72 Giorgos Korfiatis
771 7a08e179 Giorgos Korfiatis
772 3c638f72 Giorgos Korfiatis
def dismiss_application(application_id, request_user=None):
773 3c638f72 Giorgos Korfiatis
    application = get_application_for_update(application_id)
774 3c638f72 Giorgos Korfiatis
    checkAllowed(application, request_user)
775 3c638f72 Giorgos Korfiatis
776 01bdbd17 Giorgos Korfiatis
    if not application.can_dismiss():
777 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.APPLICATION_CANNOT_DISMISS %
778 7a08e179 Giorgos Korfiatis
              (application.id, application.state_display()))
779 01bdbd17 Giorgos Korfiatis
        raise PermissionDenied(m)
780 3c638f72 Giorgos Korfiatis
781 3c638f72 Giorgos Korfiatis
    application.dismiss()
782 f30f0170 Giorgos Korfiatis
    logger.info("%s has been dismissed." % (application.log_display))
783 3c638f72 Giorgos Korfiatis
784 7a08e179 Giorgos Korfiatis
785 d07ce657 Giorgos Korfiatis
def deny_application(application_id, request_user=None, reason=None):
786 19eb3ee6 Giorgos Korfiatis
    application = get_application_for_update(application_id)
787 01bdbd17 Giorgos Korfiatis
788 d07ce657 Giorgos Korfiatis
    checkAllowed(application, request_user, admin_only=True)
789 d07ce657 Giorgos Korfiatis
790 01bdbd17 Giorgos Korfiatis
    if not application.can_deny():
791 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.APPLICATION_CANNOT_DENY %
792 7a08e179 Giorgos Korfiatis
              (application.id, application.state_display()))
793 01bdbd17 Giorgos Korfiatis
        raise PermissionDenied(m)
794 19eb3ee6 Giorgos Korfiatis
795 9770ba6c Giorgos Korfiatis
    qh_release_pending_app(application.owner)
796 9770ba6c Giorgos Korfiatis
797 2b745492 Giorgos Korfiatis
    if reason is None:
798 2b745492 Giorgos Korfiatis
        reason = ""
799 2b745492 Giorgos Korfiatis
    application.deny(reason)
800 f30f0170 Giorgos Korfiatis
    logger.info("%s has been denied with reason \"%s\"." %
801 f30f0170 Giorgos Korfiatis
                (application.log_display, reason))
802 19eb3ee6 Giorgos Korfiatis
    application_deny_notify(application)
803 19eb3ee6 Giorgos Korfiatis
804 7a08e179 Giorgos Korfiatis
805 d07ce657 Giorgos Korfiatis
def approve_application(app_id, request_user=None):
806 8dcaa6ac Giorgos Korfiatis
807 8dcaa6ac Giorgos Korfiatis
    try:
808 ea1e5d9f Giorgos Korfiatis
        objects = ProjectApplication.objects
809 ea1e5d9f Giorgos Korfiatis
        application = objects.get_for_update(id=app_id)
810 8dcaa6ac Giorgos Korfiatis
    except ProjectApplication.DoesNotExist:
811 6cee26a0 Giorgos Korfiatis
        m = _(astakos_messages.UNKNOWN_PROJECT_APPLICATION_ID % (app_id,))
812 6cee26a0 Giorgos Korfiatis
        raise PermissionDenied(m)
813 8dcaa6ac Giorgos Korfiatis
814 d07ce657 Giorgos Korfiatis
    checkAllowed(application, request_user, admin_only=True)
815 d07ce657 Giorgos Korfiatis
816 01bdbd17 Giorgos Korfiatis
    if not application.can_approve():
817 7a08e179 Giorgos Korfiatis
        m = _(astakos_messages.APPLICATION_CANNOT_APPROVE %
818 7a08e179 Giorgos Korfiatis
              (application.id, application.state_display()))
819 01bdbd17 Giorgos Korfiatis
        raise PermissionDenied(m)
820 01bdbd17 Giorgos Korfiatis
821 9770ba6c Giorgos Korfiatis
    qh_release_pending_app(application.owner)
822 570015d2 Giorgos Korfiatis
    project = application.approve()
823 570015d2 Giorgos Korfiatis
    qh_sync_projects([project])
824 f30f0170 Giorgos Korfiatis
    logger.info("%s has been approved." % (application.log_display))
825 a3eb3a95 Giorgos Korfiatis
    application_approve_notify(application)
826 eb9ff37a Sofia Papagiannaki
827 7a08e179 Giorgos Korfiatis
828 7eadc230 Giorgos Korfiatis
def check_expiration(execute=False):
829 7eadc230 Giorgos Korfiatis
    objects = Project.objects
830 7eadc230 Giorgos Korfiatis
    expired = objects.expired_projects()
831 7eadc230 Giorgos Korfiatis
    if execute:
832 7eadc230 Giorgos Korfiatis
        for project in expired:
833 7eadc230 Giorgos Korfiatis
            terminate(project.id)
834 7eadc230 Giorgos Korfiatis
835 7eadc230 Giorgos Korfiatis
    return [project.expiration_info() for project in expired]
836 7eadc230 Giorgos Korfiatis
837 7a08e179 Giorgos Korfiatis
838 d07ce657 Giorgos Korfiatis
def terminate(project_id, request_user=None):
839 5b9e9530 Giorgos Korfiatis
    project = get_project_for_update(project_id)
840 d07ce657 Giorgos Korfiatis
    checkAllowed(project, request_user, admin_only=True)
841 907f15db Giorgos Korfiatis
    checkAlive(project)
842 5b9e9530 Giorgos Korfiatis
843 5b9e9530 Giorgos Korfiatis
    project.terminate()
844 570015d2 Giorgos Korfiatis
    qh_sync_projects([project])
845 f30f0170 Giorgos Korfiatis
    logger.info("%s has been terminated." % (project))
846 570015d2 Giorgos Korfiatis
847 a3eb3a95 Giorgos Korfiatis
    project_termination_notify(project)
848 eb9ff37a Sofia Papagiannaki
849 7a08e179 Giorgos Korfiatis
850 d07ce657 Giorgos Korfiatis
def suspend(project_id, request_user=None):
851 eb9ff37a Sofia Papagiannaki
    project = get_project_by_id(project_id)
852 d07ce657 Giorgos Korfiatis
    checkAllowed(project, request_user, admin_only=True)
853 db99f198 Giorgos Korfiatis
    checkAlive(project)
854 db99f198 Giorgos Korfiatis
855 db99f198 Giorgos Korfiatis
    project.suspend()
856 570015d2 Giorgos Korfiatis
    qh_sync_projects([project])
857 f30f0170 Giorgos Korfiatis
    logger.info("%s has been suspended." % (project))
858 570015d2 Giorgos Korfiatis
859 a3eb3a95 Giorgos Korfiatis
    project_suspension_notify(project)
860 db99f198 Giorgos Korfiatis
861 7a08e179 Giorgos Korfiatis
862 d07ce657 Giorgos Korfiatis
def resume(project_id, request_user=None):
863 db99f198 Giorgos Korfiatis
    project = get_project_for_update(project_id)
864 d07ce657 Giorgos Korfiatis
    checkAllowed(project, request_user, admin_only=True)
865 db99f198 Giorgos Korfiatis
866 db99f198 Giorgos Korfiatis
    if not project.is_suspended:
867 db99f198 Giorgos Korfiatis
        m = _(astakos_messages.NOT_SUSPENDED_PROJECT) % project.__dict__
868 db99f198 Giorgos Korfiatis
        raise PermissionDenied(m)
869 db99f198 Giorgos Korfiatis
870 db99f198 Giorgos Korfiatis
    project.resume()
871 570015d2 Giorgos Korfiatis
    qh_sync_projects([project])
872 f30f0170 Giorgos Korfiatis
    logger.info("%s has been unsuspended." % (project))
873 ff67242a Giorgos Korfiatis
874 7a08e179 Giorgos Korfiatis
875 ff67242a Giorgos Korfiatis
def get_by_chain_or_404(chain_id):
876 ff67242a Giorgos Korfiatis
    try:
877 ff67242a Giorgos Korfiatis
        project = Project.objects.get(id=chain_id)
878 ff67242a Giorgos Korfiatis
        application = project.application
879 ff67242a Giorgos Korfiatis
        return project, application
880 ff67242a Giorgos Korfiatis
    except:
881 ff67242a Giorgos Korfiatis
        application = ProjectApplication.objects.latest_of_chain(chain_id)
882 ff67242a Giorgos Korfiatis
        if application is None:
883 ff67242a Giorgos Korfiatis
            raise Http404
884 ff67242a Giorgos Korfiatis
        else:
885 ff67242a Giorgos Korfiatis
            return None, application
886 c7c0ec58 Giorgos Korfiatis
887 c7c0ec58 Giorgos Korfiatis
888 c7c0ec58 Giorgos Korfiatis
def get_user_setting(user_id, key):
889 c7c0ec58 Giorgos Korfiatis
    try:
890 c7c0ec58 Giorgos Korfiatis
        setting = UserSetting.objects.get(
891 c7c0ec58 Giorgos Korfiatis
            user=user_id, setting=key)
892 c7c0ec58 Giorgos Korfiatis
        return setting.value
893 c7c0ec58 Giorgos Korfiatis
    except UserSetting.DoesNotExist:
894 0a7a4104 Kostas Papadimitriou
        return getattr(settings, key)
895 c7c0ec58 Giorgos Korfiatis
896 c7c0ec58 Giorgos Korfiatis
897 c7c0ec58 Giorgos Korfiatis
def set_user_setting(user_id, key, value):
898 c7c0ec58 Giorgos Korfiatis
    try:
899 c7c0ec58 Giorgos Korfiatis
        setting = UserSetting.objects.get_for_update(
900 c7c0ec58 Giorgos Korfiatis
            user=user_id, setting=key)
901 c7c0ec58 Giorgos Korfiatis
    except UserSetting.DoesNotExist:
902 c7c0ec58 Giorgos Korfiatis
        setting = UserSetting(user_id=user_id, setting=key)
903 c7c0ec58 Giorgos Korfiatis
    setting.value = value
904 c7c0ec58 Giorgos Korfiatis
    setting.save()
905 c7c0ec58 Giorgos Korfiatis
906 c7c0ec58 Giorgos Korfiatis
907 c7c0ec58 Giorgos Korfiatis
def unset_user_setting(user_id, key):
908 c7c0ec58 Giorgos Korfiatis
    UserSetting.objects.filter(user=user_id, setting=key).delete()
909 c7c0ec58 Giorgos Korfiatis
910 c7c0ec58 Giorgos Korfiatis
911 9770ba6c Giorgos Korfiatis
def qh_add_pending_app(user, precursor=None, force=False, dry_run=False):
912 c7c0ec58 Giorgos Korfiatis
    if precursor is None:
913 9770ba6c Giorgos Korfiatis
        diff = 1
914 9770ba6c Giorgos Korfiatis
    else:
915 9770ba6c Giorgos Korfiatis
        chain = precursor.chain
916 9770ba6c Giorgos Korfiatis
        objs = ProjectApplication.objects
917 9770ba6c Giorgos Korfiatis
        q = objs.filter(chain=chain, state=ProjectApplication.PENDING)
918 9770ba6c Giorgos Korfiatis
        count = q.count()
919 9770ba6c Giorgos Korfiatis
        diff = 1 - count
920 c7c0ec58 Giorgos Korfiatis
921 9770ba6c Giorgos Korfiatis
    try:
922 9770ba6c Giorgos Korfiatis
        name = "DRYRUN" if dry_run else ""
923 9770ba6c Giorgos Korfiatis
        serial = register_pending_apps(user, diff, force, name=name)
924 9770ba6c Giorgos Korfiatis
    except NoCapacityError as e:
925 9770ba6c Giorgos Korfiatis
        limit = e.data['limit']
926 9770ba6c Giorgos Korfiatis
        return False, limit
927 9770ba6c Giorgos Korfiatis
    else:
928 9770ba6c Giorgos Korfiatis
        accept = not dry_run
929 9770ba6c Giorgos Korfiatis
        resolve_pending_serial(serial, accept=accept)
930 9770ba6c Giorgos Korfiatis
        return True, None
931 c7c0ec58 Giorgos Korfiatis
932 c7c0ec58 Giorgos Korfiatis
933 9770ba6c Giorgos Korfiatis
def qh_release_pending_app(user):
934 9770ba6c Giorgos Korfiatis
    serial = register_pending_apps(user, -1)
935 9770ba6c Giorgos Korfiatis
    resolve_pending_serial(serial)
936 570015d2 Giorgos Korfiatis
937 570015d2 Giorgos Korfiatis
938 570015d2 Giorgos Korfiatis
def qh_sync_projects(projects):
939 8cbea11d Giorgos Korfiatis
940 8cbea11d Giorgos Korfiatis
    memberships = ProjectMembership.objects.filter(project__in=projects)
941 8cbea11d Giorgos Korfiatis
    user_ids = set(m.person_id for m in memberships)
942 8cbea11d Giorgos Korfiatis
943 44104cd3 Giorgos Korfiatis
    qh_sync_users(user_ids)