Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / views / projects.py @ ff5edb80

History | View | Annotate | Download (22.2 kB)

1 ff5edb80 Giorgos Korfiatis
# Copyright 2011-2014 GRNET S.A. All rights reserved.
2 70e11eaa Sofia Papagiannaki
#
3 70e11eaa Sofia Papagiannaki
# Redistribution and use in source and binary forms, with or
4 70e11eaa Sofia Papagiannaki
# without modification, are permitted provided that the following
5 70e11eaa Sofia Papagiannaki
# conditions are met:
6 70e11eaa Sofia Papagiannaki
#
7 70e11eaa Sofia Papagiannaki
#   1. Redistributions of source code must retain the above
8 70e11eaa Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
9 70e11eaa Sofia Papagiannaki
#      disclaimer.
10 70e11eaa Sofia Papagiannaki
#
11 70e11eaa Sofia Papagiannaki
#   2. Redistributions in binary form must reproduce the above
12 70e11eaa Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
13 70e11eaa Sofia Papagiannaki
#      disclaimer in the documentation and/or other materials
14 70e11eaa Sofia Papagiannaki
#      provided with the distribution.
15 70e11eaa Sofia Papagiannaki
#
16 70e11eaa Sofia Papagiannaki
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 70e11eaa Sofia Papagiannaki
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 70e11eaa Sofia Papagiannaki
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 70e11eaa Sofia Papagiannaki
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 70e11eaa Sofia Papagiannaki
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 70e11eaa Sofia Papagiannaki
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 70e11eaa Sofia Papagiannaki
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 70e11eaa Sofia Papagiannaki
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 70e11eaa Sofia Papagiannaki
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 70e11eaa Sofia Papagiannaki
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 70e11eaa Sofia Papagiannaki
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 70e11eaa Sofia Papagiannaki
# POSSIBILITY OF SUCH DAMAGE.
28 70e11eaa Sofia Papagiannaki
#
29 70e11eaa Sofia Papagiannaki
# The views and conclusions contained in the software and
30 70e11eaa Sofia Papagiannaki
# documentation are those of the authors and should not be
31 70e11eaa Sofia Papagiannaki
# interpreted as representing official policies, either expressed
32 70e11eaa Sofia Papagiannaki
# or implied, of GRNET S.A.
33 70e11eaa Sofia Papagiannaki
34 70e11eaa Sofia Papagiannaki
import logging
35 70e11eaa Sofia Papagiannaki
import inflect
36 70e11eaa Sofia Papagiannaki
37 70e11eaa Sofia Papagiannaki
engine = inflect.engine()
38 70e11eaa Sofia Papagiannaki
39 9efd0075 Kostas Papadimitriou
from functools import wraps
40 70e11eaa Sofia Papagiannaki
from django_tables2 import RequestConfig
41 70e11eaa Sofia Papagiannaki
42 9efd0075 Kostas Papadimitriou
from django.shortcuts import get_object_or_404, render_to_response
43 70e11eaa Sofia Papagiannaki
from django.contrib import messages
44 70e11eaa Sofia Papagiannaki
from django.core.urlresolvers import reverse
45 362dadaa Giorgos Korfiatis
from django.http import Http404, HttpResponse
46 70e11eaa Sofia Papagiannaki
from django.shortcuts import redirect
47 70e11eaa Sofia Papagiannaki
from django.utils.html import escape
48 70e11eaa Sofia Papagiannaki
from django.utils.translation import ugettext as _
49 70e11eaa Sofia Papagiannaki
from django.views.generic.list_detail import object_list, object_detail
50 3e0a032d Sofia Papagiannaki
from django.core.exceptions import PermissionDenied
51 70e11eaa Sofia Papagiannaki
from django.views.decorators.http import require_http_methods
52 a3e3917f Giorgos Korfiatis
from django.db import transaction
53 9efd0075 Kostas Papadimitriou
from django.template import RequestContext
54 70e11eaa Sofia Papagiannaki
55 70e11eaa Sofia Papagiannaki
import astakos.im.messages as astakos_messages
56 70e11eaa Sofia Papagiannaki
57 70e11eaa Sofia Papagiannaki
from astakos.im import tables
58 6d583e07 Giorgos Korfiatis
from astakos.im.models import ProjectApplication, ProjectMembership, Project
59 9efd0075 Kostas Papadimitriou
from astakos.im.util import get_context, restrict_next, restrict_reverse
60 3e0a032d Sofia Papagiannaki
from astakos.im.forms import ProjectApplicationForm, AddProjectMembersForm, \
61 9efd0075 Kostas Papadimitriou
    ProjectSearchForm, ProjectModificationForm
62 3e0a032d Sofia Papagiannaki
from astakos.im.functions import check_pending_app_quota, accept_membership, \
63 3e0a032d Sofia Papagiannaki
    reject_membership, remove_membership, cancel_membership, leave_project, \
64 3e0a032d Sofia Papagiannaki
    join_project, enroll_member, can_join_request, can_leave_request, \
65 6d583e07 Giorgos Korfiatis
    get_related_project_id, approve_application, \
66 9efd0075 Kostas Papadimitriou
    deny_application, cancel_application, dismiss_application, ProjectError, \
67 9efd0075 Kostas Papadimitriou
    can_cancel_join_request
68 8998f09a Sofia Papagiannaki
from astakos.im import settings
69 59eb6ec5 Kostas Papadimitriou
from astakos.im.util import redirect_back
70 70e11eaa Sofia Papagiannaki
from astakos.im.views.util import render_response, _create_object, \
71 9efd0075 Kostas Papadimitriou
    _update_object, _resources_catalog, ExceptionHandler, \
72 9efd0075 Kostas Papadimitriou
    get_user_projects_table, handle_valid_members_form, redirect_to_next
73 70e11eaa Sofia Papagiannaki
from astakos.im.views.decorators import cookie_fix, signed_terms_required,\
74 b6496f0c Kostas Papadimitriou
    valid_astakos_user_required, login_required
