blob: 397de86b2cbc015cf77f959319354d572ef58bf2 [file] [log] [blame]
# 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')