| # Copyright 2014 the Melange authors. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """Module containing the proposal related views for Summer Of Code.""" |
| |
| import httplib |
| import json |
| |
| from google.appengine.ext import ndb |
| |
| from django import forms as django_forms |
| from django import http |
| from django.utils import html |
| from django.utils import translation |
| |
| from melange.logic import profile as profile_logic |
| from melange.models import organization as org_model |
| from melange.request import access |
| from melange.request import exception |
| from melange.utils import lists as melange_lists |
| |
| from soc.logic import cleaning |
| from soc.modules.gsoc.views import base as gsoc_base |
| from soc.modules.gsoc.views import forms as gsoc_forms |
| from soc.modules.gsoc.views.helper import url_patterns as soc_url_patterns |
| from soc.views import base |
| from soc.views import template |
| from soc.views.helper import lists |
| from soc.views.helper import url_patterns |
| |
| from summerofcode.logic import profile as soc_profile_logic |
| from summerofcode.logic import proposal as proposal_logic |
| from summerofcode.models import proposal as proposal_model |
| from summerofcode.request import error |
| from summerofcode.request import links |
| from summerofcode.request import render |
| from summerofcode.templates import proposal_list |
| from summerofcode.views.helper import urls as soc_urls |
| |
| |
| PICK_ORGANIZATION_TO_SUBMIT_PROPOSAL_PAGE_NAME = translation.ugettext( |
| 'Pick organization to submit a proposal') |
| |
| PICK_ORGANIZATION_TO_SUBMIT_PROPOSAL_LIST_DESCRIPTION = translation.ugettext( |
| 'Pick organization to which to submit a proposal.') |
| |
| LIST_PROPOSALS_FOR_ORG_MEMBER_PAGE_NAME = translation.ugettext('List proposals') |
| |
| LIST_PROPOSALS_FOR_STUDENT_PAGE_NAME = translation.ugettext('List proposals') |
| |
| PROPOSAL_EDIT_PAGE_NAME = translation.ugettext('Edit proposal') |
| |
| PROPOSAL_SUBMIT_PAGE_NAME = translation.ugettext('Submit a new proposal') |
| |
| _PROPOSAL_FORM_ABSTRACT_MAX_LENGTH = 500 |
| |
| _PROPOSAL_FORM_ABSTRACT_LABEL = translation.ugettext('Abstract') |
| |
| _PROPOSAL_FORM_ABSTRACT_HELP_TEXT = translation.ugettext( |
| 'Short description, summary, or snippet for the proposal. This text may be ' |
| 'displayed publicly, so do not put in any information you are not ' |
| 'willing to share. Please enter up to %s characters.' % |
| _PROPOSAL_FORM_ABSTRACT_MAX_LENGTH) |
| |
| _PROPOSAL_FORM_ADDITIONAL_INFO_LABEL = translation.ugettext( |
| 'Additional Info URL') |
| |
| _PROPOSAL_FORM_ADDITIONAL_INFO_HELP_TEXT = translation.ugettext( |
| 'URL to a resource containing more information about your proposal.') |
| |
| _PROPOSAL_FORM_FULL_CONTENT_LABEL = translation.ugettext('Content') |
| |
| _PROPOSAL_FORM_FULL_CONTENT_HELP_TEXT = translation.ugettext( |
| 'Actual content of your proposal. This is most likely the most important ' |
| 'part which will be evaluated by the organization members.') |
| |
| _PROPOSAL_FORM_TITLE_LABEL = translation.ugettext('Title') |
| |
| _PROPOSAL_FORM_TITLE_HELP_TEXT = translation.ugettext( |
| 'Title of the proposal.') |
| |
| _PROPOSAL_FORM_TITLE_MAX_LENGTH = 75 |
| |
| _PROPOSAL_FORM_VISIBILITY_LABEL = translation.ugettext('Visibility') |
| |
| _PROPOSAL_FORM_VISIBILITY_HELP_TEXT = translation.ugettext( |
| 'Set who will be able to see this proposal.') |
| |
| _VISIBILITY_ORG_MEMBER_ID = 'org_member' |
| _VISIBILITY_STUDENT_ID = 'student' |
| _VISIBILITY_PUBLIC_ID = 'public' |
| |
| _VISIBILITY_ID_TO_ENUM_LINK = ( |
| (_VISIBILITY_PUBLIC_ID, proposal_model.Visibility.PUBLIC), |
| (_VISIBILITY_STUDENT_ID, proposal_model.Visibility.STUDENT), |
| (_VISIBILITY_ORG_MEMBER_ID, proposal_model.Visibility.ORG_MEMBER) |
| ) |
| _VISIBILITY_ID_TO_ENUM_MAP = dict(_VISIBILITY_ID_TO_ENUM_LINK) |
| _VISIBILITY_ID_ENUM_TO_ID_MAP = dict( |
| (v, k) for (k, v) in _VISIBILITY_ID_TO_ENUM_LINK) |
| |
| _VISIBILITY_CHOICES = ( |
| (_VISIBILITY_ORG_MEMBER_ID, translation.ugettext('Organization members')), |
| (_VISIBILITY_STUDENT_ID, translation.ugettext('All students')), |
| (_VISIBILITY_PUBLIC_ID, translation.ugettext('Public'))) |
| |
| _MESSAGE_ENROLLMENT_FORM_REQUIRED = translation.ugettext( |
| 'Having submitted a proof of enrollment is required, before you can ' |
| 'submit a new proposal.') |
| |
| _MESSAGE_PROPOSAL_LIMIT_REACHED = translation.ugettext( |
| 'You have reached the maximum number of proposals (%d) allowed ' |
| 'for this program.') |
| |
| _MESSAGE_PROPOSAL_CANNOT_BE_EDITED = translation.ugettext( |
| 'This proposal cannot be modified at this time.') |
| |
| |
| class ProposalForm(gsoc_forms.GSoCModelForm): |
| """Django form to show specific fields for a proposal.""" |
| |
| title = django_forms.CharField( |
| required=True, |
| label=_PROPOSAL_FORM_TITLE_LABEL, |
| help_text=_PROPOSAL_FORM_TITLE_HELP_TEXT, |
| max_length=_PROPOSAL_FORM_TITLE_MAX_LENGTH) |
| |
| abstract = django_forms.CharField( |
| required=True, |
| widget=django_forms.Textarea(), |
| label=_PROPOSAL_FORM_ABSTRACT_LABEL, |
| help_text=_PROPOSAL_FORM_ABSTRACT_HELP_TEXT, |
| max_length=_PROPOSAL_FORM_ABSTRACT_MAX_LENGTH) |
| |
| full_content = django_forms.CharField( |
| required=True, |
| widget=django_forms.Textarea(), |
| label=_PROPOSAL_FORM_FULL_CONTENT_LABEL, |
| help_text=_PROPOSAL_FORM_FULL_CONTENT_HELP_TEXT) |
| |
| additional_info = django_forms.URLField( |
| required=False, |
| label=_PROPOSAL_FORM_ADDITIONAL_INFO_LABEL, |
| help_text=_PROPOSAL_FORM_ADDITIONAL_INFO_HELP_TEXT) |
| |
| visibility = django_forms.CharField( |
| required=True, |
| widget=django_forms.Select(choices=_VISIBILITY_CHOICES), |
| label=_PROPOSAL_FORM_VISIBILITY_LABEL, |
| help_text=_PROPOSAL_FORM_VISIBILITY_HELP_TEXT) |
| |
| clean_full_content = cleaning.clean_html_content('full_content') |
| |
| |
| def _adoptProposalPropertiesForForm(proposal_properties): |
| """Adopts properties of a proposal entity, which are persisted in datastore, |
| to representation which may be passed to populate ProposalForm. |
| |
| Args: |
| proposal_properties: A dict containing proposal properties as persisted |
| in datastore. |
| |
| Returns: |
| A dict mapping properties of proposal model to values which can be |
| populated to a proposal form. |
| """ |
| return { |
| 'title': proposal_properties['title'], |
| 'abstract': proposal_properties['abstract'], |
| 'full_content': proposal_properties['content'], |
| 'additional_info': proposal_properties.get('additional_info'), |
| 'visibility': _VISIBILITY_ID_ENUM_TO_ID_MAP.get( |
| proposal_properties['visibility']), |
| } |
| |
| |
| def _adaptProposalPropertiesForDatastore(form_data): |
| """Adopts properties corresponding to proposal's properties, which |
| have been submitted in a form, to the format that is compliant with |
| proposal_model.Proposal model. |
| |
| Args: |
| form_data: A dict containing data submitted in a form. |
| |
| Returns: |
| A dict mapping properties of proposal model to values based on |
| data submitted in a form. |
| """ |
| return { |
| proposal_model.Proposal.title._name: form_data.get('title'), |
| proposal_model.Proposal.abstract._name: form_data.get('abstract'), |
| proposal_model.Proposal.content._name: form_data.get('full_content'), |
| proposal_model.Proposal.additional_info._name: |
| form_data.get('additional_info') or None, |
| proposal_model.Proposal.visibility._name: _VISIBILITY_ID_TO_ENUM_MAP.get( |
| form_data.get('visibility')) |
| } |
| |
| |
| class PickOrganizationToSubmitProposalListRowRedirect( |
| melange_lists.RedirectCustomRow): |
| """Class which provides redirects for rows of pick organization to submit |
| a proposal list.""" |
| |
| def __init__(self, data, url_names): |
| """Initializes a new instance of the row redirect. |
| |
| Args: |
| data: request_data.RequestData for the current request. |
| url_names: Instance of url_names.UrlNames. |
| """ |
| super(PickOrganizationToSubmitProposalListRowRedirect, self).__init__() |
| self.data = data |
| self.url_names = url_names |
| |
| def getLink(self, item): |
| """See lists.RedirectCustomRow.getLink for specification.""" |
| org_key = ndb.Key( |
| self.data.models.ndb_org_model._get_kind(), item['columns']['key']) |
| return links.SOC_LINKER.organization( |
| org_key, self.url_names.PROPOSAL_SUBMIT) |
| |
| |
| # TODO(daniel): replace this class with a new style list |
| class PickOrganizationToSubmitProposalList(template.Template): |
| """List of organizations to which a proposal may be submitted.""" |
| |
| def __init__(self, data): |
| """See template.Template.__init__ for specification.""" |
| super(PickOrganizationToSubmitProposalList, self).__init__(data) |
| self._list_config = lists.ListConfiguration() |
| self._list_config.addSimpleColumn('org_id', 'Organization ID', hidden=True) |
| self._list_config.addPlainTextColumn( |
| 'name', 'Name', lambda e, *args: e.name.strip()) |
| |
| def templatePath(self): |
| """See template.Template.templatePath for specification.""" |
| # TODO(daniel): this path should not be hardcoded to gsoc |
| return 'modules/gsoc/admin/_accepted_orgs_list.html' |
| |
| def context(self): |
| """See template.Template.context for specification.""" |
| description = PICK_ORGANIZATION_TO_SUBMIT_PROPOSAL_LIST_DESCRIPTION |
| |
| list_configuration_response = lists.ListConfigurationResponse( |
| self.data, self._list_config, 0, description) |
| |
| return { |
| 'lists': [list_configuration_response], |
| } |
| |
| # TODO(daniel): add requirement that forms should have been submitted. |
| PICK_ORGANIZATION_TO_SUBMIT_PROPOSAL_ACCESS_CHECKER = ( |
| access.ConjuctionAccessChecker([ |
| access.PROGRAM_ACTIVE_ACCESS_CHECKER, |
| access.STUDENT_PROFILE_ACCESS_CHECKER])) |
| |
| class PickOrganizationToSubmitProposalPage(base.RequestHandler): |
| """Page for student users to pick organization to which |
| to submit a proposal.""" |
| |
| access_checker = PICK_ORGANIZATION_TO_SUBMIT_PROPOSAL_ACCESS_CHECKER |
| |
| def __init__(self, initializer, linker, renderer, error_handler, |
| url_pattern_constructor, url_names, template_path): |
| """Initializes a new instance of the request handler for the specified |
| parameters. |
| |
| Args: |
| initializer: Implementation of initialize.Initializer interface. |
| linker: Instance of links.Linker class. |
| renderer: Implementation of render.Renderer interface. |
| error_handler: Implementation of error.ErrorHandler interface. |
| url_pattern_constructor: |
| Implementation of url_patterns.UrlPatternConstructor. |
| url_names: Instance of url_names.UrlNames. |
| template_path: The path of the template to be used. |
| """ |
| super(PickOrganizationToSubmitProposalPage, self).__init__( |
| initializer, linker, renderer, error_handler) |
| self.url_pattern_constructor = url_pattern_constructor |
| self.url_names = url_names |
| self.template_path = template_path |
| |
| def templatePath(self): |
| """See base.RequestHandler.templatePath for specification.""" |
| return self.template_path |
| |
| def djangoURLPatterns(self): |
| """See base.RequestHandler.djangoURLPatterns for specification.""" |
| return [ |
| self.url_pattern_constructor.construct( |
| r'proposal/pick/%s$' % url_patterns.PROGRAM, |
| self, name=self.url_names.PROPOSAL_PICK_ORG), |
| ] |
| |
| def jsonContext(self, data, check, mutator): |
| """See base.RequestHandler.jsonContext for specification.""" |
| query = data.models.ndb_org_model.query( |
| org_model.Organization.program == |
| ndb.Key.from_old_key(data.program.key()), |
| org_model.Organization.status == org_model.Status.ACCEPTED) |
| |
| response = melange_lists.JqgridResponse( |
| melange_lists.ORGANIZATION_LIST_ID, |
| row=PickOrganizationToSubmitProposalListRowRedirect( |
| data, self.url_names)) |
| |
| start = html.escape(data.GET.get('start', '')) |
| |
| return response.getData(query, start=start) |
| |
| def context(self, data, check, mutator): |
| """See base.RequestHandler.context for specification.""" |
| return { |
| 'page_name': PICK_ORGANIZATION_TO_SUBMIT_PROPOSAL_PAGE_NAME, |
| 'accepted_orgs_list': PickOrganizationToSubmitProposalList(data), |
| } |
| |
| PICK_ORGANIZATION_TO_SUBMIT_PROPOSAL_PAGE = ( |
| PickOrganizationToSubmitProposalPage( |
| gsoc_base._GSOC_INITIALIZER, links.SOC_LINKER, render.SOC_RENDERER, |
| error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR, |
| soc_urls.UrlNames, 'modules/gsoc/accepted_orgs/base.html')) |
| |
| |
| class ProposalLimitNotReachedAccessChecker(access.AccessChecker): |
| """AccessChecker that ensures that the currently logged in student has not |
| reached the limit of submitted proposals which is defined for the program. |
| """ |
| |
| def checkAccess(self, data, check): |
| """See AccessChecker.checkAccess for specification.""" |
| if proposal_logic.isProposalLimitReached(data.ndb_profile, data.program): |
| raise exception.Forbidden( |
| message=_MESSAGE_PROPOSAL_LIMIT_REACHED |
| % data.program.apps_tasks_limit) |
| |
| |
| class EnrollmentFormSubmittedAccessChecker(access.AccessChecker): |
| """AccessChecker that ensures that the currently logged in student has |
| already submitted their enrollment form. |
| """ |
| |
| def checkAccess(self, data, check): |
| """See AccessChecker.checkAccess for specification.""" |
| if not profile_logic.enrollmentFormSubmitted(data.ndb_profile): |
| raise exception.Forbidden(message=_MESSAGE_ENROLLMENT_FORM_REQUIRED) |
| |
| |
| PROPOSAL_SUBMIT_ACCESS_CHECKER = access.ConjuctionAccessChecker([ |
| access.STUDENT_PROFILE_ACCESS_CHECKER, |
| access.UrlOrgStatusAccessChecker([org_model.Status.ACCEPTED]), |
| access.STUDENT_SIGNUP_ACTIVE_ACCESS_CHECKER, |
| ProposalLimitNotReachedAccessChecker(), |
| EnrollmentFormSubmittedAccessChecker()]) |
| |
| class ProposalSubmitPage(base.RequestHandler): |
| """Page for students to submit a proposal.""" |
| |
| access_checker = PROPOSAL_SUBMIT_ACCESS_CHECKER |
| |
| def __init__(self, initializer, linker, renderer, error_handler, |
| url_pattern_constructor, url_names, template_path): |
| """Initializes a new instance of the request handler for the specified |
| parameters. |
| |
| Args: |
| initializer: Implementation of initialize.Initializer interface. |
| linker: Instance of links.Linker class. |
| renderer: Implementation of render.Renderer interface. |
| error_handler: Implementation of error.ErrorHandler interface. |
| url_pattern_constructor: |
| Implementation of url_patterns.UrlPatternConstructor. |
| url_names: Instance of url_names.UrlNames. |
| template_path: The path of the template to be used. |
| """ |
| super(ProposalSubmitPage, self).__init__( |
| initializer, linker, renderer, error_handler) |
| self.url_pattern_constructor = url_pattern_constructor |
| self.url_names = url_names |
| self.template_path = template_path |
| |
| def djangoURLPatterns(self): |
| """See base.RequestHandler.djangoURLPatterns for specification.""" |
| return [ |
| self.url_pattern_constructor.construct( |
| r'proposal/submit/%s$' % url_patterns.ORG, |
| self, name=self.url_names.PROPOSAL_SUBMIT), |
| ] |
| |
| def templatePath(self): |
| """See base.RequestHandler.templatePath for specification.""" |
| return self.template_path |
| |
| def context(self, data, check, mutator): |
| """See base.RequestHandler.context for specification.""" |
| initial = {'full_content': data.url_ndb_org.contrib_template} |
| form = ProposalForm(data=data.POST, initial=initial) |
| return { |
| 'page_name': PROPOSAL_SUBMIT_PAGE_NAME, |
| 'forms': [form], |
| } |
| |
| def post(self, data, check, mutator): |
| """See base.RequestHandler.post for specification.""" |
| form = ProposalForm(data=data.POST) |
| if not form.is_valid(): |
| # TODO(nathaniel): problematic self-use. |
| return self.get(data, check, mutator) |
| else: |
| proposal_properties = ( |
| _adaptProposalPropertiesForDatastore(form.cleaned_data)) |
| proposal_properties['organization'] = data.url_ndb_org.key |
| |
| profiles_to_notify = ( |
| soc_profile_logic.getProfilesForNewProposalNotification( |
| data.url_ndb_org.key)) |
| |
| result = proposal_logic.createProposal( |
| data.ndb_profile.key, data.url_ndb_org, data.program, |
| proposal_properties, profiles_to_notify=profiles_to_notify, |
| site=data.site, url_names=self.url_names) |
| if not result: |
| raise exception.BadRequest(message=result.extra) |
| else: |
| url = links.SOC_LINKER.userId( |
| data.ndb_profile.key, result.extra.key.id(), |
| self.url_names.PROPOSAL_EDIT) + '?validated=True' |
| return http.HttpResponseRedirect(url) |
| |
| PROPOSAL_SUBMIT_PAGE = ProposalSubmitPage( |
| gsoc_base._GSOC_INITIALIZER, links.SOC_LINKER, render.SOC_RENDERER, |
| error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR, |
| soc_urls.UrlNames, 'summerofcode/proposal/proposal_edit.html') |
| |
| |
| class IsUrlProposalEditableAccessChecker(access.AccessChecker): |
| """AccessChecker that ensures that the proposal which is specified in |
| the URL is currently editable. |
| """ |
| |
| def checkAccess(self, data, check): |
| """See AccessChecker.checkAccess for specification.""" |
| if not proposal_logic.canStudentUpdateProposal( |
| data.url_ndb_proposal, data.program.timeline): |
| raise exception.Forbidden(message=_MESSAGE_PROPOSAL_CANNOT_BE_EDITED) |
| |
| |
| class ProposalEditPage(base.RequestHandler): |
| """Page for students to edit a proposal.""" |
| |
| access_checker = access.ConjuctionAccessChecker([ |
| access.IS_URL_USER_ACCESS_CHECKER, |
| IsUrlProposalEditableAccessChecker()]) |
| |
| def __init__(self, initializer, linker, renderer, error_handler, |
| url_pattern_constructor, url_names, template_path): |
| """Initializes a new instance of the request handler for the specified |
| parameters. |
| |
| Args: |
| initializer: Implementation of initialize.Initializer interface. |
| linker: Instance of links.Linker class. |
| renderer: Implementation of render.Renderer interface. |
| error_handler: Implementation of error.ErrorHandler interface. |
| url_pattern_constructor: |
| Implementation of url_patterns.UrlPatternConstructor. |
| url_names: Instance of url_names.UrlNames. |
| template_path: The path of the template to be used. |
| """ |
| super(ProposalEditPage, self).__init__( |
| initializer, linker, renderer, error_handler) |
| self.url_pattern_constructor = url_pattern_constructor |
| self.url_names = url_names |
| self.template_path = template_path |
| |
| def djangoURLPatterns(self): |
| """See base.RequestHandler.djangoURLPatterns for specification.""" |
| return [ |
| self.url_pattern_constructor.construct( |
| r'proposal/edit/%s$' % url_patterns.USER_ID, |
| self, name=self.url_names.PROPOSAL_EDIT), |
| ] |
| |
| def templatePath(self): |
| """See base.RequestHandler.templatePath for specification.""" |
| return self.template_path |
| |
| def context(self, data, check, mutator): |
| """See base.RequestHandler.context for specification.""" |
| form_data = _adoptProposalPropertiesForForm(data.url_ndb_proposal.to_dict()) |
| form = ProposalForm(data=data.POST or form_data) |
| return { |
| 'forms': [form], |
| 'page_name': PROPOSAL_EDIT_PAGE_NAME, |
| } |
| |
| def post(self, data, check, mutator): |
| """See base.RequestHandler.post for specification.""" |
| form = ProposalForm(data=data.POST) |
| if not form.is_valid(): |
| # TODO(nathaniel): problematic self-use. |
| return self.get(data, check, mutator) |
| else: |
| proposal_properties = ( |
| _adaptProposalPropertiesForDatastore(form.cleaned_data)) |
| |
| result = proposal_logic.updateProposal( |
| data.url_ndb_proposal.key, proposal_properties) |
| if not result: |
| raise exception.BadRequest(message=result.extra) |
| else: |
| url = links.SOC_LINKER.userId( |
| data.ndb_profile.key, result.extra.key.id(), |
| self.url_names.PROPOSAL_REVIEW_AS_STUDENT) + '?validated=True' |
| return http.HttpResponseRedirect(url) |
| |
| PROPOSAL_EDIT_PAGE = ProposalEditPage( |
| gsoc_base._GSOC_INITIALIZER, links.SOC_LINKER, render.SOC_RENDERER, |
| error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR, |
| soc_urls.UrlNames, 'summerofcode/proposal/proposal_edit.html') |
| |
| |
| class ListProposalsForStudent(base.RequestHandler): |
| """View to list all proposals for a student.""" |
| |
| access_checker = access.STUDENT_PROFILE_ACCESS_CHECKER |
| |
| def __init__(self, initializer, linker, renderer, error_handler, |
| url_pattern_constructor, url_names, template_path): |
| """Initializes a new instance of the request handler for the specified |
| parameters. |
| |
| Args: |
| initializer: Implementation of initialize.Initializer interface. |
| linker: Instance of links.Linker class. |
| renderer: Implementation of render.Renderer interface. |
| error_handler: Implementation of error.ErrorHandler interface. |
| url_pattern_constructor: |
| Implementation of url_patterns.UrlPatternConstructor. |
| url_names: Instance of url_names.UrlNames. |
| template_path: The path of the template to be used. |
| """ |
| super(ListProposalsForStudent, self).__init__( |
| initializer, linker, renderer, error_handler) |
| self.url_pattern_constructor = url_pattern_constructor |
| self.url_names = url_names |
| self.template_path = template_path |
| |
| def djangoURLPatterns(self): |
| """See base.RequestHandler.djangoURLPatterns for specification.""" |
| return [ |
| self.url_pattern_constructor.construct( |
| r'proposal/list/student/%s$' % url_patterns.PROGRAM, |
| self, name=self.url_names.PROPOSAL_LIST_FOR_STUDENT) |
| ] |
| |
| def templatePath(self): |
| """See base.RequestHandler.templatePath for specification.""" |
| return self.template_path |
| |
| def context(self, data, check, mutator): |
| """See base.RequestHandler.context for specification.""" |
| return { |
| 'list': proposal_list.getProposalListForStudent( |
| data, data.ndb_profile.key), |
| 'page_name': LIST_PROPOSALS_FOR_STUDENT_PAGE_NAME, |
| } |
| |
| def jsonContext(self, data, check, mutator): |
| """See base.RequestHandler.jsonContext for specification.""" |
| list_data = proposal_list.getProposalListForStudent( |
| data, data.ndb_profile.key).getListData() |
| if list_data: |
| return list_data.content() |
| else: |
| raise exception.BadRequest(message='This data cannot be accessed.') |
| |
| def post(self, data, check, mutator): |
| list_data = proposal_list.getProposalListForStudent( |
| data, data.ndb_profile.key) |
| result = list_data.postStudentProposalList() |
| |
| if result: |
| return http.HttpResponse() |
| else: |
| return http.HttpResponse( |
| json.dumps(result.extra), mimetype="application/json", |
| status=httplib.BAD_REQUEST) |
| |
| LIST_PROPOSALS_FOR_STUDENT = ListProposalsForStudent( |
| gsoc_base._GSOC_INITIALIZER, links.SOC_LINKER, render.SOC_RENDERER, |
| error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR, |
| soc_urls.UrlNames, 'summerofcode/list.html') |
| |
| |
| class ListProposalsForOrgMember(base.RequestHandler): |
| """View to list all proposals for an organization member.""" |
| |
| # TODO(daniel): this is accessible to all people with non-student profiles. |
| # it is fine, because it will list nothing to users who are not actual mentors |
| # but maybe this should be visible to actual mentors? |
| access_checker = access.NON_STUDENT_PROFILE_ACCESS_CHECKER |
| |
| def __init__(self, initializer, linker, renderer, error_handler, |
| url_pattern_constructor, url_names, template_path): |
| """Initializes a new instance of the request handler for the specified |
| parameters. |
| |
| Args: |
| initializer: Implementation of initialize.Initializer interface. |
| linker: Instance of links.Linker class. |
| renderer: Implementation of render.Renderer interface. |
| error_handler: Implementation of error.ErrorHandler interface. |
| url_pattern_constructor: |
| Implementation of url_patterns.UrlPatternConstructor. |
| url_names: Instance of url_names.UrlNames. |
| template_path: The path of the template to be used. |
| """ |
| super(ListProposalsForOrgMember, self).__init__( |
| initializer, linker, renderer, error_handler) |
| self.url_pattern_constructor = url_pattern_constructor |
| self.url_names = url_names |
| self.template_path = template_path |
| |
| def djangoURLPatterns(self): |
| """See base.RequestHandler.djangoURLPatterns for specification.""" |
| return [ |
| self.url_pattern_constructor.construct( |
| r'proposal/list/org/%s$' % url_patterns.PROGRAM, |
| self, name=self.url_names.PROPOSAL_LIST_FOR_ORG_MEMBER) |
| ] |
| |
| def templatePath(self): |
| """See base.RequestHandler.templatePath for specification.""" |
| return self.template_path |
| |
| def context(self, data, check, mutator): |
| """See base.RequestHandler.context for specification.""" |
| return { |
| 'list': proposal_list.getProposalListForOrgMember( |
| data, data.ndb_profile), |
| 'page_name': LIST_PROPOSALS_FOR_ORG_MEMBER_PAGE_NAME, |
| } |
| |
| def jsonContext(self, data, check, mutator): |
| """See base.RequestHandler.jsonContext for specification.""" |
| list_data = proposal_list.getProposalListForOrgMember( |
| data, data.ndb_profile).getListData() |
| if list_data: |
| return list_data.content() |
| else: |
| raise exception.BadRequest(message='This data cannot be accessed.') |
| |
| def post(self, data, check, mutator): |
| """See base.RequestHandler.post for specification.""" |
| json_data = data.POST.get('data') |
| if json_data: |
| parsed_data = json.loads(json_data) |
| |
| button_id = data.POST.get('button_id') |
| if not button_id: |
| raise exception.BadRequest(message='Missing button ID') |
| elif button_id == proposal_list.SAVE_BUTTON_ID: |
| # the first and the only key is string representation of proposal key |
| if len(parsed_data.keys()) != 1: |
| raise exception.BadRequest(message='Invalid data format.') |
| else: |
| urlsafe_key = parsed_data.keys()[0] |
| proposal_key = ndb.Key(urlsafe=urlsafe_key) |
| proposal = proposal_key.get() |
| if not proposal: |
| raise exception.BadRequest(message='Proposal does not exist.') |
| else: |
| proposal_logic.setExtraData( |
| proposal.key, proposal.organization.get(), |
| parsed_data[urlsafe_key]) |
| return http.HttpResponse() |
| else: |
| raise exception.BadRequest(message='Missing data') |
| |
| LIST_PROPOSALS_FOR_ORG_MEMBER = ListProposalsForOrgMember( |
| gsoc_base._GSOC_INITIALIZER, links.SOC_LINKER, render.SOC_RENDERER, |
| error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR, |
| soc_urls.UrlNames, 'summerofcode/list.html') |