75 70e11eaa Sofia Papagiannaki
76 9efd0075 Kostas Papadimitriou
from astakos.api import projects as api
77 9efd0075 Kostas Papadimitriou
from astakos.im import functions as project_actions
78 9efd0075 Kostas Papadimitriou
79 70e11eaa Sofia Papagiannaki
logger = logging.getLogger(__name__)
80 70e11eaa Sofia Papagiannaki
81 70e11eaa Sofia Papagiannaki
82 9efd0075 Kostas Papadimitriou
def no_transaction(func):
83 9efd0075 Kostas Papadimitriou
    return func
84 70e11eaa Sofia Papagiannaki
85 70e11eaa Sofia Papagiannaki
86 9efd0075 Kostas Papadimitriou
def project_view(get=True, post=False, transaction=False):
87 9efd0075 Kostas Papadimitriou
    methods = []
88 9efd0075 Kostas Papadimitriou
    if get:
89 9efd0075 Kostas Papadimitriou
        methods.append("GET")
90 9efd0075 Kostas Papadimitriou
    if post:
91 9efd0075 Kostas Papadimitriou
        methods.append("POST")
92 70e11eaa Sofia Papagiannaki
93 9efd0075 Kostas Papadimitriou
    if transaction:
94 9efd0075 Kostas Papadimitriou
        transaction_method = transaction.commit_on_success
95 9efd0075 Kostas Papadimitriou
    else:
96 9efd0075 Kostas Papadimitriou
        transaction_method = no_transaction
97 70e11eaa Sofia Papagiannaki
98 9efd0075 Kostas Papadimitriou
    def wrapper(func):
99 9efd0075 Kostas Papadimitriou
        return \
100 9efd0075 Kostas Papadimitriou
            wraps(func)(
101 9efd0075 Kostas Papadimitriou
                require_http_methods(methods)(
102 9efd0075 Kostas Papadimitriou
                    cookie_fix(
103 9efd0075 Kostas Papadimitriou
                        valid_astakos_user_required(
104 9efd0075 Kostas Papadimitriou
                            transaction_method(
105 9efd0075 Kostas Papadimitriou
                                func)))))
106 9efd0075 Kostas Papadimitriou
    return wrapper
107 70e11eaa Sofia Papagiannaki
108 70e11eaa Sofia Papagiannaki
109 9efd0075 Kostas Papadimitriou
@project_view()
110 9efd0075 Kostas Papadimitriou
def how_it_works(request):
111 9efd0075 Kostas Papadimitriou
    return render_response('im/how_it_works.html',
112 9efd0075 Kostas Papadimitriou
                           context_instance=get_context(request))
113 8fb8d0cf Giorgos Korfiatis
114 70e11eaa Sofia Papagiannaki
115 9efd0075 Kostas Papadimitriou
@project_view()
116 9efd0075 Kostas Papadimitriou
def project_list(request, template_name="im/projects/project_list.html"):
117 9efd0075 Kostas Papadimitriou
    query = api.make_project_query({})
118 9efd0075 Kostas Papadimitriou
    projects = api._get_projects(query, request_user=request.user)
119 70e11eaa Sofia Papagiannaki
120 9efd0075 Kostas Papadimitriou
    table = None
121 9efd0075 Kostas Papadimitriou
    if projects.count():
122 9efd0075 Kostas Papadimitriou
        table = get_user_projects_table(projects, user=request.user,
123 9efd0075 Kostas Papadimitriou
                                        prefix="my_projects_")
124 70e11eaa Sofia Papagiannaki
125 9efd0075 Kostas Papadimitriou
    context = {'is_search': False, 'table': table}
126 9efd0075 Kostas Papadimitriou
    return object_list(request, projects, template_name=template_name,
127 9efd0075 Kostas Papadimitriou
                       extra_context=context)
128 70e11eaa Sofia Papagiannaki
129 70e11eaa Sofia Papagiannaki
130 9efd0075 Kostas Papadimitriou
@project_view(post=True)
131 9efd0075 Kostas Papadimitriou
def project_add_or_modify(request, project_uuid=None):
132 70e11eaa Sofia Papagiannaki
    user = request.user
133 70e11eaa Sofia Papagiannaki
134 9efd0075 Kostas Papadimitriou
    # only check quota for non project admin users
135 70e11eaa Sofia Papagiannaki
    if not user.is_project_admin():
136 9efd0075 Kostas Papadimitriou
        ok, limit = check_pending_app_quota(user)
137 70e11eaa Sofia Papagiannaki
        if not ok:
138 9efd0075 Kostas Papadimitriou
            m = _(astakos_messages.PENDING_APPLICATION_LIMIT_ADD) % limit
139 70e11eaa Sofia Papagiannaki
            messages.error(request, m)
140 9efd0075 Kostas Papadimitriou
            return redirect(restrict_reverse(
141 9efd0075 Kostas Papadimitriou
                'astakos.im.views.project_list'))
142 9efd0075 Kostas Papadimitriou
143 9efd0075 Kostas Papadimitriou
    project = None
144 9efd0075 Kostas Papadimitriou
    if project_uuid:
145 9efd0075 Kostas Papadimitriou
        project = get_object_or_404(Project, uuid=project_uuid)
146 9efd0075 Kostas Papadimitriou
147 9efd0075 Kostas Papadimitriou
        if not user.owns_project(project) and not user.is_project_admin():
148 9efd0075 Kostas Papadimitriou
            m = _(astakos_messages.NOT_ALLOWED)
149 9efd0075 Kostas Papadimitriou
            raise PermissionDenied(m)
150 70e11eaa Sofia Papagiannaki
151 70e11eaa Sofia Papagiannaki
    details_fields = ["name", "homepage", "description", "start_date",
152 70e11eaa Sofia Papagiannaki
                      "end_date", "comments"]
