| # 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 project related views for Summer Of Code.""" |
| |
| from django import forms as django_forms |
| from django import http |
| from django.utils import translation |
| |
| from melange.logic import profile as profile_logic |
| from melange.request import access |
| from melange.request import exception |
| |
| from soc.logic import cleaning |
| from soc.modules.gsoc.views import assign_mentor |
| from soc.modules.gsoc.views import forms as gsoc_forms |
| from soc.modules.gsoc.views import base as gsoc_base |
| from soc.modules.gsoc.views.helper import url_patterns as soc_url_patterns |
| from soc.views import base |
| from soc.views import base_templates |
| from soc.views.helper import url_patterns |
| |
| from summerofcode.logic import project as project_logic |
| from summerofcode.models import project as project_model |
| from summerofcode.request import access as soc_access |
| from summerofcode.request import error |
| from summerofcode.request import links |
| from summerofcode.request import render |
| from summerofcode.templates import project_list |
| from summerofcode.templates import sidebar_actions |
| from summerofcode.views.helper import urls as soc_urls |
| |
| |
| _MANAGE_ACTIONS_TITLE = translation.ugettext('Project Actions') |
| |
| _PROJECT_DETAILS_PAGE_NAME = translation.ugettext('Project details') |
| _PROJECT_EDIT_PAGE_NAME = translation.ugettext('Edit Project') |
| _PROJECT_LIST_PUBLIC_PAGE_NAME = translation.ugettext('Accepted projects - %s') |
| |
| _PROJECT_FORM_TITLE_LABEL = translation.ugettext('Title') |
| |
| _PROJECT_FORM_TITLE_HELP_TEXT = translation.ugettext('Title of the project') |
| |
| _PROJECT_FORM_TITLE_MAX_LENGTH = 75 |
| |
| _PROJECT_FORM_ABSTRACT_LABEL = translation.ugettext('Abstract') |
| |
| _PROJECT_FORM_ABSTRACT_MAX_LENGTH = 500 |
| |
| _PROJECT_FORM_ABSTRACT_HELP_TEXT = translation.ugettext( |
| 'Short description, summary, or snippet for the project. ' |
| 'This part is displayed publicly. Please enter up to %s characters.' |
| % _PROJECT_FORM_ABSTRACT_MAX_LENGTH) |
| |
| _PROJECT_FORM_FULL_CONTENT_LABEL = translation.ugettext('Content') |
| |
| _PROJECT_FORM_FULL_CONTENT_HELP_TEXT = translation.ugettext( |
| 'Additional information about this project to be shown publicly.') |
| |
| _PROJECT_FORM_ADDITIONAL_INFO_LABEL = translation.ugettext( |
| 'Additional Info URL') |
| |
| _PROJECT_FORM_ADDITIONAL_INFO_HELP_TEXT = translation.ugettext( |
| 'URL to a resource containing more information about your project.') |
| |
| class ProjectForm(gsoc_forms.GSoCModelForm): |
| """Django form to show specific fields for a project.""" |
| |
| title = django_forms.CharField( |
| required=True, |
| label=_PROJECT_FORM_TITLE_LABEL, |
| help_text=_PROJECT_FORM_TITLE_HELP_TEXT, |
| max_length=_PROJECT_FORM_TITLE_MAX_LENGTH) |
| |
| abstract = django_forms.CharField( |
| required=True, |
| widget=django_forms.Textarea(), |
| label=_PROJECT_FORM_ABSTRACT_LABEL, |
| help_text=_PROJECT_FORM_ABSTRACT_HELP_TEXT, |
| max_length=_PROJECT_FORM_ABSTRACT_MAX_LENGTH) |
| |
| full_content = django_forms.CharField( |
| required=False, |
| widget=django_forms.Textarea(), |
| label=_PROJECT_FORM_FULL_CONTENT_LABEL, |
| help_text=_PROJECT_FORM_FULL_CONTENT_HELP_TEXT) |
| |
| additional_info = django_forms.URLField( |
| required=False, |
| label=_PROJECT_FORM_ADDITIONAL_INFO_LABEL, |
| help_text=_PROJECT_FORM_ADDITIONAL_INFO_HELP_TEXT) |
| |
| clean_full_content = cleaning.clean_html_content('full_content') |
| |
| |
| def _adoptProjectPropertiesForForm(project_properties): |
| """Adopts properties of a project entity, which are persisted in datastore, |
| to representation which may be passed to populate ProjectForm. |
| |
| Args: |
| project_properties: A dict containing project properties as persisted |
| in datastore. |
| |
| Returns: |
| A dict mapping properties of project model to values which can be |
| populated to a project form. |
| """ |
| return { |
| 'title': project_properties['title'], |
| 'abstract': project_properties['abstract'], |
| 'full_content': project_properties['content'], |
| 'additional_info': project_properties.get('additional_info'), |
| } |
| |
| |
| def _adaptProjectPropertiesForDatastore(form_data): |
| """Adopts properties corresponding to project's properties, which |
| have been submitted in a form, to the format that is compliant with |
| project_model.Project model. |
| |
| Args: |
| form_data: A dict containing data submitted in a form. |
| |
| Returns: |
| A dict mapping properties of project model to values based on |
| data submitted in a form. |
| """ |
| return { |
| project_model.Project.title._name: form_data.get('title'), |
| project_model.Project.abstract._name: form_data.get('abstract'), |
| project_model.Project.content._name: form_data.get('full_content'), |
| project_model.Project.additional_info._name: |
| form_data.get('additional_info') or None, |
| } |
| |
| |
| class ProjectEditPage(base.RequestHandler): |
| """View to edit various project details by the student.""" |
| |
| access_checker = access.ConjuctionAccessChecker([ |
| access.IS_URL_USER_ACCESS_CHECKER, |
| soc_access.STUDENTS_ANNOUNCED_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(ProjectEditPage, 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'project/edit/%s$' % url_patterns.USER_ID, |
| self, name=self.url_names.PROJECT_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 = _adoptProjectPropertiesForForm(data.url_ndb_project.to_dict()) |
| form = ProjectForm(data=data.POST or form_data) |
| |
| return { |
| 'forms': [form], |
| 'page_name': _PROJECT_EDIT_PAGE_NAME, |
| } |
| |
| def post(self, data, check, mutator): |
| """See base.RequestHandler.post for specification.""" |
| form = ProjectForm(data=data.POST) |
| if not form.is_valid(): |
| # TODO(nathaniel): problematic self-use. |
| return self.get(data, check, mutator) |
| else: |
| project_properties = ( |
| _adaptProjectPropertiesForDatastore(form.cleaned_data)) |
| |
| result = project_logic.updateProject( |
| data.url_ndb_project.key, project_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.PROJECT_EDIT) + '?validated=True' |
| return http.HttpResponseRedirect(url) |
| |
| PROJECT_EDIT_PAGE = ProjectEditPage( |
| 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/form_base.html',) |
| |
| |
| |
| class ProjectDetailsPage(base.RequestHandler): |
| """View to display project details.""" |
| |
| access_checker = soc_access.STUDENTS_ANNOUNCED_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(ProjectDetailsPage, 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'project/details/%s$' % url_patterns.USER_ID, |
| self, name=self.url_names.PROJECT_DETAILS), |
| ] |
| |
| 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.""" |
| mentors = ', '.join( |
| mentor_key.get().public_name |
| for mentor_key in data.url_ndb_project.mentors) |
| |
| if ((data.ndb_profile |
| and data.url_ndb_project.organization in data.ndb_profile.admin_for) |
| or data.is_host): |
| all_mentors = profile_logic.queryAllMentorsForOrg( |
| data.url_ndb_project.organization).fetch(1000, keys_only=True) |
| |
| # TODO(daniel): replace it with a correct URL |
| assign_mentor_url = links.SOC_LINKER.userId( |
| data.url_ndb_profile.key, data.url_ndb_project.key.id(), |
| self.url_names.PROJECT_ASSIGN_MENTORS) |
| |
| assign_mentor_field = assign_mentor.AssignMentorFields( |
| data, data.url_ndb_project.mentors, assign_mentor_url, |
| all_mentors=all_mentors, allow_multiple=True) |
| |
| # TODO(daniel): add actual toggle buttons. |
| toggle_buttons = [] |
| |
| manage_actions = sidebar_actions.SidebarActions( |
| data, _MANAGE_ACTIONS_TITLE, toggle_buttons, |
| assign_mentor_field=assign_mentor_field) |
| else: |
| manage_actions = None |
| |
| edit_url = ( |
| links.SOC_LINKER.userId( |
| data.url_ndb_profile.key, data.url_ndb_project.key.id(), |
| self.url_names.PROJECT_EDIT) |
| if (data.ndb_profile |
| and data.ndb_profile.key == data.url_ndb_project.key.parent()) |
| else None) |
| |
| return { |
| 'abstract': data.url_ndb_project.abstract, |
| 'content': data.url_ndb_project.content, |
| 'edit_url': edit_url, |
| 'manage_actions': manage_actions, |
| 'mentors': mentors, |
| 'org_name': data.url_ndb_project.organization.get().name, |
| 'page_name': _PROJECT_DETAILS_PAGE_NAME, |
| 'student_name': data.url_ndb_project.key.parent().get().public_name, |
| 'title': data.url_ndb_project.title, |
| } |
| |
| PROJECT_DETAILS_PAGE = ProjectDetailsPage( |
| 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/project/project_details.html') |
| |
| |
| class ListProjectPublic(base.RequestHandler): |
| """View to list all projects for the specified program.""" |
| |
| access_checker = soc_access.STUDENTS_ANNOUNCED_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(ListProjectPublic, 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'projects/list/%s$' % url_patterns.PROGRAM, |
| self, name=self.url_names.PROJECT_LIST_PUBLIC) |
| ] |
| |
| def templatePath(self): |
| """See base.RequestHandler.templatePath for specification.""" |
| return self.template_path |
| |
| def jsonContext(self, data, check, mutator): |
| """Handler for JSON requests.""" |
| list_content = project_list.getPublicProjectList(data).getListData() |
| |
| if list_content: |
| return list_content.content() |
| else: |
| raise exception.BadRequest(message='This data cannot be accessed.') |
| |
| def context(self, data, check, mutator): |
| """Handler for GSoC Accepted Projects List page HTTP get request.""" |
| return { |
| 'page_name': _PROJECT_LIST_PUBLIC_PAGE_NAME % data.program.name, |
| 'list': project_list.getPublicProjectList(data), |
| 'program_select': base_templates.DefaultProgramSelect( |
| data, self.url_names.PROJECT_LIST_PUBLIC), |
| } |
| |
| LIST_PROJECT_PUBLIC = ListProjectPublic( |
| 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') |