Fixes an issue where the organization home page would throw a 505 when no projects...
[Melange.git] / app / soc / views / models / org_app.py
blob229d15c2347d332e7418ba1f0d55667b2d64f6c1
1 #!/usr/bin/python2.5
3 # Copyright 2009 the Melange authors.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Views for Organization App profiles.
18 """
20 __authors__ = [
21 '"Lennard de Rijk" <ljvderijk@gmail.com>',
25 import os
27 from django import forms
28 from django.utils import simplejson
30 from soc.logic import accounts
31 from soc.logic import cleaning
32 from soc.logic import dicts
33 from soc.logic import mail_dispatcher
34 from soc.logic import models as model_logic
35 from soc.logic.models import program as program_logic
36 from soc.logic.models import org_app as org_app_logic
37 from soc.views import helper
38 from soc.views.helper import access
39 from soc.views.helper import decorators
40 from soc.views.helper import redirects
41 from soc.views.helper import responses
42 from soc.views.helper import widgets
43 from soc.views.models import group_app
44 from soc.views.models import program as program_view
47 class View(group_app.View):
48 """View methods for the Organization Application model.
49 """
51 def __init__(self, params=None):
52 """Defines the fields and methods required for the base View class
53 to provide the user with list, public, create, edit and delete views.
55 Params:
56 params: a dict with params for this View
57 """
59 rights = access.Checker(params)
60 rights['create'] = ['checkIsDeveloper']
61 rights['delete'] = [('checkCanEditGroupApp',
62 [org_app_logic.logic]),
63 ('checkIsActivePeriod', ['org_signup', 'scope_path'])]
64 rights['edit'] = [('checkCanEditGroupApp',
65 [org_app_logic.logic]),
66 ('checkIsActivePeriod', ['org_signup', 'scope_path'])]
67 rights['list'] = ['checkIsDeveloper']
68 rights['list_self'] = ['checkIsUser']
69 rights['show'] = ['allow']
70 rights['review'] = ['checkIsHostForProgramInScope',
71 ('checkCanReviewGroupApp', [org_app_logic.logic])]
72 rights['review_overview'] = ['checkIsHostForProgramInScope']
73 rights['bulk_accept'] = ['checkIsHostForProgramInScope']
74 rights['bulk_reject'] = ['checkIsHostForProgramInScope']
75 rights['apply'] = ['checkIsUser',
76 ('checkCanCreateOrgApp', ['org_signup']),
77 'checkIsNotStudentForProgramInScope']
79 new_params = {}
81 new_params['rights'] = rights
82 new_params['logic'] = org_app_logic.logic
84 new_params['scope_view'] = program_view
85 new_params['scope_redirect'] = redirects.getCreateRedirect
87 new_params['sidebar_grouping'] = 'Organizations'
89 new_params['list_key_order'] = [
90 'link_id', 'scope_path', 'name', 'home_page', 'email',
91 'description', 'why_applying','pub_mailing_list','irc_channel',
92 'member_criteria', 'prior_participation', 'prior_application',
93 'license_name', 'ideas', 'dev_mailing_list', 'contrib_template',
94 'contrib_disappears', 'member_disappears', 'encourage_contribs',
95 'continued_contribs']
97 patterns = [(r'^%(url_name)s/(?P<access_type>apply)/%(scope)s$',
98 'soc.views.models.%(module_name)s.create',
99 'Create an %(name_plural)s'),
100 (r'^%(url_name)s/(?P<access_type>bulk_accept)/%(scope)s$',
101 'soc.views.models.%(module_name)s.bulk_accept',
102 'Bulk Acceptation of %(name_plural)s'),
103 (r'^%(url_name)s/(?P<access_type>bulk_reject)/%(scope)s$',
104 'soc.views.models.%(module_name)s.bulk_reject',
105 'Bulk Rejection of %(name_plural)s'),]
107 new_params['extra_django_patterns'] = patterns
108 new_params['extra_key_order'] = ['admin_agreement',
109 'agreed_to_admin_agreement']
111 new_params['extra_dynaexclude'] = ['applicant', 'backup_admin', 'status',
112 'created_on', 'last_modified_on']
114 new_params['create_dynafields'] = [
115 {'name': 'link_id',
116 'base': forms.fields.CharField,
117 'label': 'Organization Link ID',
121 new_params['create_extra_dynaproperties'] = {
122 'scope_path': forms.fields.CharField(widget=forms.HiddenInput,
123 required=True),
124 'contrib_template': forms.fields.CharField(
125 widget=helper.widgets.FullTinyMCE(
126 attrs={'rows': 25, 'cols': 100})),
127 'description': forms.fields.CharField(
128 widget=helper.widgets.FullTinyMCE(
129 attrs={'rows': 25, 'cols': 100})),
130 'admin_agreement': forms.fields.Field(required=False,
131 widget=widgets.AgreementField),
132 'agreed_to_admin_agreement': forms.fields.BooleanField(
133 initial=False, required=True),
135 'clean_description': cleaning.clean_html_content('description'),
136 'clean_contrib_template': cleaning.clean_html_content(
137 'contrib_template'),
138 'clean_ideas': cleaning.clean_url('ideas'),
139 'clean': cleaning.validate_new_group('link_id', 'scope_path',
140 model_logic.organization, org_app_logic)}
142 # get rid of the clean method
143 new_params['edit_extra_dynaproperties'] = {
144 'clean': (lambda x: x.cleaned_data)}
146 new_params['name'] = "Organization Application"
147 new_params['name_plural'] = "Organization Applications"
148 new_params['name_short'] = "Org App"
149 new_params['url_name'] = "org_app"
150 new_params['group_name'] = "Organization"
151 new_params['group_url_name'] = 'org'
153 new_params['review_template'] = 'soc/org_app/review.html'
154 # TODO use a proper template that works for each program
155 new_params['accepted_mail_template'] = \
156 'soc/org_app/mail/accepted_gsoc2009.html'
157 new_params['rejected_mail_template'] = 'soc/org_app/mail/rejected.html'
159 params = dicts.merge(params, new_params)
161 super(View, self).__init__(params=params)
163 @ decorators.merge_params
164 def reviewOverview(self, request, access_type,
165 page_name=None, params=None, **kwargs):
166 """View that allows to see organization application review overview.
168 For Args see base.View.public().
171 params['list_template'] = 'soc/org_app/review_overview.html'
172 context = {
173 'bulk_accept_link': '/org_app/bulk_accept/%(scope_path)s' % (kwargs),
174 'bulk_reject_link': '/org_app/bulk_reject/%(scope_path)s' % (kwargs),}
176 return super(View, self).reviewOverview(request, access_type,
177 page_name=page_name, params=params, context=context, **kwargs)
179 def _editContext(self, request, context):
180 """See base.View._editContext.
183 entity = context['entity']
184 form = context['form']
186 if 'scope_path' in form.initial:
187 scope_path = form.initial['scope_path']
188 elif 'scope_path' in request.POST:
189 scope_path = request.POST['scope_path']
190 else:
191 del form.fields['admin_agreement']
192 return
194 entity = program_logic.logic.getFromKeyName(scope_path)
196 if not (entity and entity.org_admin_agreement):
197 return
199 agreement = entity.org_admin_agreement
201 content = agreement.content
202 params = {'url_name': 'document'}
204 widget = form.fields['admin_agreement'].widget
205 widget.text = content
206 widget.url = redirects.getPublicRedirect(agreement, params)
208 def _review(self, request, params, app_entity, status, **kwargs):
209 """Sends out an email if an org_app has been accepted or rejected.
211 For params see group_app.View._review().
214 if status == 'accepted' or status == 'rejected':
216 default_sender = mail_dispatcher.getDefaultMailSender()
218 if not default_sender:
219 # no default sender abort
220 return
221 else:
222 (sender_name, sender) = default_sender
224 # construct the contents of the email
225 admin_entity = app_entity.applicant
226 backup_entity = app_entity.backup_admin
228 context = {
229 'sender': sender,
230 'sender_name': sender_name,
231 'program_name': app_entity.scope.name,
232 'org_app_name': app_entity.name
235 if status == 'accepted':
236 # use the accepted template and subject
237 template = params['accepted_mail_template']
238 context['subject'] = 'Congratulations!'
239 context['HTTP_host'] = 'http://%s' % (os.environ['HTTP_HOST'])
240 elif status == 'rejected':
241 # use the rejected template and subject
242 template = params['rejected_mail_template']
243 context['subject'] = 'Thank you for your application'
245 for to in [admin_entity, backup_entity]:
246 if not to:
247 continue
249 email = accounts.denormalizeAccount(to.account).email()
250 context['to'] = email
251 context['to_name'] = to.name
253 # send out the constructed email
254 mail_dispatcher.sendMailFromTemplate(template, context)
256 @decorators.merge_params
257 @decorators.check_access
258 def bulkAccept(self, request, access_type,
259 page_name=None, params=None, **kwargs):
260 """Returns a HTTP Response containing JSON information needed
261 to bulk-accept orgs.
264 program_keyname = kwargs['scope_path']
265 return self._bulkReview(request, params, 'pre-accepted', 'accepted',
266 program_keyname)
268 @decorators.merge_params
269 @decorators.check_access
270 def bulkReject(self, request, access_type,
271 page_name=None, params=None, **kwargs):
272 """Returns a HTTP Response containing JSON information needed
273 to bulk-accept orgs.
276 program_keyname = kwargs['scope_path']
277 return self._bulkReview(request, params, 'pre-rejected', 'rejected',
278 program_keyname)
280 def _bulkReview(self, request, params, from_status, to_status,
281 program_keyname):
282 """Returns a HTTP Response containing JSON information needed
283 to bulk-review organization applications.
285 Args:
286 request: Standard Django HTTP Request object
287 params: Params for this view
288 from_status: The status for the applications which should
289 be reviewed (can be a list)
290 to_status: The status to which all applications should be changed to
291 program_keyname: The keyname for the program to which
292 the application belongs
295 # get the program entity from the keyname
296 program_entity = program_logic.logic.getFromKeyName(program_keyname)
298 # get all the organization applications for the
299 # given program and from_status
300 filter = {'scope': program_entity,
301 'status': from_status}
303 org_app_entities = params['logic'].getForFields(filter=filter)
305 # convert each application into a dictionary containing only the fields
306 # given by the dict_filter
307 dict_filter = ['link_id', 'name']
308 org_apps = [dicts.filter(i.toDict(), dict_filter) for i in org_app_entities]
310 to_json = {
311 'program' : program_entity.name,
312 'nr_applications' : len(org_apps),
313 'application_type' : params['name_plural'],
314 'applications': org_apps,
315 'link' : '/%s/review/%s/(link_id)?status=%s' %(
316 params['url_name'] ,program_entity.key().id_or_name(), to_status),
319 json = simplejson.dumps(to_json)
321 # use the standard JSON template to return our response
322 context = {'json': json}
323 template = 'soc/json.html'
325 return responses.respond(request, template, context)
328 view = View()
330 admin = decorators.view(view.admin)
331 bulk_accept = decorators.view(view.bulkAccept)
332 bulk_reject = decorators.view(view.bulkReject)
333 create = decorators.view(view.create)
334 delete = decorators.view(view.delete)
335 edit = decorators.view(view.edit)
336 list = decorators.view(view.list)
337 list_self = decorators.view(view.listSelf)
338 public = decorators.view(view.public)
339 export = decorators.view(view.export)
340 review = decorators.view(view.review)
341 review_overview = decorators.view(view.reviewOverview)