153 70e11eaa Sofia Papagiannaki
    membership_fields = ["member_join_policy", "member_leave_policy",
154 70e11eaa Sofia Papagiannaki
                         "limit_on_members_number"]
155 9efd0075 Kostas Papadimitriou
156 2dc27ac1 Giorgos Korfiatis
    resource_catalog, resource_groups = _resources_catalog()
157 9efd0075 Kostas Papadimitriou
    resource_catalog_dict, resource_groups_dict = \
158 9efd0075 Kostas Papadimitriou
            _resources_catalog(as_dict=True)
159 9efd0075 Kostas Papadimitriou
160 70e11eaa Sofia Papagiannaki
    extra_context = {
161 70e11eaa Sofia Papagiannaki
        'resource_catalog': resource_catalog,
162 70e11eaa Sofia Papagiannaki
        'resource_groups': resource_groups,
163 9efd0075 Kostas Papadimitriou
        'resource_catalog_dict': resource_catalog_dict,
164 9efd0075 Kostas Papadimitriou
        'resource_groups_dict': resource_groups_dict,
165 70e11eaa Sofia Papagiannaki
        'show_form': True,
166 70e11eaa Sofia Papagiannaki
        'details_fields': details_fields,
167 9efd0075 Kostas Papadimitriou
        'membership_fields': membership_fields,
168 9efd0075 Kostas Papadimitriou
        'object': project
169 70e11eaa Sofia Papagiannaki
    }
170 70e11eaa Sofia Papagiannaki
171 9efd0075 Kostas Papadimitriou
    with transaction.commit_on_success():
172 9efd0075 Kostas Papadimitriou
        template_name = 'im/projects/projectapplication_form.html'
173 9efd0075 Kostas Papadimitriou
        summary_template_name = \
174 9efd0075 Kostas Papadimitriou
                'im/projects/projectapplication_form_summary.html'
175 9efd0075 Kostas Papadimitriou
        success_msg = _("The project application has been received and "
176 9efd0075 Kostas Papadimitriou
                        "is under consideration.")
177 9efd0075 Kostas Papadimitriou
        form_class = ProjectApplicationForm
178 9efd0075 Kostas Papadimitriou
179 9efd0075 Kostas Papadimitriou
        if project:
180 9efd0075 Kostas Papadimitriou
            template_name = 'im/projects/projectmodification_form.html'
181 9efd0075 Kostas Papadimitriou
            summary_template_name = \
182 9efd0075 Kostas Papadimitriou
                    'im/projects/projectmodification_form_summary.html'
183 9efd0075 Kostas Papadimitriou
            success_msg = _("The project modification has been received and "
184 9efd0075 Kostas Papadimitriou
                            "is under consideration.")
185 9efd0075 Kostas Papadimitriou
            form_class = ProjectModificationForm
186 9efd0075 Kostas Papadimitriou
            details_fields.remove('start_date')
187 9efd0075 Kostas Papadimitriou
188 9efd0075 Kostas Papadimitriou
        extra_context['edit'] = 0
189 9efd0075 Kostas Papadimitriou
        if request.method == 'POST':
190 9efd0075 Kostas Papadimitriou
            form = form_class(request.POST, request.FILES, instance=project)
191 9efd0075 Kostas Papadimitriou
            if form.is_valid():
192 9efd0075 Kostas Papadimitriou
                verify = request.GET.get('verify')
193 9efd0075 Kostas Papadimitriou
                edit = request.GET.get('edit')
194 9efd0075 Kostas Papadimitriou
                if verify == '1':
195 9efd0075 Kostas Papadimitriou
                    extra_context['show_form'] = False
196 9efd0075 Kostas Papadimitriou
                    extra_context['form_data'] = form.cleaned_data
197 9efd0075 Kostas Papadimitriou
                    template_name = summary_template_name
198 9efd0075 Kostas Papadimitriou
                elif edit == '1':
199 9efd0075 Kostas Papadimitriou
                    extra_context['show_form'] = True
200 9efd0075 Kostas Papadimitriou
                else:
201 9efd0075 Kostas Papadimitriou
                    new_object = form.save()
202 9efd0075 Kostas Papadimitriou
                    messages.success(request, success_msg,
203 9efd0075 Kostas Papadimitriou
                                     fail_silently=True)
204 9efd0075 Kostas Papadimitriou
                    return redirect(restrict_reverse('project_list'))
205 9efd0075 Kostas Papadimitriou
        else:
206 9efd0075 Kostas Papadimitriou
            form = form_class(instance=project)
207 9efd0075 Kostas Papadimitriou
208 9efd0075 Kostas Papadimitriou
        extra_context['form'] = form
209 9efd0075 Kostas Papadimitriou
        return render_to_response(template_name, extra_context,
210 9efd0075 Kostas Papadimitriou
                                  context_instance=RequestContext(request))
211 9efd0075 Kostas Papadimitriou
212 9efd0075 Kostas Papadimitriou
213 9efd0075 Kostas Papadimitriou
@project_view(get=False, post=True)
214 9efd0075 Kostas Papadimitriou
def project_app_cancel(request, project_uuid, application_id):
215 70e11eaa Sofia Papagiannaki
    with ExceptionHandler(request):
216 9efd0075 Kostas Papadimitriou
        with transaction.commit_on_success():
217 9efd0075 Kostas Papadimitriou
            cancel_application(application_id, project_uuid,
218 9efd0075 Kostas Papadimitriou
                               request_user=request.user)
219 9efd0075 Kostas Papadimitriou
            messages.success(request, _(astakos_messages.APPLICATION_CANCELLED))
220 9efd0075 Kostas Papadimitriou
    return redirect(reverse('project_list'))
221 9024ed2e Giorgos Korfiatis
222 9024ed2e Giorgos Korfiatis
223 70e11eaa Sofia Papagiannaki
224 9efd0075 Kostas Papadimitriou
@project_view(post=True)
225 9efd0075 Kostas Papadimitriou
def project_or_app_detail(request, project_uuid, app_id=None):
226 8fb8d0cf Giorgos Korfiatis
227 9efd0075 Kostas Papadimitriou
    project = get_object_or_404(Project, uuid=project_uuid)
228 9efd0075 Kostas Papadimitriou
    application = None
229 9efd0075 Kostas Papadimitriou
    if app_id:
230 9efd0075 Kostas Papadimitriou
        application = get_object_or_404(ProjectApplication, id=app_id)
231 9efd0075 Kostas Papadimitriou
        if request.method == "POST":
232 9efd0075 Kostas Papadimitriou
            raise PermissionDenied
233 9efd0075 Kostas Papadimitriou
234 9efd0075 Kostas Papadimitriou
235 9efd0075 Kostas Papadimitriou
    if project.state in [Project.O_PENDING] and not application:
236 9efd0075 Kostas Papadimitriou
        return redirect(reverse('project_app',
237 9efd0075 Kostas Papadimitriou
                                args=(project.uuid,
238 9efd0075 Kostas Papadimitriou
                                      project.last_application.id,)))
239 9efd0075 Kostas Papadimitriou
240 9efd0075 Kostas Papadimitriou
    members = project.projectmembership_set
241 9efd0075 Kostas Papadimitriou
242 9efd0075 Kostas Papadimitriou
    # handle members
243 362dadaa Giorgos Korfiatis
    if request.method == 'POST':
244 362dadaa Giorgos Korfiatis
        addmembers_form = AddProjectMembersForm(
245 362dadaa Giorgos Korfiatis
            request.POST,
246 9efd0075 Kostas Papadimitriou
            project_id=project.pk,
247 362dadaa Giorgos Korfiatis
            request_user=request.user)
248 362dadaa Giorgos Korfiatis
        with ExceptionHandler(request):
249 9efd0075 Kostas Papadimitriou
            handle_valid_members_form(request, project.pk, addmembers_form)
250 362dadaa Giorgos Korfiatis
251 362dadaa Giorgos Korfiatis
        if addmembers_form.is_valid():
252 362dadaa Giorgos Korfiatis
            addmembers_form = AddProjectMembersForm()  # clear form data
253 362dadaa Giorgos Korfiatis
    else:
254 362dadaa Giorgos Korfiatis
        addmembers_form = AddProjectMembersForm()  # initialize form
255 362dadaa Giorgos Korfiatis
256 362dadaa Giorgos Korfiatis
    approved_members_count = project.members_count()
257 362dadaa Giorgos Korfiatis
    pending_members_count = project.count_pending_memberships()
258 362dadaa Giorgos Korfiatis
    _limit = project.limit_on_members_number
259 362dadaa Giorgos Korfiatis
    remaining_memberships_count = (max(0, _limit - approved_members_count)
260 362dadaa Giorgos Korfiatis
                                   if _limit is not None else None)
261 362dadaa Giorgos Korfiatis
    members = members.associated()
262 362dadaa Giorgos Korfiatis
    members = members.select_related()
263 362dadaa Giorgos Korfiatis
    members_table = tables.ProjectMembersTable(project,
264 362dadaa Giorgos Korfiatis
                                               members,
265 362dadaa Giorgos Korfiatis
                                               user=request.user,
266 362dadaa Giorgos Korfiatis
                                               prefix="members_")
267 9efd0075 Kostas Papadimitriou
    paginate = {"per_page": settings.PAGINATE_BY}
268 9efd0075 Kostas Papadimitriou
    RequestConfig(request, paginate=paginate).configure(members_table)
269 362dadaa Giorgos Korfiatis
270 362dadaa Giorgos Korfiatis
    user = request.user
271 362dadaa Giorgos Korfiatis
    is_project_admin = user.is_project_admin()
272 362dadaa Giorgos Korfiatis
    is_owner = user.owns_project(project)
273 362dadaa Giorgos Korfiatis
274 362dadaa Giorgos Korfiatis
    if not (is_owner or is_project_admin) and \
275 362dadaa Giorgos Korfiatis
            not user.non_owner_can_view(project):
276 362dadaa Giorgos Korfiatis
        m = _(astakos_messages.NOT_ALLOWED)
277 362dadaa Giorgos Korfiatis
        raise PermissionDenied(m)
278 362dadaa Giorgos Korfiatis
279 362dadaa Giorgos Korfiatis
    membership = user.get_membership(project) if project else None
280 362dadaa Giorgos Korfiatis
    membership_id = membership.id if membership else None
281 362dadaa Giorgos Korfiatis
    mem_display = user.membership_display(project) if project else None
282 362dadaa Giorgos Korfiatis
    can_join_req = can_join_request(project, user) if project else False
283 362dadaa Giorgos Korfiatis
    can_leave_req = can_leave_request(project, user) if project else False
284 9efd0075 Kostas Papadimitriou
    can_cancel_req = can_cancel_join_request(project, user) if project else False
285 362dadaa Giorgos Korfiatis
286 9efd0075 Kostas Papadimitriou
    is_modification = application.is_modification() if application else False
287 70e11eaa Sofia Papagiannaki
288 9efd0075 Kostas Papadimitriou
    queryset = Project.objects.select_related()
289 9efd0075 Kostas Papadimitriou
    object_id = project.pk
290 9efd0075 Kostas Papadimitriou
    resources_set = project.resource_set
291 9efd0075 Kostas Papadimitriou
    template_name = "im/projects/project_detail.html"
292 9efd0075 Kostas Papadimitriou
    if application:
293 9efd0075 Kostas Papadimitriou
        queryset = ProjectApplication.objects.select_related()
294 9efd0075 Kostas Papadimitriou
        object_id = application.pk
295 9efd0075 Kostas Papadimitriou
        resources_set = application.resource_set
296 9efd0075 Kostas Papadimitriou
        template_name = "im/projects/project_application_detail.html"
297 70e11eaa Sofia Papagiannaki
298 70e11eaa Sofia Papagiannaki
    return object_detail(
299 70e11eaa Sofia Papagiannaki
        request,
300 9efd0075 Kostas Papadimitriou
        queryset=queryset,
301 9efd0075 Kostas Papadimitriou
        object_id=object_id,
302 460b907d Kostas Papadimitriou
        template_name=template_name,
303 70e11eaa Sofia Papagiannaki
        extra_context={
304 9efd0075 Kostas Papadimitriou
            'project': project,
305 8eb176ab Kostas Papadimitriou
            'application': application,
306 9efd0075 Kostas Papadimitriou
            'is_application': bool(application),
307 9efd0075 Kostas Papadimitriou
            'is_modification': is_modification,
308 460b907d Kostas Papadimitriou
            'addmembers_form': addmembers_form,
309 460b907d Kostas Papadimitriou
            'approved_members_count': approved_members_count,
310 460b907d Kostas Papadimitriou
            'pending_members_count': pending_members_count,
311 70e11eaa Sofia Papagiannaki
            'members_table': members_table,
312 70e11eaa Sofia Papagiannaki
            'owner_mode': is_owner,
313 70e11eaa Sofia Papagiannaki
            'admin_mode': is_project_admin,
314 70e11eaa Sofia Papagiannaki
            'mem_display': mem_display,
315 dc946891 Giorgos Korfiatis
            'membership_id': membership_id,
316 70e11eaa Sofia Papagiannaki
            'can_join_request': can_join_req,
317 70e11eaa Sofia Papagiannaki
            'can_leave_request': can_leave_req,
318 9efd0075 Kostas Papadimitriou
            'can_cancel_join_request': can_cancel_req,
319 9efd0075 Kostas Papadimitriou
            'resources_set': resources_set,
320 9efd0075 Kostas Papadimitriou
            'last_app': None if application else project.last_application,
321 9efd0075 Kostas Papadimitriou
            'remaining_memberships_count': remaining_memberships_count
322 0f433ce2 Giorgos Korfiatis
        })
323 0f433ce2 Giorgos Korfiatis
324 70e11eaa Sofia Papagiannaki
325 9efd0075 Kostas Papadimitriou
MEMBERSHIP_STATUS_FILTER = {
326 9efd0075 Kostas Papadimitriou
    0: {'state': ProjectMembership.REQUESTED},
327 9efd0075 Kostas Papadimitriou
    1: {'state__in': ProjectMembership.ACCEPTED_STATES}
328 9efd0075 Kostas Papadimitriou
}
329 9efd0075 Kostas Papadimitriou
330 9efd0075 Kostas Papadimitriou
331 70e11eaa Sofia Papagiannaki
@require_http_methods(["GET", "POST"])
332 70e11eaa Sofia Papagiannaki
@cookie_fix
333 70e11eaa Sofia Papagiannaki
@valid_astakos_user_required
334 70e11eaa Sofia Papagiannaki
def project_search(request):
335 70e11eaa Sofia Papagiannaki
    q = request.GET.get('q', '')
336 70e11eaa Sofia Papagiannaki
    form = ProjectSearchForm()
337 70e11eaa Sofia Papagiannaki
    q = q.strip()
338 70e11eaa Sofia Papagiannaki
339 70e11eaa Sofia Papagiannaki
    if request.method == "POST":
340 70e11eaa Sofia Papagiannaki
        form = ProjectSearchForm(request.POST)
341 70e11eaa Sofia Papagiannaki
        if form.is_valid():
342 70e11eaa Sofia Papagiannaki
            q = form.cleaned_data['q'].strip()
343 70e11eaa Sofia Papagiannaki
        else:
344 70e11eaa Sofia Papagiannaki
            q = None
345 70e11eaa Sofia Papagiannaki
346 70e11eaa Sofia Papagiannaki
    if q is None:
347 f243d667 Giorgos Korfiatis
        projects = Project.objects.none()
348 70e11eaa Sofia Papagiannaki
    else:
349 f243d667 Giorgos Korfiatis
        accepted = request.user.projectmembership_set.filter(
350 f243d667 Giorgos Korfiatis
            state__in=ProjectMembership.ACCEPTED_STATES).values_list(
351 72313b77 Giorgos Korfiatis
            'project', flat=True)
352 f243d667 Giorgos Korfiatis
353 f243d667 Giorgos Korfiatis
        projects = Project.objects.search_by_name(q)
354 362dadaa Giorgos Korfiatis
        projects = projects.filter(state=Project.NORMAL)
355 b10ceccd Giorgos Korfiatis
        projects = projects.exclude(id__in=accepted).select_related(
356 b10ceccd Giorgos Korfiatis
            'application', 'application__owner', 'application__applicant')
357 f243d667 Giorgos Korfiatis
358 b10ceccd Giorgos Korfiatis
    table = get_user_projects_table(projects, user=request.user,
359 b10ceccd Giorgos Korfiatis
                                    prefix="my_projects_")
360 70e11eaa Sofia Papagiannaki
    if request.method == "POST":
361 70e11eaa Sofia Papagiannaki
        table.caption = _('SEARCH RESULTS')
362 70e11eaa Sofia Papagiannaki
    else:
363 70e11eaa Sofia Papagiannaki
        table.caption = _('ALL PROJECTS')
364 70e11eaa Sofia Papagiannaki
365 70e11eaa Sofia Papagiannaki
    return object_list(
366 70e11eaa Sofia Papagiannaki
        request,
367 70e11eaa Sofia Papagiannaki
        projects,
368 70e11eaa Sofia Papagiannaki
        template_name='im/projects/project_list.html',
369 70e11eaa Sofia Papagiannaki
        extra_context={
370 8fb8d0cf Giorgos Korfiatis
            'form': form,
371 8fb8d0cf Giorgos Korfiatis
            'is_search': True,
372 8fb8d0cf Giorgos Korfiatis
            'q': q,
373 8fb8d0cf Giorgos Korfiatis
            'table': table
374 70e11eaa Sofia Papagiannaki
        })
375 70e11eaa Sofia Papagiannaki
376 8fb8d0cf Giorgos Korfiatis
377 9efd0075 Kostas Papadimitriou
@project_view(get=False, post=True)
378 9efd0075 Kostas Papadimitriou
def project_join(request, project_uuid):
379 9efd0075 Kostas Papadimitriou
    project = get_object_or_404(Project, uuid=project_uuid)
380 70e11eaa Sofia Papagiannaki
    with ExceptionHandler(request):
381 9efd0075 Kostas Papadimitriou
        with transaction.commit_on_success():
382 9efd0075 Kostas Papadimitriou
            membership = join_project(project_uuid, request.user)
383 9efd0075 Kostas Papadimitriou
            if membership.state != membership.REQUESTED:
384 9efd0075 Kostas Papadimitriou
                m = _(astakos_messages.USER_JOINED_PROJECT)
385 9efd0075 Kostas Papadimitriou
            else:
386 9efd0075 Kostas Papadimitriou
                m = _(astakos_messages.USER_JOIN_REQUEST_SUBMITTED)
387 9efd0075 Kostas Papadimitriou
            messages.success(request, m)
388 9efd0075 Kostas Papadimitriou
    return redirect_to_next(request, 'project_detail', args=(project.uuid,))
389 70e11eaa Sofia Papagiannaki
390 70e11eaa Sofia Papagiannaki
391 9efd0075 Kostas Papadimitriou
@project_view(get=False, post=True)
392 9efd0075 Kostas Papadimitriou
def project_leave(request, project_uuid):
393 9efd0075 Kostas Papadimitriou
    project = get_object_or_404(Project, uuid=project_uuid)
394 70e11eaa Sofia Papagiannaki
    with ExceptionHandler(request):
395 9efd0075 Kostas Papadimitriou
        with transaction.commit_on_success():
396 9efd0075 Kostas Papadimitriou
            memb_id = request.user.get_membership(project).pk
397 9efd0075 Kostas Papadimitriou
            auto_accepted = leave_project(memb_id, request.user)
398 9efd0075 Kostas Papadimitriou
            if auto_accepted:
399 9efd0075 Kostas Papadimitriou
                m = _(astakos_messages.USER_LEFT_PROJECT)
400 9efd0075 Kostas Papadimitriou
            else:
401 9efd0075 Kostas Papadimitriou
                m = _(astakos_messages.USER_LEAVE_REQUEST_SUBMITTED)
402 9efd0075 Kostas Papadimitriou
            messages.success(request, m)
403 9efd0075 Kostas Papadimitriou
    return redirect_to_next(request, 'project_detail', args=(project.uuid,))
404 70e11eaa Sofia Papagiannaki
405 70e11eaa Sofia Papagiannaki
406 9efd0075 Kostas Papadimitriou
@project_view(get=False, post=True)
407 9efd0075 Kostas Papadimitriou
def project_cancel_join(request, project_uuid):
408 9efd0075 Kostas Papadimitriou
    project = get_object_or_404(Project, uuid=project_uuid)
409 70e11eaa Sofia Papagiannaki
    with ExceptionHandler(request):
410 9efd0075 Kostas Papadimitriou
        with transaction.commit_on_success():
411 9efd0075 Kostas Papadimitriou
            project = get_object_or_404(Project, uuid=project_uuid)
412 9efd0075 Kostas Papadimitriou
            memb_id = request.user.get_membership(project).pk
413 9efd0075 Kostas Papadimitriou
            cancel_membership(memb_id, request.user)
414 9efd0075 Kostas Papadimitriou
            m = _(astakos_messages.USER_REQUEST_CANCELLED)
415 9efd0075 Kostas Papadimitriou
            messages.success(request, m)
416 9efd0075 Kostas Papadimitriou
    return redirect_to_next(request, 'project_detail', args=(project.uuid,))
417 70e11eaa Sofia Papagiannaki
418 70e11eaa Sofia Papagiannaki
419 9efd0075 Kostas Papadimitriou
@project_view(get=False, post=True)
420 9efd0075 Kostas Papadimitriou
def project_app_approve(request, project_uuid, application_id):
421 70e11eaa Sofia Papagiannaki
    with ExceptionHandler(request):
422 9efd0075 Kostas Papadimitriou
        with transaction.commit_on_success():
423 9efd0075 Kostas Papadimitriou
            approve_application(application_id, project_uuid,
424 9efd0075 Kostas Papadimitriou
                                request_user=request.user)
425 9efd0075 Kostas Papadimitriou
            messages.success(request, _(astakos_messages.APPLICATION_APPROVED))
426 9efd0075 Kostas Papadimitriou
    return redirect(reverse('project_detail', args=(project_uuid,)))
427 70e11eaa Sofia Papagiannaki
428 70e11eaa Sofia Papagiannaki
429 9efd0075 Kostas Papadimitriou
@project_view(get=False, post=True)
430 9efd0075 Kostas Papadimitriou
def project_app_deny(request, project_uuid, application_id):
431 70e11eaa Sofia Papagiannaki
    with ExceptionHandler(request):
432 9efd0075 Kostas Papadimitriou
        reason = request.POST.get("reason", "")
433 9efd0075 Kostas Papadimitriou
        with transaction.commit_on_success():
434 9efd0075 Kostas Papadimitriou
            deny_application(application_id, project_uuid,
435 9efd0075 Kostas Papadimitriou
                                request_user=request.user, reason=reason)
436 9efd0075 Kostas Papadimitriou
            messages.success(request, _(astakos_messages.APPLICATION_DENIED))
437 9efd0075 Kostas Papadimitriou
    return redirect(reverse("project_list"))
438 70e11eaa Sofia Papagiannaki
439 70e11eaa Sofia Papagiannaki
440 9efd0075 Kostas Papadimitriou
@project_view(get=False, post=True)
441 9efd0075 Kostas Papadimitriou
def project_app_dismiss(request, project_uuid, application_id):
442 70e11eaa Sofia Papagiannaki
    with ExceptionHandler(request):
443 9efd0075 Kostas Papadimitriou
        with transaction.commit_on_success():
444 9efd0075 Kostas Papadimitriou
            dismiss_application(application_id, project_uuid,
445 9efd0075 Kostas Papadimitriou
                                request_user=request.user)
446 9efd0075 Kostas Papadimitriou
            messages.success(request,
447 9efd0075 Kostas Papadimitriou
                             _(astakos_messages.APPLICATION_DISMISSED))
448 9efd0075 Kostas Papadimitriou
    return redirect(reverse("project_list"))
449 70e11eaa Sofia Papagiannaki
450 70e11eaa Sofia Papagiannaki
451 9efd0075 Kostas Papadimitriou
@project_view(post=True)
452 9efd0075 Kostas Papadimitriou
def project_members(request, project_uuid, members_status_filter=None,
453 460b907d Kostas Papadimitriou
                    template_name='im/projects/project_members.html'):
454 9efd0075 Kostas Papadimitriou
    project = get_object_or_404(Project, uuid=project_uuid)
455 7971e5da Kostas Papadimitriou
456 7971e5da Kostas Papadimitriou
    user = request.user
457 7971e5da Kostas Papadimitriou
    if not user.owns_project(project) and not user.is_project_admin():
458 7971e5da Kostas Papadimitriou
        return redirect(reverse('index'))
459 7971e5da Kostas Papadimitriou
460 362dadaa Giorgos Korfiatis
    if request.method == 'POST':
461 362dadaa Giorgos Korfiatis
        addmembers_form = AddProjectMembersForm(
462 362dadaa Giorgos Korfiatis
            request.POST,
463 362dadaa Giorgos Korfiatis
            chain_id=int(chain_id),
464 362dadaa Giorgos Korfiatis
            request_user=request.user)
465 362dadaa Giorgos Korfiatis
        with ExceptionHandler(request):
466 9efd0075 Kostas Papadimitriou
            handle_valid_members_form(request, chain_id, addmembers_form)
467 362dadaa Giorgos Korfiatis
468 362dadaa Giorgos Korfiatis
        if addmembers_form.is_valid():
469 362dadaa Giorgos Korfiatis
            addmembers_form = AddProjectMembersForm()  # clear form data
470 362dadaa Giorgos Korfiatis
    else:
471 362dadaa Giorgos Korfiatis
        addmembers_form = AddProjectMembersForm()  # initialize form
472 362dadaa Giorgos Korfiatis
473 9efd0075 Kostas Papadimitriou
    query = api.make_membership_query({'project': project_uuid})
474 9efd0075 Kostas Papadimitriou
    members = api._get_memberships(query, request_user=user)
475 362dadaa Giorgos Korfiatis
    approved_members_count = project.members_count()
476 362dadaa Giorgos Korfiatis
    pending_members_count = project.count_pending_memberships()
477 362dadaa Giorgos Korfiatis
    _limit = project.limit_on_members_number
478 362dadaa Giorgos Korfiatis
    if _limit is not None:
479 362dadaa Giorgos Korfiatis
        remaining_memberships_count = \
480 362dadaa Giorgos Korfiatis
            max(0, _limit - approved_members_count)
481 362dadaa Giorgos Korfiatis
    flt = MEMBERSHIP_STATUS_FILTER.get(members_status_filter)
482 362dadaa Giorgos Korfiatis
    if flt is not None:
483 9efd0075 Kostas Papadimitriou
        members = members.filter(**flt)
484 362dadaa Giorgos Korfiatis
    else:
485 9efd0075 Kostas Papadimitriou
        members = members.filter(state__in=ProjectMembership.ASSOCIATED_STATES)
486 9efd0075 Kostas Papadimitriou
487 362dadaa Giorgos Korfiatis
    members = members.select_related()
488 362dadaa Giorgos Korfiatis
    members_table = tables.ProjectMembersTable(project,
489 362dadaa Giorgos Korfiatis
                                               members,
490 362dadaa Giorgos Korfiatis
                                               user=request.user,
491 362dadaa Giorgos Korfiatis
                                               prefix="members_")
492 362dadaa Giorgos Korfiatis
    RequestConfig(request, paginate={"per_page": settings.PAGINATE_BY}
493 362dadaa Giorgos Korfiatis
                  ).configure(members_table)
494 362dadaa Giorgos Korfiatis
495 362dadaa Giorgos Korfiatis
496 362dadaa Giorgos Korfiatis
    user = request.user
497 362dadaa Giorgos Korfiatis
    is_project_admin = user.is_project_admin()
498 362dadaa Giorgos Korfiatis
    is_owner = user.owns_application(project)
499 362dadaa Giorgos Korfiatis
    if (
500 362dadaa Giorgos Korfiatis
        not (is_owner or is_project_admin) and
501 362dadaa Giorgos Korfiatis
        not user.non_owner_can_view(project)
502 362dadaa Giorgos Korfiatis
    ):
503 362dadaa Giorgos Korfiatis
        m = _(astakos_messages.NOT_ALLOWED)
504 362dadaa Giorgos Korfiatis
        raise PermissionDenied(m)
505 362dadaa Giorgos Korfiatis
506 362dadaa Giorgos Korfiatis
    membership = user.get_membership(project) if project else None
507 362dadaa Giorgos Korfiatis
    membership_id = membership.id if membership else None
508 362dadaa Giorgos Korfiatis
    mem_display = user.membership_display(project) if project else None
509 362dadaa Giorgos Korfiatis
    can_join_req = can_join_request(project, user) if project else False
510 362dadaa Giorgos Korfiatis
    can_leave_req = can_leave_request(project, user) if project else False
511 362dadaa Giorgos Korfiatis
512 362dadaa Giorgos Korfiatis
    return object_detail(
513 362dadaa Giorgos Korfiatis
        request,
514 362dadaa Giorgos Korfiatis
        queryset=Project.objects.select_related(),
515 362dadaa Giorgos Korfiatis
        object_id=project.id,
516 362dadaa Giorgos Korfiatis
        template_name='im/projects/project_members.html',
517 362dadaa Giorgos Korfiatis
        extra_context={
518 362dadaa Giorgos Korfiatis
            'addmembers_form': addmembers_form,
519 362dadaa Giorgos Korfiatis
            'approved_members_count': approved_members_count,
520 362dadaa Giorgos Korfiatis
            'pending_members_count': pending_members_count,
521 362dadaa Giorgos Korfiatis
            'members_table': members_table,
522 362dadaa Giorgos Korfiatis
            'owner_mode': is_owner,
523 362dadaa Giorgos Korfiatis
            'admin_mode': is_project_admin,
524 362dadaa Giorgos Korfiatis
            'mem_display': mem_display,
525 362dadaa Giorgos Korfiatis
            'membership_id': membership_id,
526 362dadaa Giorgos Korfiatis
            'can_join_request': can_join_req,
527 362dadaa Giorgos Korfiatis
            'can_leave_request': can_leave_req,
528 362dadaa Giorgos Korfiatis
            'members_status_filter': members_status_filter,
529 9efd0075 Kostas Papadimitriou
            'project': project,
530 362dadaa Giorgos Korfiatis
            'remaining_memberships_count': remaining_memberships_count,
531 362dadaa Giorgos Korfiatis
        })
532 c363a678 Kostas Papadimitriou
533 c363a678 Kostas Papadimitriou
534 9efd0075 Kostas Papadimitriou
@project_view(get=False, post=True)
535 9efd0075 Kostas Papadimitriou
def project_members_action(request, project_uuid, action=None, redirect_to='',
536 9efd0075 Kostas Papadimitriou
                           memb_id=None):
537 c363a678 Kostas Papadimitriou
538 c363a678 Kostas Papadimitriou
    actions_map = {
539 9efd0075 Kostas Papadimitriou
        'remove': {
540 9efd0075 Kostas Papadimitriou
            'method': 'remove_membership',
541 9efd0075 Kostas Papadimitriou
            'msg': _(astakos_messages.USER_MEMBERSHIP_REMOVED)
542 9efd0075 Kostas Papadimitriou
        },
543 9efd0075 Kostas Papadimitriou
        'accept': {
544 9efd0075 Kostas Papadimitriou
            'method': 'accept_membership',
545 9efd0075 Kostas Papadimitriou
            'msg': _(astakos_messages.USER_MEMBERSHIP_ACCEPTED)
546 9efd0075 Kostas Papadimitriou
        },
547 9efd0075 Kostas Papadimitriou
        'reject': {
548 9efd0075 Kostas Papadimitriou
            'method': 'reject_membership',
549 9efd0075 Kostas Papadimitriou
            'msg': _(astakos_messages.USER_MEMBERSHIP_REJECTED)
550 9efd0075 Kostas Papadimitriou
        }
551 c363a678 Kostas Papadimitriou
    }
552 c363a678 Kostas Papadimitriou
553 9efd0075 Kostas Papadimitriou
554 c363a678 Kostas Papadimitriou
    if not action in actions_map.keys():
555 c363a678 Kostas Papadimitriou
        raise PermissionDenied
556 c363a678 Kostas Papadimitriou
557 9efd0075 Kostas Papadimitriou
    if memb_id:
558 9efd0075 Kostas Papadimitriou
        member_ids = [memb_id]
559 9efd0075 Kostas Papadimitriou
    else:
560 9efd0075 Kostas Papadimitriou
        member_ids = request.POST.getlist('members')
561 9efd0075 Kostas Papadimitriou
562 9efd0075 Kostas Papadimitriou
    project = get_object_or_404(Project, uuid=project_uuid)
563 c363a678 Kostas Papadimitriou
564 c363a678 Kostas Papadimitriou
    user = request.user
565 c363a678 Kostas Papadimitriou
    if not user.owns_project(project) and not user.is_project_admin():
566 c363a678 Kostas Papadimitriou
        return redirect(reverse('index'))
567 c363a678 Kostas Papadimitriou
568 9efd0075 Kostas Papadimitriou
    logger.info("Member(s) action from %s (project: %r, action: %s, "
569 9efd0075 Kostas Papadimitriou
                "members: %r)", user.log_display, project.uuid, action, member_ids)
570 c363a678 Kostas Papadimitriou
571 9efd0075 Kostas Papadimitriou
    action = actions_map.get(action)
572 9efd0075 Kostas Papadimitriou
    action_func = getattr(project_actions, action.get('method'))
573 c363a678 Kostas Papadimitriou
    for member_id in member_ids:
574 c363a678 Kostas Papadimitriou
        member_id = int(member_id)
575 c363a678 Kostas Papadimitriou
        with ExceptionHandler(request):
576 9efd0075 Kostas Papadimitriou
            with transaction.commit_on_success():
577 9efd0075 Kostas Papadimitriou
                try:
578 9efd0075 Kostas Papadimitriou
                    m = action_func(member_id, request.user)
579 9efd0075 Kostas Papadimitriou
                except ProjectError as e:
580 9efd0075 Kostas Papadimitriou
                    messages.error(request, e)
581 9efd0075 Kostas Papadimitriou
                else:
582 9efd0075 Kostas Papadimitriou
                    email = escape(m.person.email)
583 9efd0075 Kostas Papadimitriou
                    msg =  action.get('msg') % email
584 9efd0075 Kostas Papadimitriou
                    messages.success(request, msg)
585 c363a678 Kostas Papadimitriou
586 59eb6ec5 Kostas Papadimitriou
    return redirect_back(request, 'project_list')