Merge branch 'issue1658'.
diff --git a/app/app.yaml.template b/app/app.yaml.template
index a12b0fe..accfeaa 100644
--- a/app/app.yaml.template
+++ b/app/app.yaml.template
@@ -15,7 +15,7 @@
# TODO(proto): uncomment and supply a Google App Engine application instance
# application: FIXME
# TODO(release): see the instructions in README about the "version:" field
-version: 2-0-20121128-p1
+version: 2-0-20121217
runtime: python
api_version: 1
diff --git a/app/soc/content/css/v2/gci/account_deletion.css b/app/soc/content/css/v2/gci/account_deletion.css
new file mode 100644
index 0000000..96e2d99
--- /dev/null
+++ b/app/soc/content/css/v2/gci/account_deletion.css
@@ -0,0 +1,7 @@
+.delete-info {
+ color: #000;
+}
+
+.moderate-delete-message {
+ padding: 10px 0 10px 0;
+}
diff --git a/app/soc/content/css/v2/gci/style.css b/app/soc/content/css/v2/gci/style.css
index 21e9773..65f5834 100644
--- a/app/soc/content/css/v2/gci/style.css
+++ b/app/soc/content/css/v2/gci/style.css
@@ -322,8 +322,10 @@
.header {
height: 143px;
}
- .header .logo img {
+ .header .logo {
float: left;
+ }
+ .header .logo img {
height: 127px;
margin: 10px 0 0 58px;
width: 252px;
@@ -1414,7 +1416,7 @@
line-height: 18px;
padding: 4px 25px 5px 10px;
}
- .block-how-it-works .block-how-it-works-start a.example-tasks {
+ .block-how-it-works .block-how-it-works-start a.example-tasks, a.all-tasks-tasks {
color: #c53926;
display: block;
float: left;
@@ -1425,6 +1427,10 @@
margin-left: 100px;
padding: 4px 25px 5px 10px;
}
+ a.all-tasks-tasks {
+ font-size: 14px;
+ margin-left: 75px;
+ }
.block-featured-task {/*Featured Task*/
}
.block-featured-task .block-title {
diff --git a/app/soc/logic/delete_account.py b/app/soc/logic/delete_account.py
index 035a47e..789b015 100644
--- a/app/soc/logic/delete_account.py
+++ b/app/soc/logic/delete_account.py
@@ -19,9 +19,16 @@
from google.appengine.api import mail
+from google.appengine.ext import db
from soc.logic import accounts
from soc.logic import system
+from soc.logic import user as user_logic
+from soc.models import user
+
+from soc.modules.gci.logic import profile as profile_logic
+from soc.modules.gci.models import comment
+from soc.modules.gci.models import task
ADMIN_REQUEST_EMAIL_SUBJEST = """
@@ -39,7 +46,7 @@
def request_account_deletion(user):
"""Requests deletion of user's account from application administrators
by sending them an email.
-
+
This is a temporary method, until we have an automated solution.
"""
account = accounts.getCurrentAccount(normalize=False)
@@ -55,3 +62,85 @@
}
mail.send_mail_to_admins(sender, subject, body)
+
+
+def confirm_delete(profile):
+ """Deletes the given profile entity and also the user entity if possible.
+
+ 1. Deletes the profile.
+ 2. Deletes the user entity if no other profiles exist for the user.
+ 3. Removes the user from task notification subscription lists
+ 4. Replaces GCITask created_by, modified_by, student and GCIComment
+ created_by properties with dummy "melange_deleted_user" profile or user
+ entity.
+
+ This method implements a giant XG transaction, but should not take a long
+ time because experience has shown that there won't be too much data to
+ modify or delete.
+
+ Args:
+ profile: GCIProfile entity of the user.
+ """
+ profile_key = profile.key()
+
+ # Cannot delete the user entity if the user has other profiles, so set it
+ # to False in that case.
+ user_delete = not (profile_logic.hasOtherGCIProfiles(profile) or
+ profile_logic.hasOtherGCIProfiles(profile))
+
+ task_sub_q = task.GCITask.all().filter('subscribers', profile)
+
+ tasks_created_by_q = task.GCITask.all().filter('created_by', profile)
+
+ tasks_modified_by_q = task.GCITask.all().filter('modified_by', profile)
+
+ tasks_student_q = task.GCITask.all().filter('student', profile)
+
+ comments_created_by_q = comment.GCIComment.all().filter(
+ 'created_by', profile.user)
+
+ dummy_user = user_logic.getOrCreateDummyMelangeDeletedUser()
+ dummy_profile = profile_logic.getOrCreateDummyMelangeDeletedProfile(
+ profile.scope)
+
+ options = db.create_transaction_options(xg=True)
+
+ def delete_account_txn():
+ entities_to_save = set([])
+ entities_to_del = set([])
+
+ # The batch size for query.run() is 20, in most of the cases we have
+ # seen so far the user had a few tasks with subscriptions, created_by,
+ # modified_by etc., so this should still be single datastore hits per
+ # loop.
+ for task in task_sub_q.run():
+ task.subscribers.remove(profile_key)
+ entities_to_save.add(task)
+
+ for task in tasks_created_by_q.run():
+ task.created_by = dummy_profile
+ entities_to_save.add(task)
+
+ for task in tasks_modified_by_q.run():
+ task.modified_by = dummy_profile
+ entities_to_save.add(task)
+
+ for task in tasks_student_q.run():
+ task.student = dummy_profile
+ entities_to_save.add(task)
+
+ for comment in comments_created_by_q.run():
+ comment.created_by = dummy_user
+ entities_to_save.add(comment)
+
+ if profile.student_info:
+ entities_to_del.add(profile.student_info)
+ entities_to_del.add(profile)
+
+ if user_delete:
+ entities_to_del.add(profile.parent())
+
+ db.put(entities_to_save)
+ db.delete(entities_to_del)
+
+ db.run_in_transaction_options(options, delete_account_txn)
diff --git a/app/soc/logic/user.py b/app/soc/logic/user.py
index 0d411f7..9a4bcbb 100644
--- a/app/soc/logic/user.py
+++ b/app/soc/logic/user.py
@@ -19,17 +19,20 @@
from google.appengine.api import users
-from google.appengine.ext import db
+from google.appengine.runtime import apiproxy_errors
from soc.logic import accounts
+from soc.logic import exceptions
+from soc.models import user
-from soc.models.user import User
+
+MELANGE_DELETED_USER = 'melange_deleted_user'
def isFormerAccount(account):
"""Returns true if account is a former account of some User.
"""
- return User.all().filter('former_accounts', account).count() > 0
+ return user.User.all().filter('former_accounts', account).count() > 0
def forCurrentAccount():
@@ -41,25 +44,24 @@
If there is no user logged in, or they have no valid associated User
entity, None is returned.
"""
-
account = accounts.getCurrentAccount()
if not account:
return None
- user = forAccount(account)
+ user_ent = forAccount(account)
- if user and not user.user_id and account.user_id():
- from google.appengine.runtime.apiproxy_errors import CapabilityDisabledError
+ if user_ent and not user_ent.user_id and account.user_id():
# update the user id that was added to GAE after Melange was launched
try:
- user.user_id = account.user_id()
- user.put()
- except CapabilityDisabledError, e:
+ user_ent.user_id = account.user_id()
+ user_ent.put()
+ except apiproxy_errors.CapabilityDisabledError, _:
# readonly mode, that's fine
pass
- return user
+ return user_ent
+
def forCurrentUserId():
"""Retrieves the user entity for the currently logged in user id.
@@ -67,26 +69,26 @@
If there is no user logged in, or they have no valid associated User
entity, None is returned.
"""
-
user_id = accounts.getCurrentUserId()
if not user_id:
return None
- user = forUserId(user_id)
+ user_ent = forUserId(user_id)
current_account = accounts.getCurrentAccount()
- if user and (str(user.account) != str(current_account)):
+ if user_ent and (str(user_ent.account) != str(current_account)):
# The account of the user has changed, we use this account to send system
# emails to.
try:
- user.account = current_account
- user.put()
- except CapabilityDisabledError, e:
+ user_ent.account = current_account
+ user_ent.put()
+ except apiproxy_errors.CapabilityDisabledError, _:
# readonly mode, that's fine
pass
- return user
+ return user_ent
+
def current():
"""Retrieves the user entity for the currently logged in user.
@@ -95,10 +97,10 @@
The User entity of the logged in user or None if not available.
"""
# look up with the unique id first
- user = forCurrentUserId()
+ user_ent = forCurrentUserId()
- if user:
- return user
+ if user_ent:
+ return user_ent
# look up using the account address thereby setting the unique id
return forCurrentAccount()
@@ -110,13 +112,12 @@
If there is no user logged in, or they have no valid associated User
entity, None is returned.
"""
-
if not account:
- raise base.InvalidArgumentError("Missing argument 'account'")
+ raise exceptions.BadRequest("Missing argument 'account'")
account = accounts.normalizeAccount(account)
- q = User.all()
+ q = user.User.all()
q.filter('account', account)
q.filter('status', 'valid')
return q.get()
@@ -128,38 +129,52 @@
If there is no user logged in, or they have no valid associated User
entity, None is returned.
"""
-
if not user_id:
- raise base.InvalidArgumentError("Missing argument 'user_id'")
+ raise exceptions.BadRequest("Missing argument 'user_id'")
- q = User.all()
+ q = user.User.all()
q.filter('user_id', user_id)
q.filter('status', 'valid')
return q.get()
-def isDeveloper(account=None, user=None):
+def isDeveloper(account=None, user_ent=None):
"""Returns true iff the specified user is a Developer.
Args:
account: if not supplied, defaults to the current account
- user: if not specified, defaults to the current user
+ user_ent: if not specified, defaults to the current user
"""
-
current = accounts.getCurrentAccount()
if not account:
# default account to the current logged in account
account = current
- if account and (not user):
+ if account and (not user_ent):
# default user to the current logged in user
- user = forAccount(account)
+ user_ent = forAccount(account)
# pylint: disable=E1103
- if user and user.is_developer:
+ if user_ent and user_ent.is_developer:
return True
if account and (account == current):
return users.is_current_user_admin()
+
+def getOrCreateDummyMelangeDeletedUser():
+ """Fetches or creates the dummy melange deleted user entity.
+ """
+ q = user.User.all().filter('link_id', MELANGE_DELETED_USER)
+ user_ent = q.get()
+
+ # If the requested user does not exist, create one.
+ if not user_ent:
+ account = users.User(email=MELANGE_DELETED_USER)
+ user_ent = user.User(
+ key_name=MELANGE_DELETED_USER, account=account,
+ link_id=MELANGE_DELETED_USER)
+ user_ent.put()
+
+ return user_ent
diff --git a/app/soc/modules/gci/callback.py b/app/soc/modules/gci/callback.py
index 9962de4..627dc29 100644
--- a/app/soc/modules/gci/callback.py
+++ b/app/soc/modules/gci/callback.py
@@ -43,6 +43,7 @@
from soc.modules.gci.views import homepage
from soc.modules.gci.views import invite
from soc.modules.gci.views import leaderboard
+ from soc.modules.gci.views import moderate_delete_account
from soc.modules.gci.views import org_app
from soc.modules.gci.views import org_home
from soc.modules.gci.views import org_profile
@@ -78,6 +79,7 @@
self.views.append(invite.ListUserInvitesPage())
self.views.append(leaderboard.LeaderboardPage())
self.views.append(leaderboard.StudentTasksPage())
+ self.views.append(moderate_delete_account.ModerateDeleteAccountPage())
self.views.append(org_app.GCIOrgAppEditPage())
self.views.append(org_profile.OrgProfilePage())
self.views.append(org_app.GCIOrgAppPreviewPage())
diff --git a/app/soc/modules/gci/logic/comment.py b/app/soc/modules/gci/logic/comment.py
index 63a53f9..0912bb7 100644
--- a/app/soc/modules/gci/logic/comment.py
+++ b/app/soc/modules/gci/logic/comment.py
@@ -50,7 +50,7 @@
to_emails = []
profiles = GCIProfile.get(task.subscribers)
for profile in profiles:
- if ((not comment.created_by) or
+ if profile and ((not comment.created_by) or
profile.user.key() != comment.created_by.key()):
to_emails.append(profile.email)
diff --git a/app/soc/modules/gci/logic/profile.py b/app/soc/modules/gci/logic/profile.py
index 1c85d07..bd779bc 100644
--- a/app/soc/modules/gci/logic/profile.py
+++ b/app/soc/modules/gci/logic/profile.py
@@ -18,12 +18,38 @@
"""
+import datetime
+
+from soc.logic import user as user_logic
from soc.tasks import mailer
+from soc.modules.gsoc.models import profile as gsoc_profile_model
+
from soc.modules.gci.logic.helper import notifications
-from soc.modules.gci.models.profile import GCIProfile
-from soc.modules.gci.models.profile import GCIStudentInfo
-from soc.modules.gci.models.task import GCITask
+from soc.modules.gci.models import comment as comment_model
+from soc.modules.gci.models import profile as profile_model
+from soc.modules.gci.models import task as task_model
+
+
+MELANGE_DELETED_USER_PNAME = 'Melange Deleted User'
+
+MELANGE_DELETED_USER_GNAME = 'Melange Deleted User GName'
+
+MELANGE_DELETED_USER_SNAME = 'Melange Deleted User Surname'
+
+MELANGE_DELETED_USER_EMAIL = 'melange_deleted_user@gmail.com'
+
+MELANGE_DELETED_USER_RES_STREET = 'No address'
+
+MELANGE_DELETED_USER_RES_CITY = 'No city'
+
+MELANGE_DELETED_USER_RES_COUNTY = 'United States'
+
+MELANGE_DELETED_USER_RES_POSTAL_CODE = '00000'
+
+MELANGE_DELETED_USER_PHONE = '0000000000'
+
+MELANGE_DELETED_USER_BIRTH_DATE = datetime.datetime(1, 1, 1)
def hasStudentFormsUploaded(student):
@@ -46,7 +72,7 @@
"""
# get all mentors keys first
- query = GCIProfile.all(keys_only=keys_only)
+ query = profile_model.GCIProfile.all(keys_only=keys_only)
query.filter('mentor_for', org)
mentors = query.fetch(limit=limit)
@@ -84,7 +110,7 @@
Args:
org: The GCIOrganization entity for which the admins should be found.
"""
- query = GCIProfile.all()
+ query = profile_model.GCIProfile.all()
query.filter('org_admin_for', org)
return query.fetch(limit)
@@ -98,7 +124,7 @@
user: User entity for which the profile should be found
program: GCIProgram entity for which the profile should be found
"""
- return GCIProfile.all().ancestor(user).filter('scope = ', program)
+ return profile_model.GCIProfile.all().ancestor(user).filter('scope = ', program)
def queryStudentInfoForParent(parent):
@@ -108,4 +134,114 @@
Args:
parent: GCIProfile entity which is the parent of the entity to retrieve
"""
- return GCIStudentInfo.all().ancestor(parent)
+ return profile_model.GCIStudentInfo.all().ancestor(parent)
+
+
+def hasTasks(profile):
+ """Returns True if the given student profile has been assigned to a task.
+
+ Assign also means the tasks completed by the student.
+
+ Args:
+ profile: GCIProfile entity of the student.
+ """
+ q = task_model.GCITask.all()
+ q.filter('student', profile)
+ return q.count() > 0
+
+
+def hasCreatedOrModifiedTask(profile):
+ """Returns True if the given user has created or modified a task.
+
+ Args:
+ profile: GCIProfile entity of the user.
+ """
+ q = task_model.GCITask.all()
+ q.filter('created_by', profile)
+ if q.count() > 0:
+ return True
+
+ q = task_model.GCITask.all()
+ q.filter('modified_by', profile)
+ return q.count() > 0
+
+
+def hasTaskComments(profile):
+ """Returns True if the given profile has task comments associated with it.
+
+ Args:
+ profile: GCIProfile entity of the user.
+ """
+ user = profile.parent()
+
+ q = comment_model.GCIComment.all()
+ q.filter('created_by', user)
+
+ return q.count() > 0
+
+
+def hasOtherGCIProfiles(profile):
+ """Returns True if the given user had profiles in previous GCIs.
+
+ Args:
+ profile: GCIProfile entity of the user.
+ """
+ user = profile.parent()
+
+ q = profile_model.GCIProfile.all()
+ q.ancestor(user)
+
+ # We check for > 1 not > 0 because we already know that there is one profile
+ # for this program. So we need to check if there are others.
+ return q.count() > 1
+
+
+def hasOtherGSoCProfiles(profile):
+ """Returns True if the given user has profiles in previous GSoCs.
+
+ Args:
+ profile: GCIProfile entity of the user.
+ """
+ user = profile.parent()
+
+ q = gsoc_profile_model.GSoCProfile.all()
+ q.ancestor(user)
+
+ return q.count() > 0
+
+
+def getOrCreateDummyMelangeDeletedProfile(program):
+ """Fetches or creates the dummy melange deleted profile for the given program.
+
+ Args:
+ program: The program entity for which the dummy profile should be fetched
+ or created.
+ """
+ q = profile_model.GCIProfile.all()
+ q.filter('link_id', user_logic.MELANGE_DELETED_USER)
+ q.filter('scope', program)
+ profile_ent = q.get()
+
+ # If the requested user does not exist, create one.
+ if not profile_ent:
+ user_ent = user_logic.getOrCreateDummyMelangeDeletedUser()
+ key_name = '%s/%s' % (program.key(), user_logic.MELANGE_DELETED_USER)
+
+ profile_ent = profile_model.GCIProfile(
+ parent=user_ent, key_name=key_name,
+ link_id=user_logic.MELANGE_DELETED_USER, scope=program,
+ scope_path=program.key().id_or_name(), user=user_ent,
+ public_name=MELANGE_DELETED_USER_PNAME,
+ given_name=MELANGE_DELETED_USER_GNAME,
+ surname=MELANGE_DELETED_USER_SNAME,
+ email=MELANGE_DELETED_USER_EMAIL,
+ res_street=MELANGE_DELETED_USER_RES_STREET,
+ res_city=MELANGE_DELETED_USER_RES_CITY,
+ res_country=MELANGE_DELETED_USER_RES_COUNTY,
+ res_postalcode=MELANGE_DELETED_USER_RES_POSTAL_CODE,
+ phone=MELANGE_DELETED_USER_PHONE,
+ birth_date=MELANGE_DELETED_USER_BIRTH_DATE)
+
+ profile_ent.put()
+
+ return profile_ent
diff --git a/app/soc/modules/gci/templates/task_list.py b/app/soc/modules/gci/templates/task_list.py
index e61e6c9..46f2ff4 100644
--- a/app/soc/modules/gci/templates/task_list.py
+++ b/app/soc/modules/gci/templates/task_list.py
@@ -97,7 +97,15 @@
if 'mentors' in self._columns:
list_config.addColumn('mentors', 'Mentors',
lambda entity, mentors, *args: ', '.join(
- mentors[i].name() for i in entity.mentors))
+ mentors[i].name() for i in entity.mentors), hidden=True)
+
+ if 'types' in self._columns:
+ list_config.addColumn('types', 'Category',
+ lambda entity, *args: ', '.join(entity.types))
+
+ if 'tags' in self._columns:
+ list_config.addColumn('tags', 'Tags',
+ lambda entity, *args: ', '.join(entity.tags))
if 'status' in self._columns:
list_config.addSimpleColumn('status', 'Status')
diff --git a/app/soc/modules/gci/views/accepted_orgs.py b/app/soc/modules/gci/views/accepted_orgs.py
index a1365c7..2b1afce 100644
--- a/app/soc/modules/gci/views/accepted_orgs.py
+++ b/app/soc/modules/gci/views/accepted_orgs.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for GCI accepted orgs.
-"""
-
+"""Module containing the views for GCI accepted orgs."""
from django.conf.urls.defaults import url as django_url
@@ -28,8 +24,7 @@
from soc.modules.gci.logic import profile as profile_logic
from soc.modules.gci.models.organization import GCIOrganization
from soc.modules.gci.templates.org_list import OrgList
-from soc.modules.gci.views.base import RequestHandler
-#from soc.modules.gci.views.base_templates import ProgramSelect
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper.url_patterns import url
from soc.modules.gci.views.helper import url_names
@@ -43,14 +38,19 @@
self.data.program.name)
def _getListConfig(self):
- r = self.data.redirect
+ # TODO(nathaniel): squeeze this back into a lambda expression in
+ # the call to setRowAction below.
+ def RowAction(e, *args):
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.data.redirect.organization(organization=e)
+
+ return self.data.redirect.urlOf(url_names.GCI_ORG_HOME)
list_config = lists.ListConfiguration()
list_config.addColumn('name', 'Name',
lambda e, *args: e.name.strip())
list_config.addSimpleColumn('link_id', 'Link ID', hidden=True)
- list_config.setRowAction(
- lambda e, *args: r.organization(e).urlOf(url_names.GCI_ORG_HOME))
+ list_config.setRowAction(RowAction)
list_config.addColumn(
'ideas', 'Ideas',
(lambda e, *args: url_helper.urlize(e.ideas, name="[ideas page]")),
@@ -66,7 +66,7 @@
return query
-class AcceptedOrgsPage(RequestHandler):
+class AcceptedOrgsPage(GCIRequestHandler):
"""View for the accepted organizations page.
"""
@@ -107,14 +107,19 @@
self.data.program.name)
def _getListConfig(self):
- r = self.data.redirect
+ # TODO(nathaniel): squeeze this back into a lambda expression in the
+ # call to setRowAction below.
+ def RowAction(e, *args):
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.data.redirect.organization(organization=e)
+
+ return self.data.redirect.urlOf(url_names.GCI_ORG_HOME)
list_config = lists.ListConfiguration()
list_config.addColumn('name', 'Name',
lambda e, *args: e.name.strip())
list_config.addSimpleColumn('link_id', 'Link ID', hidden=True)
- list_config.setRowAction(
- lambda e, *args: r.organization(e).urlOf(url_names.GCI_ORG_HOME))
+ list_config.setRowAction(RowAction)
list_config.setDefaultPagination(False)
list_config.setDefaultSort('name')
list_config.addColumn(
@@ -140,7 +145,7 @@
return query
-class AcceptedOrgsAdminPage(RequestHandler):
+class AcceptedOrgsAdminPage(GCIRequestHandler):
"""View for the accepted organizations page for admin with additional info.
"""
diff --git a/app/soc/modules/gci/views/admin.py b/app/soc/modules/gci/views/admin.py
index 0334ded..3b2281a 100644
--- a/app/soc/modules/gci/views/admin.py
+++ b/app/soc/modules/gci/views/admin.py
@@ -32,7 +32,7 @@
from soc.modules.gci.models.profile import GCIProfile
from soc.modules.gci.views import forms as gci_forms
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -77,7 +77,7 @@
self.cleaned_data['profile'] = q.get()
-class DashboardPage(RequestHandler):
+class DashboardPage(GCIRequestHandler):
"""Dashboard for admins.
"""
@@ -229,13 +229,6 @@
'title': 'List of documents',
'link': r.urlOf('list_gci_documents')
},
- {
- 'name': 'students_info',
- 'description': ugettext(
- 'Details of students participating in the current program.'),
- 'title': 'Details of students',
- 'link': r.urlOf(url_names.GCI_STUDENTS_INFO)
- },
]
super(ProgramSettingsDashboard, self).__init__(request, data, subpages)
@@ -362,6 +355,13 @@
'title': 'List students',
'link': r.urlOf(url_names.GCI_STUDENTS_INFO)
},
+ {
+ 'name': 'leaderboard',
+ 'description': ugettext(
+ 'Leaderboard for the program'),
+ 'title': 'Leaderboard',
+ 'link': r.urlOf(url_names.GCI_LEADERBOARD)
+ },
]
super(ParticipantsDashboard, self).__init__(request, data, subpages)
@@ -384,7 +384,7 @@
}
-class LookupLinkIdPage(RequestHandler):
+class LookupLinkIdPage(GCIRequestHandler):
"""View for the participant profile.
"""
@@ -428,4 +428,3 @@
'posted': error,
'page_name': 'Lookup profile',
}
-
diff --git a/app/soc/modules/gci/views/age_check.py b/app/soc/modules/gci/views/age_check.py
index 080296c..6b45518 100644
--- a/app/soc/modules/gci/views/age_check.py
+++ b/app/soc/modules/gci/views/age_check.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GCI age check.
-"""
-
+"""Module for the GCI age check."""
from django import forms
@@ -25,7 +21,7 @@
from soc.views.helper import url_patterns
from soc.modules.gci.views import forms as gci_forms
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper.url_patterns import url
@@ -42,7 +38,7 @@
required=True)
-class AgeCheck(RequestHandler):
+class AgeCheck(GCIRequestHandler):
"""View for the GCI age check.
"""
@@ -109,4 +105,7 @@
self.response.set_cookie('age_check', '0')
# redirect to the same page and have the cookies sent across
- self.redirect.program().to('gci_age_check')
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.redirect.program()
+
+ self.redirect.to('gci_age_check')
diff --git a/app/soc/modules/gci/views/all_tasks.py b/app/soc/modules/gci/views/all_tasks.py
index 78750f8..adf65b8 100644
--- a/app/soc/modules/gci/views/all_tasks.py
+++ b/app/soc/modules/gci/views/all_tasks.py
@@ -22,9 +22,9 @@
from soc.views.helper import url_patterns
from soc.modules.gci.logic import task as task_logic
-from soc.modules.gci.models.task import CLAIMABLE
from soc.modules.gci.templates.task_list import TaskList
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
+from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -32,7 +32,8 @@
"""Template for list of all tasks which are claimable for the program.
"""
- _LIST_COLUMNS = ['title', 'organization', 'mentors', 'status']
+ _LIST_COLUMNS = ['title', 'organization', 'tags', 'types',
+ 'mentors', 'status']
def __init__(self, request, data):
super(AllTasksList, self).__init__(request, data)
@@ -47,7 +48,7 @@
return task_logic.queryClaimableTasksForProgram(self.data.program)
-class TaskListPage(RequestHandler):
+class TaskListPage(GCIRequestHandler):
"""View for the list task page.
"""
@@ -59,7 +60,7 @@
def djangoURLPatterns(self):
return [
url(r'tasks/%s$' % url_patterns.PROGRAM, self,
- name='gci_list_tasks'),
+ name=url_names.GCI_ALL_TASKS_LIST),
]
def checkAccess(self):
diff --git a/app/soc/modules/gci/views/base.py b/app/soc/modules/gci/views/base.py
index 45904b5..943a5fb 100644
--- a/app/soc/modules/gci/views/base.py
+++ b/app/soc/modules/gci/views/base.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,26 +12,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the boiler plate required to construct GCI views.
-"""
+"""Module containing the boiler plate required to construct GCI views."""
+import httplib
-from soc.views.base import RequestHandler
+from django import http
+
+from soc.views import base
from soc.modules.gci.views import base_templates
from soc.modules.gci.views.helper import access_checker
-from soc.modules.gci.views.helper.request_data import RequestData
-from soc.modules.gci.views.helper.request_data import RedirectHelper
+from soc.modules.gci.views.helper import request_data
-class RequestHandler(RequestHandler):
- """Customization required by GCI to handle HTTP requests.
- """
+class GCIRequestHandler(base.RequestHandler):
+ """Customization required by GCI to handle HTTP requests."""
def render(self, template_path, context):
"""Renders the page using the specified context.
- See soc.views.base.RequestHandler.
+ See soc.views.base.RequestHandler for specification.
The context object is extended with the following values:
base_layout: path to the base template.
@@ -47,11 +45,11 @@
context['header'] = base_templates.Header(self.data)
context['mainmenu'] = base_templates.MainMenu(self.data)
context['footer'] = base_templates.Footer(self.data)
- super(RequestHandler, self).render(template_path, context)
+ return super(GCIRequestHandler, self).render(template_path, context)
def init(self, request, args, kwargs):
- self.data = RequestData()
- self.redirect = RedirectHelper(self.data, self.response)
+ self.data = request_data.RequestData()
+ self.redirect = request_data.RedirectHelper(self.data, self.response)
self.data.populate(self.redirect, request, args, kwargs)
if self.data.is_developer:
self.mutator = access_checker.DeveloperMutator(self.data)
@@ -59,19 +57,22 @@
else:
self.mutator = access_checker.Mutator(self.data)
self.check = access_checker.AccessChecker(self.data)
- super(RequestHandler, self).init(request, args, kwargs)
+ super(GCIRequestHandler, self).init(request, args, kwargs)
def error(self, status, message=None):
+ """See base.RequestHandler.error for specification."""
if not self.data.program:
- return super(RequestHandler, self).error(status, message)
+ return super(GCIRequestHandler, self).error(status, message)
- self.response.set_status(status, message=message)
+ # If message is not set, set it to the default associated with the
+ # given status (such as "Method Not Allowed" or "Service Unavailable").
+ message = message or httplib.responses.get(status, '')
- template_path = "v2/modules/gci/error.html"
+ template_path = 'v2/modules/gci/error.html'
context = {
- 'page_name': self.response.content,
- 'message': self.response.content,
+ 'page_name': message,
+ 'message': message,
}
- self.response.content = ''
- self.render(template_path, context)
+ return http.HttpResponse(
+ content=self.render(template_path, context), status=status)
diff --git a/app/soc/modules/gci/views/base_templates.py b/app/soc/modules/gci/views/base_templates.py
index fb9ae20..492d5a2 100644
--- a/app/soc/modules/gci/views/base_templates.py
+++ b/app/soc/modules/gci/views/base_templates.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +14,6 @@
"""This module contains the view for the site menus."""
-
import datetime
from google.appengine.api import users
@@ -62,7 +59,9 @@
context['dashboard_link'] = redirect.dashboard().url()
if data.timeline.tasksPubliclyVisible():
- context['tasks_link'] = redirect.program().urlOf('gci_list_tasks')
+ # TODO(nathaniel): make this .program() call unnecessary.
+ redirect.program()
+ context['tasks_link'] = redirect.urlOf('gci_list_tasks')
if not data.user:
context['register_as_student_link'] = redirect.createProfile(
'student').urlOf('create_gci_profile', secure=True)
diff --git a/app/soc/modules/gci/views/bulk_create.py b/app/soc/modules/gci/views/bulk_create.py
index d1c5478..2fdf0fd 100644
--- a/app/soc/modules/gci/views/bulk_create.py
+++ b/app/soc/modules/gci/views/bulk_create.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GCI view to bulk create GCITasks.
-"""
-
+"""Module for the GCI view to bulk create GCITasks."""
from django import forms
@@ -25,7 +21,7 @@
from soc.modules.gci.models.bulk_create_data import GCIBulkCreateData
from soc.modules.gci.tasks import bulk_create
from soc.modules.gci.views import forms as gci_forms
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -43,7 +39,7 @@
widget=forms.Textarea)
-class BulkCreate(RequestHandler):
+class BulkCreate(GCIRequestHandler):
"""View for bulk creation of GCITasks.
"""
@@ -97,5 +93,7 @@
form.cleaned_data['task_data'], self.data.organization,
self.data.profile)
- self.redirect.organization(self.data.organization)
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.redirect.organization(organization=self.data.organization)
+
self.redirect.to(url_names.GCI_TASK_BULK_CREATE, validated=True)
diff --git a/app/soc/modules/gci/views/dashboard.py b/app/soc/modules/gci/views/dashboard.py
index dd39a31..f2ff3bc 100644
--- a/app/soc/modules/gci/views/dashboard.py
+++ b/app/soc/modules/gci/views/dashboard.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GCI participant dashboard.
-"""
+"""Module for the GCI participant dashboard."""
import logging
@@ -40,7 +37,7 @@
from soc.modules.gci.models.task import CLAIMABLE
from soc.modules.gci.models.task import GCITask
from soc.modules.gci.models.task import UNPUBLISHED
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -107,7 +104,7 @@
}
-class DashboardPage(RequestHandler):
+class DashboardPage(GCIRequestHandler):
"""View for the participant dashboard.
"""
@@ -892,9 +889,15 @@
'organization you will create the task for.')}
def _setRowAction(self, request, data):
- self._list_config.setRowAction(
- lambda e, *args: data.redirect.organization(e).
- urlOf('gci_create_task'))
+ # TODO(nathaniel): squeeze this back into a lambda expression in the
+ # setRowAction call below.
+ def RowAction(e, *args):
+ # TODO(nathaniel): make this .organization call unnecessary.
+ data.redirect.organization(organization=e)
+
+ return data.redirect.urlOf('gci_create_task')
+
+ self._list_config.setRowAction(RowAction)
class MyOrgsListBeforeBulkCreateTask(MyOrgsList):
@@ -918,9 +921,15 @@
'organization you will upload the tasks for.')}
def _setRowAction(self, request, data):
- self._list_config.setRowAction(
- lambda e, *args: data.redirect.organization(e).
- urlOf(url_names.GCI_TASK_BULK_CREATE))
+ # TODO(nathaniel): squeeze this back into a lambda expression in the
+ # setRowAction call below.
+ def RowAction(e, *args):
+ # TODO(nathaniel): make this .organization call unnecessary.
+ data.redirect.organization(organization=e)
+
+ return data.redirect.urlOf(url_names.GCI_TASK_BULK_CREATE)
+
+ self._list_config.setRowAction(RowAction)
class MyOrgsListBeforeInviteMentor(MyOrgsList):
@@ -987,9 +996,15 @@
self.idx = 12
def _setRowAction(self, request, data):
- self._list_config.setRowAction(
- lambda e, *args: data.redirect.organization(e)
- .urlOf(url_names.GCI_ORG_SCORES))
+ # TODO(nathaniel): squeeze this back into a lambda expression in the
+ # call to setRowAction below.
+ def RowAction(e, *args):
+ # TODO(nathaniel): make this .organization call unnecessary.
+ data.redirect.organization(organization=e)
+
+ return data.redirect.urlOf(url_names.GCI_ORG_SCORES)
+
+ self._list_config.setRowAction(RowAction)
def getListData(self):
if lists.getListIndex(self.request) != self.idx:
@@ -1096,9 +1111,15 @@
'organization you will edit the profile for.')}
def _setRowAction(self, request, data):
- self._list_config.setRowAction(
- lambda e, *args: data.redirect.organization(e).
- urlOf(url_names.EDIT_GCI_ORG_PROFILE, secure=True))
+ # TODO(nathaniel): squeeze this back into a lambda expression in the
+ # call to setRowAction below.
+ def RowAction(e, *args):
+ # TODO(nathaniel): make this .organization call unnecessary.
+ data.redirect.organization(organization=e)
+
+ return data.redirect.urlOf(url_names.EDIT_GCI_ORG_PROFILE, secure=True)
+
+ self._list_config.setRowAction(RowAction)
class AllOrgsListBeforeRequestRole(MyOrgsList):
diff --git a/app/soc/modules/gci/views/delete_account.py b/app/soc/modules/gci/views/delete_account.py
index 019d25c..b71b4fc 100644
--- a/app/soc/modules/gci/views/delete_account.py
+++ b/app/soc/modules/gci/views/delete_account.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,19 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GCI delete account page.
-"""
-
+"""Module for the GCI delete account page."""
from soc.logic import delete_account
from soc.views.helper import url_patterns
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views import base
from soc.modules.gci.views.helper.url_patterns import url
-class DeleteAccountPage(RequestHandler):
+class DeleteAccountPage(base.GCIRequestHandler):
"""View for the GCI delete account page.
"""
@@ -49,4 +45,6 @@
def post(self):
delete_account.request_account_deletion(self.data.user)
- self.redirect.program().to('gci_delete_account', validated=True)
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.redirect.program()
+ self.redirect.to('gci_delete_account', validated=True)
diff --git a/app/soc/modules/gci/views/document.py b/app/soc/modules/gci/views/document.py
index 7eb857f..7d61488 100644
--- a/app/soc/modules/gci/views/document.py
+++ b/app/soc/modules/gci/views/document.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for GCI documents page.
-"""
-
+"""Module containing the views for GCI documents page."""
from soc.logic.exceptions import AccessViolation
from soc.models.document import Document
@@ -25,7 +21,7 @@
from soc.views.helper.access_checker import isSet
from soc.views.template import Template
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.forms import GCIModelForm
#from soc.modules.gci.views.base_templates import ProgramSelect
from soc.modules.gci.views.helper.url_patterns import url
@@ -44,7 +40,7 @@
]
-class EditDocumentPage(RequestHandler):
+class EditDocumentPage(GCIRequestHandler):
"""Encapsulate all the methods required to edit documents.
"""
@@ -91,7 +87,7 @@
self.get()
-class DocumentPage(RequestHandler):
+class DocumentPage(GCIRequestHandler):
"""Encapsulate all the methods required to show documents.
"""
@@ -117,7 +113,7 @@
}
-class EventsPage(RequestHandler):
+class EventsPage(GCIRequestHandler):
"""Encapsulates all the methods required to show the events page.
"""
@@ -153,7 +149,7 @@
return 'v2/modules/gci/document/_document_list.html'
-class DocumentListPage(RequestHandler):
+class DocumentListPage(GCIRequestHandler):
"""View for the list documents page.
"""
diff --git a/app/soc/modules/gci/views/helper/url_names.py b/app/soc/modules/gci/views/helper/url_names.py
index 89776b2..a14d023 100644
--- a/app/soc/modules/gci/views/helper/url_names.py
+++ b/app/soc/modules/gci/views/helper/url_names.py
@@ -35,6 +35,8 @@
GCI_ORG_CHOOSE_FOR_SCORE = 'gci_org_choose_for_score'
GCI_ORG_TASKS_ALL = 'gci_org_tasks_all'
+GCI_ALL_TASKS_LIST = 'gci_list_tasks'
+
GCI_LEADERBOARD = 'gci_leaderboard'
GCI_STUDENT_TASKS = 'gci_student_tasks'
GCI_STUDENT_TASKS_FOR_ORG = 'gci_student_tasks_for_org'
diff --git a/app/soc/modules/gci/views/homepage.py b/app/soc/modules/gci/views/homepage.py
index 286548c..0aaf40d 100644
--- a/app/soc/modules/gci/views/homepage.py
+++ b/app/soc/modules/gci/views/homepage.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for GCI home page.
-"""
-
+"""Module containing the views for GCI home page."""
from soc.views.helper import url_patterns
from soc.views.template import Template
@@ -24,14 +20,13 @@
from soc.modules.gci.logic import organization as org_logic
from soc.modules.gci.logic import task as task_logic
from soc.modules.gci.views import common_templates
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper.url_patterns import url
from soc.modules.gci.views.helper import url_names
class HowItWorks(Template):
- """How it works template.
- """
+ """How it works template."""
CONTEST_BEGINS_ON_MSG = "Contest begins on %s %s"
@@ -41,30 +36,40 @@
self.data = data
def context(self):
- r = self.data.redirect
program = self.data.program
from soc.modules.gci.models.program import GCIProgram
about_page = GCIProgram.about_page.get_value_for_datastore(program)
example_tasks_link = ''
+ all_tasks_link = ''
main_text = self._getMainText()
if self.data.timeline.orgSignup():
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.data.redirect.program()
+
start_text = 'Sign up as organization'
- start_link = r.program().urlOf('gci_take_org_app')
+ start_link = self.data.redirect.urlOf('gci_take_org_app')
if self.data.program.example_tasks:
example_tasks_link = self.data.program.example_tasks
elif self.data.timeline.studentSignup() and not self.data.profile:
start_text = 'Register As Student'
- start_link = r.createProfile('student').urlOf('create_gci_profile',
- secure=True)
- if self.data.program.example_tasks:
- example_tasks_link = self.data.program.example_tasks
+
+ start_link = self.data.redirect.createProfile('student').urlOf(
+ 'create_gci_profile', secure=True)
+
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.data.redirect.program()
+
+ all_tasks_link = self.data.redirect.urlOf(url_names.GCI_ALL_TASKS_LIST)
elif self.data.timeline.tasksPubliclyVisible():
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.data.redirect.program()
+
start_text = 'Search for tasks'
- start_link = self.data.redirect.program().urlOf('gci_list_tasks')
+ start_link = self.data.redirect.urlOf('gci_list_tasks')
elif self.data.program.example_tasks:
start_text = 'See example tasks'
start_link = self.data.program.example_tasks
@@ -72,10 +77,11 @@
start_text = start_link = ''
return {
- 'about_link': r.document(about_page).url(),
+ 'about_link': self.data.redirect.document(about_page).url(),
'start_text': start_text,
'start_link': start_link,
'example_tasks_link': example_tasks_link,
+ 'all_tasks_link': all_tasks_link,
'main_text': main_text,
}
@@ -123,14 +129,12 @@
self.data = data
def context(self):
- r = self.data.redirect
-
participating_orgs = []
current_orgs = org_logic.participating(
self.data.program, org_count=self._ORG_COUNT)
for org in current_orgs:
participating_orgs.append({
- 'link': r.orgHomepage(org.link_id).url(),
+ 'link': self.data.redirect.orgHomepage(org.link_id).url(),
'logo': org.logo_url,
'name': org.short_name,
})
@@ -147,12 +151,17 @@
row, orgs = orgs[:self._TABLE_WIDTH], orgs[self._TABLE_WIDTH:]
participating_orgs_table_rows.append(row)
- accepted_orgs_url = r.program().urlOf('gci_accepted_orgs')
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.data.redirect.program()
+
+ accepted_orgs_url = self.data.redirect.urlOf('gci_accepted_orgs')
return {
'participating_orgs': participating_orgs,
'participating_orgs_table_rows': participating_orgs_table_rows,
'org_list_url': accepted_orgs_url,
+ 'all_participating_orgs': (
+ self.data.program.nr_accepted_orgs <= len(participating_orgs)),
}
def templatePath(self):
@@ -160,16 +169,17 @@
class Leaderboard(Template):
- """Leaderboard template.
- """
+ """Leaderboard template."""
def __init__(self, data):
self.data = data
def context(self):
- r = self.data.redirect
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.data.redirect.program()
+
return {
- 'leaderboard_url': r.program().urlOf(url_names.GCI_LEADERBOARD),
+ 'leaderboard_url': self.data.redirect.urlOf(url_names.GCI_LEADERBOARD),
}
def templatePath(self):
@@ -192,7 +202,7 @@
return "v2/modules/gci/homepage/_connect_with_us.html"
-class Homepage(RequestHandler):
+class Homepage(GCIRequestHandler):
"""Encapsulate all the methods required to generate GCI Home page.
"""
diff --git a/app/soc/modules/gci/views/invite.py b/app/soc/modules/gci/views/invite.py
index abdf17f..8fc1004 100644
--- a/app/soc/modules/gci/views/invite.py
+++ b/app/soc/modules/gci/views/invite.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the view for GCI invitation page.
-"""
-
+"""Module containing the view for GCI invitation page."""
import logging
@@ -46,7 +42,7 @@
from soc.modules.gci.models.request import GCIRequest
from soc.modules.gci.views import forms as gci_forms
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -82,7 +78,7 @@
self.fields.insert(0, 'identifiers', field)
field.help_text = ugettext(
"Comma separated usernames or emails of the people to invite.")
-
+
def clean_identifiers(self):
"""Accepts link_ids or email addresses of users which may be invited.
"""
@@ -165,7 +161,7 @@
query.filter('role', self.request_data.kwargs['role'])
query.filter('org', self.request_data.organization)
return query
-
+
def _getProfile(self, user_to_invite):
key_name = '/'.join([
self.request_data.program.key().name(), user_to_invite.link_id])
@@ -182,7 +178,7 @@
fields = ['message']
-class InvitePage(RequestHandler):
+class InvitePage(GCIRequestHandler):
"""Encapsulate all the methods required to generate Invite page.
"""
@@ -227,7 +223,7 @@
assert isSet(self.data.organization)
invite_form = InviteForm(self.data, self.data.POST)
-
+
if not invite_form.is_valid():
return False
@@ -264,7 +260,7 @@
self.redirect.dashboard().to()
-class ManageInvite(RequestHandler):
+class ManageInvite(GCIRequestHandler):
"""View to manage the invitation by organization admins.
"""
@@ -358,7 +354,7 @@
return GCIProfile.get_by_key_name(key_name, parent=self.data.invited_user)
-class RespondInvite(RequestHandler):
+class RespondInvite(GCIRequestHandler):
"""View to respond to the invitation by the user.
"""
@@ -453,7 +449,7 @@
return 'v2/modules/gci/invite/_invite_list.html'
-class ListUserInvitesPage(RequestHandler):
+class ListUserInvitesPage(GCIRequestHandler):
"""View for the page that lists all the invites which have been sent to
the current user.
"""
diff --git a/app/soc/modules/gci/views/leaderboard.py b/app/soc/modules/gci/views/leaderboard.py
index d89e2fe..1bef7ae 100644
--- a/app/soc/modules/gci/views/leaderboard.py
+++ b/app/soc/modules/gci/views/leaderboard.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,8 +11,8 @@
# 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 view for GCI leaderboard page.
-"""
+
+"""Module containing the view for GCI leaderboard page."""
from soc.logic.exceptions import AccessViolation
@@ -28,7 +26,7 @@
from soc.modules.gci.templates.task_list import TaskList
from soc.modules.gci.views import common_templates
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -112,7 +110,7 @@
return task_logic.queryAllTasksClosedByStudent(self.data.url_profile)
-class LeaderboardPage(RequestHandler):
+class LeaderboardPage(GCIRequestHandler):
"""View for the leaderboard page.
"""
@@ -126,7 +124,7 @@
]
def checkAccess(self):
- pass
+ self.check.isHost()
def jsonContext(self):
list_content = LeaderboardList(self.request, self.data).getListData()
@@ -152,7 +150,7 @@
return context
-class StudentTasksPage(RequestHandler):
+class StudentTasksPage(GCIRequestHandler):
"""View for the list of all the tasks closed by the specified student.
"""
@@ -166,10 +164,16 @@
]
def checkAccess(self):
- pass
+ self.mutator.studentFromKwargs()
+ try:
+ self.check.isHost()
+ except AccessViolation:
+ self.check.hasProfile()
+ # check if the profile in URL kwargs is the current profile
+ if self.data.profile.key() != self.data.url_profile.key():
+ raise AccessViolation('You do not have access to this data')
def jsonContext(self):
- self.mutator.studentFromKwargs()
list_content = AllStudentTasksList(self.request, self.data).getListData()
if not list_content:
@@ -178,7 +182,6 @@
return list_content.content()
def context(self):
- self.mutator.studentFromKwargs()
return {
'page_name': "Tasks closed by %s" % self.data.url_profile.name(),
'tasks_list': AllStudentTasksList(self.request, self.data),
diff --git a/app/soc/modules/gci/views/moderate_delete_account.py b/app/soc/modules/gci/views/moderate_delete_account.py
new file mode 100644
index 0000000..c8932ab
--- /dev/null
+++ b/app/soc/modules/gci/views/moderate_delete_account.py
@@ -0,0 +1,60 @@
+# Copyright 2011 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 for the GCI delete account page."""
+
+from soc.logic import delete_account
+
+from soc.views.helper import url_patterns
+
+from soc.modules.gci.logic import profile as profile_logic
+from soc.modules.gci.views import base
+from soc.modules.gci.views.helper.url_patterns import url
+
+
+class ModerateDeleteAccountPage(base.GCIRequestHandler):
+ """View for the GCI delete account page.
+ """
+
+ def templatePath(self):
+ return 'v2/modules/gci/moderate_delete_account/base.html'
+
+ def djangoURLPatterns(self):
+ return [
+ url(r'admin/delete_account/%s$' % url_patterns.PROFILE,
+ self, name='gci_moderate_delete_account')
+ ]
+
+ def checkAccess(self):
+ self.check.isHost()
+
+ self.mutator.profileFromKwargs()
+
+ def context(self):
+ profile = self.data.url_profile
+
+ return {
+ 'page_name': 'Moderate delete account requests',
+ 'profile': profile,
+ 'has_tasks': profile_logic.hasTasks(profile),
+ 'has_created_or_modified_tasks': profile_logic.hasCreatedOrModifiedTask(
+ profile),
+ 'has_task_comments': profile_logic.hasTaskComments(profile),
+ 'has_other_gci_profiles': profile_logic.hasOtherGCIProfiles(profile),
+ 'has_other_gsoc_profiles': profile_logic.hasOtherGSoCProfiles(profile),
+ }
+
+ def post(self):
+ delete_account.confirm_delete(self.data.url_profile)
+ self.redirect.program().to('gci_moderate_delete_account', validated=True)
diff --git a/app/soc/modules/gci/views/org_app.py b/app/soc/modules/gci/views/org_app.py
index 0bc48a2..9a71999 100644
--- a/app/soc/modules/gci/views/org_app.py
+++ b/app/soc/modules/gci/views/org_app.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GCI Organization application.
-"""
-
+"""Module for the GCI Organization application."""
import logging
@@ -32,12 +28,12 @@
from soc.logic import org_app as org_app_logic
from soc.modules.gci.views import forms as gci_forms
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
-class GCIOrgAppEditPage(RequestHandler):
+class GCIOrgAppEditPage(GCIRequestHandler):
"""View for creating/editing organization application.
"""
@@ -66,9 +62,12 @@
else:
page_name = 'Create new organization application'
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.redirect.program()
+
context = {
'page_name': page_name,
- 'post_url': self.redirect.program().urlOf('gci_edit_org_app'),
+ 'post_url': self.redirect.urlOf('gci_edit_org_app'),
'forms': [form],
'error': bool(form.errors),
}
@@ -105,13 +104,15 @@
def post(self):
org_app = self.orgAppFromForm()
if org_app:
- r = self.redirect.program()
- r.to('gci_edit_org_app', validated=True)
+ # TODO(nathaniel): make unnecessary this .program() call.
+ self.redirect.program()
+
+ self.redirect.to('gci_edit_org_app', validated=True)
else:
self.get()
-class GCIOrgAppPreviewPage(RequestHandler):
+class GCIOrgAppPreviewPage(GCIRequestHandler):
"""View for organizations to submit their application.
"""
@@ -141,7 +142,7 @@
return context
-class GCIOrgAppTakePage(RequestHandler):
+class GCIOrgAppTakePage(GCIRequestHandler):
"""View for organizations to submit their application.
"""
@@ -161,7 +162,11 @@
# FIXME: There will never be organization in kwargs
show_url = None
if 'organization' in self.kwargs:
- show_url = self.data.redirect.organization().urlOf('gci_show_org_app')
+ # TODO(nathaniel): make this .organization() call unnecessary. Like,
+ # more than it already is (see the note above).
+ self.data.redirect.organization()
+
+ show_url = self.data.redirect.urlOf('gci_show_org_app')
self.check.isSurveyActive(self.data.org_app, show_url)
@@ -229,12 +234,12 @@
self.get()
-class GCIOrgAppRecordsList(org_app.OrgAppRecordsList, RequestHandler):
+class GCIOrgAppRecordsList(org_app.OrgAppRecordsList, GCIRequestHandler):
"""View for listing all records of a GCI Organization application.
"""
def __init__(self, *args, **kwargs):
- RequestHandler.__init__(self, *args, **kwargs)
+ GCIRequestHandler.__init__(self, *args, **kwargs)
org_app.OrgAppRecordsList.__init__(self, 'gci_show_org_app')
def djangoURLPatterns(self):
@@ -290,15 +295,13 @@
class OrgAppReadOnlyTemplate(org_app.OrgAppReadOnlyTemplate):
- """Template to construct readonly organization application record.
- """
+ """Template to construct readonly organization application record."""
template_path = 'v2/modules/gci/org_app/readonly_template.html'
-class GCIOrgAppShowPage(RequestHandler):
- """View to display the readonly page for organization application.
- """
+class GCIOrgAppShowPage(GCIRequestHandler):
+ """View to display the readonly page for organization application."""
def djangoURLPatterns(self):
return [
diff --git a/app/soc/modules/gci/views/org_home.py b/app/soc/modules/gci/views/org_home.py
index 5463568..5bb4ae9 100644
--- a/app/soc/modules/gci/views/org_home.py
+++ b/app/soc/modules/gci/views/org_home.py
@@ -12,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the Org Homepage view.
-"""
-
+"""Module containing the Org Homepage view."""
from django.utils.translation import ugettext
@@ -29,17 +27,17 @@
from soc.modules.gci.models.organization import GCIOrganization
from soc.modules.gci.models.task import CLAIMABLE
from soc.modules.gci.models.task import GCITask
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper.url_patterns import url
from soc.modules.gci.views.helper import url_names
class AboutUs(Template):
- """About us template.
- """
+ """About us template."""
+
def __init__(self, data):
self.data = data
-
+
def context(self):
org = self.data.organization
return {
@@ -48,14 +46,13 @@
'homepage': org.home_page,
'short_name': org.short_name,
}
-
+
def templatePath(self):
return 'v2/modules/gci/org_home/_about_us.html'
class ContactUs(Template):
- """Organization Contact template.
- """
+ """Organization Contact template."""
def __init__(self, data):
self.data = data
@@ -70,13 +67,13 @@
class OpenTasksList(Template):
- """List to display all the open tasks for the current organization.
- """
+ """List to display all the open tasks for the current organization."""
+
def __init__(self, request, data):
self.request = request
self.data = data
list_config = lists.ListConfiguration()
-
+
list_config.addSimpleColumn('title', 'Title')
#list_config.addColumn(
# 'task_type', 'Type',
@@ -87,12 +84,12 @@
lambda entity, *args: entity.taskTimeToComplete())
list_config.addColumn('types', 'Type',
lambda entity, *args: ", ".join(entity.types))
-
+
list_config.setRowAction(
lambda e, *args: data.redirect.id(e.key().id()).urlOf(url_names.GCI_VIEW_TASK))
self.list_config = list_config
-
+
def context(self):
description = 'List of all Open tasks.'
list = lists.ListConfigurationResponse(
@@ -100,7 +97,7 @@
return {
'lists': [list],
}
-
+
def getListData(self):
if lists.getListIndex(self.request) != 0:
return None
@@ -112,30 +109,30 @@
response_builder = lists.RawQueryContentResponseBuilder(
self.request, self.list_config, q, starter)
return response_builder.build()
-
+
def templatePath(self):
return 'v2/modules/gci/org_home/_open_tasks.html'
class CompletedTasksList(Template):
- """List to display all the closed/completed tasks for the current organization.
- """
+ """List to display all the closed/completed tasks for the current organization."""
+
def __init__(self, request, data):
self.request = request
self.data = data
-
+
list_config = lists.ListConfiguration()
-
+
list_config.addSimpleColumn('title', 'Title')
list_config.addColumn('student', 'Student',
lambda entity, *args: entity.student.name())
list_config.addColumn('types', 'Type',
lambda entity, *args: ", ".join(entity.types))
-
+
list_config.setRowAction(
lambda e, *args: data.redirect.id(e.key().id()).urlOf(url_names.GCI_VIEW_TASK))
self.list_config = list_config
-
+
def context(self):
description = 'List of all Completed tasks.'
list = lists.ListConfigurationResponse(
@@ -143,7 +140,7 @@
return {
'lists': [list],
}
-
+
def getListData(self):
if lists.getListIndex(self.request) != 1:
return None
@@ -155,14 +152,13 @@
response_builder = lists.RawQueryContentResponseBuilder(
self.request, self.list_config, q, starter)
return response_builder.build()
-
+
def templatePath(self):
return 'v2/modules/gci/org_home/_closed_tasks.html'
-class GCIBanOrgPost(BanOrgPost, RequestHandler):
- """Handles banning/unbanning of GCI organizations.
- """
+class GCIBanOrgPost(BanOrgPost, GCIRequestHandler):
+ """Handles banning/unbanning of GCI organizations."""
def _getModulePrefix(self):
return 'gci'
@@ -178,9 +174,8 @@
class GCIHostActions(HostActions):
- """Template to render the left side host actions.
- """
-
+ """Template to render the left side host actions."""
+
DEF_BAN_ORGANIZATION_HELP = ugettext(
'When an organization is banned, students cannot work on their tasks')
@@ -191,9 +186,9 @@
return self.DEF_BAN_ORGANIZATION_HELP
-class OrgHomepage(RequestHandler):
- """Encapsulates all the methods required to render the org homepage.
- """
+class OrgHomepage(GCIRequestHandler):
+ """Encapsulates all the methods required to render the org homepage."""
+
def templatePath(self):
return 'v2/modules/gci/org_home/base.html'
diff --git a/app/soc/modules/gci/views/org_profile.py b/app/soc/modules/gci/views/org_profile.py
index 8da06a1..f8d1184 100644
--- a/app/soc/modules/gci/views/org_profile.py
+++ b/app/soc/modules/gci/views/org_profile.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,15 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GCI organization profile page.
-"""
-
+"""Module for the GCI organization profile page."""
from soc.views.helper import url_patterns
from soc.views import org_profile
from soc.modules.gci.models.organization import GCIOrganization
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views import forms as gci_forms
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -31,9 +27,9 @@
'task_quota_limit',
]
+
class OrgProfileForm(org_profile.OrgProfileForm):
- """Django form for the organization profile.
- """
+ """Django form for the organization profile."""
def __init__(self, *args, **kwargs):
super(OrgProfileForm, self).__init__(
@@ -49,8 +45,7 @@
class OrgCreateProfileForm(OrgProfileForm):
- """Django form to create the organization profile.
- """
+ """Django form to create the organization profile."""
class Meta:
model = GCIOrganization
@@ -58,9 +53,8 @@
exclude = PROFILE_EXCLUDE
-class OrgProfilePage(RequestHandler):
- """View for the Organization Profile page.
- """
+class OrgProfilePage(GCIRequestHandler):
+ """View for the Organization Profile page."""
def djangoURLPatterns(self):
return [
@@ -102,15 +96,18 @@
}
if self.data.organization:
- r = self.data.redirect.organization()
- context['org_home_page_link'] = r.urlOf('gci_org_home')
+ # TODO(nathaniel): make this .organization() call unnecessary.
+ self.data.redirect.organization()
+ context['org_home_page_link'] = self.data.redirect.urlOf('gci_org_home')
return context
def post(self):
org_profile = self.createOrgProfileFromForm()
if org_profile:
- self.redirect.organization(org_profile)
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.redirect.organization(organization=org_profile)
+
self.redirect.to('edit_gci_org_profile', validated=True)
else:
self.get()
@@ -121,7 +118,6 @@
Returns:
a newly created organization entity or None
"""
-
if self.data.organization:
form = OrgProfileForm(self.data.POST, instance=self.data.organization)
else:
@@ -134,7 +130,7 @@
org_id = self.data.GET['org_id']
form.cleaned_data['founder'] = self.data.user
form.cleaned_data['scope'] = self.data.program
- form.cleaned_data['scope_path'] = self.data.program.key().name()
+ form.cleaned_data['scope_path'] = self.data.program.key().name()
form.cleaned_data['link_id'] = org_id
key_name = '%s/%s' % (self.data.program.key().name(), org_id)
entity = form.create(key_name=key_name)
diff --git a/app/soc/modules/gci/views/org_score.py b/app/soc/modules/gci/views/org_score.py
index 320dae8..73061a0 100644
--- a/app/soc/modules/gci/views/org_score.py
+++ b/app/soc/modules/gci/views/org_score.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2012 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GCI organization score page.
-"""
-
+"""Module for the GCI organization score page."""
from soc.logic import exceptions
from soc.views.helper import lists
@@ -25,7 +21,7 @@
from soc.modules.gci.models.score import GCIOrgScore
from soc.modules.gci.templates.org_list import BasicOrgList
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -88,7 +84,7 @@
return 'v2/modules/gci/leaderboard/_leaderboard_list.html'
-class OrgScoresForOrgzanizationPage(RequestHandler):
+class OrgScoresForOrgzanizationPage(GCIRequestHandler):
"""View for the organizations scores page.
"""
@@ -106,7 +102,7 @@
def context(self):
return {
- 'page_name': "Organization scores for %s" %
+ 'page_name': "Organization scores for %s" %
self.data.organization.name,
'org_scores_list': OrgScoresList(self.request, self.data),
}
@@ -130,12 +126,14 @@
def _getRedirect(self):
def redirect(e, *args):
- r = self.data.redirect
- return r.organization(e).urlOf(url_names.GCI_ORG_SCORES)
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.data.redirect.organization(organization=e)
+
+ return self.data.redirect.urlOf(url_names.GCI_ORG_SCORES)
return redirect
-class ChooseOrganizationForOrgScorePage(RequestHandler):
+class ChooseOrganizationForOrgScorePage(GCIRequestHandler):
"""View with a list of organizations. When a user clicks on one of them,
he or she is moved to the organization scores for this organization.
"""
diff --git a/app/soc/modules/gci/views/participants.py b/app/soc/modules/gci/views/participants.py
index d311574..8e42d33 100644
--- a/app/soc/modules/gci/views/participants.py
+++ b/app/soc/modules/gci/views/participants.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the view for GCI tasks list page.
-"""
-
+"""Module containing the view for GCI tasks list page."""
from soc.logic.exceptions import AccessViolation
from soc.views.helper import addresses
@@ -25,7 +21,7 @@
from soc.views.template import Template
from soc.modules.gci.models.profile import GCIProfile
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper.url_patterns import url
@@ -95,9 +91,8 @@
return "v2/modules/gci/participants/_mentors_list.html"
-class MentorsListAdminPage(RequestHandler):
- """View for the organization admin and mentors page for admin.
- """
+class MentorsListAdminPage(GCIRequestHandler):
+ """View for the organization admin and mentors page for admin."""
def templatePath(self):
return 'v2/modules/gci/participants/base.html'
diff --git a/app/soc/modules/gci/views/profile.py b/app/soc/modules/gci/views/profile.py
index 88a1e64..a90a749 100644
--- a/app/soc/modules/gci/views/profile.py
+++ b/app/soc/modules/gci/views/profile.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GCI profile page.
-"""
-
+"""Module for the GCI profile page."""
from django import forms as django_forms
from django.core.urlresolvers import reverse
@@ -37,7 +33,7 @@
from soc.modules.gci.models.profile import GCIProfile
from soc.modules.gci.models.profile import GCIStudentInfo
from soc.modules.gci.views import forms as gci_forms
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
@@ -268,9 +264,8 @@
model, ['school_country', 'school_type', 'degree'])
-class GCIProfilePage(profile.ProfilePage, RequestHandler):
- """View for the GCI participant profile.
- """
+class GCIProfilePage(profile.ProfilePage, GCIRequestHandler):
+ """View for the GCI participant profile."""
def checkAccess(self):
self.check.isProgramVisible()
@@ -317,7 +312,7 @@
if 'delete_account' in self.data.POST:
self.deleteAccountPostAction()
else: # regular POST request
- self.editProfilePostAction()
+ self.editProfilePostAction()
def deleteAccountPostAction(self):
"""Handler for Delete Account POST action.
@@ -326,19 +321,20 @@
self.redirect.to('gci_delete_account', secure=True)
def editProfilePostAction(self):
- """Handler for regular (edit/create profile) POST action.
- """
+ """Handler for regular (edit/create profile) POST action."""
if not self.validate():
self.get()
return
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.redirect.program()
+
org_id = self.data.GET.get('new_org')
if org_id:
- create_url = self.redirect.program().urlOf('create_gci_org_profile')
+ create_url = self.redirect.urlOf('create_gci_org_profile')
raise RedirectRequest(create_url + '?org_id=' + org_id)
- self.redirect.program()
self.redirect.to(self._getEditProfileURLName(), validated=True, secure=True)
def _getModulePrefix(self):
@@ -390,4 +386,3 @@
def _getStudentInfoForm(self):
return GCIStudentInfoForm(self.data.POST or None,
instance=self.data.student_info)
-
diff --git a/app/soc/modules/gci/views/profile_show.py b/app/soc/modules/gci/views/profile_show.py
index f1550c5..a73741c 100644
--- a/app/soc/modules/gci/views/profile_show.py
+++ b/app/soc/modules/gci/views/profile_show.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,10 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for displaying the GCI profile read-only page.
-"""
+"""Module for displaying the GCI profile read-only page."""
-
+import httplib
import logging
from django.utils import translation
@@ -30,7 +27,7 @@
from soc.modules.gci.models.profile import GCIProfile
from soc.modules.gci.views import readonly_template
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -86,9 +83,8 @@
'tshirt_style', 'tshirt_size', 'gender', 'program_knowledge']
-class GCIProfileShowPage(profile_show.ProfileShowPage, RequestHandler):
- """View to display the read-only profile page.
- """
+class GCIProfileShowPage(profile_show.ProfileShowPage, GCIRequestHandler):
+ """View to display the read-only profile page."""
def djangoURLPatterns(self):
return [
@@ -155,11 +151,10 @@
return context
def post(self):
- """Handles student form verification by host.
- """
+ """Handles student form verification by host."""
if not self.data.url_profile.student_info:
logging.warn(NON_STUDENT_ERR_MSG)
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
return
post_data = self.data.POST
diff --git a/app/soc/modules/gci/views/program.py b/app/soc/modules/gci/views/program.py
index a46f3eb..d3cd001 100644
--- a/app/soc/modules/gci/views/program.py
+++ b/app/soc/modules/gci/views/program.py
@@ -29,7 +29,7 @@
from soc.modules.gci.models.program import GCIProgram
from soc.modules.gci.models.program import GCIProgramMessages
from soc.modules.gci.models.timeline import GCITimeline
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.forms import GCIModelForm
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -86,9 +86,8 @@
model = GCIProgramMessages
-class ProgramPage(RequestHandler):
- """View for the program profile.
- """
+class ProgramPage(GCIRequestHandler):
+ """View for the program profile."""
def djangoURLPatterns(self):
return [
@@ -145,7 +144,7 @@
self.get()
-class TimelinePage(RequestHandler):
+class TimelinePage(GCIRequestHandler):
"""View for the participant profile."""
def djangoURLPatterns(self):
@@ -192,7 +191,7 @@
class GCIProgramMessagesPage(
- program_view.ProgramMessagesPage, RequestHandler):
+ program_view.ProgramMessagesPage, GCIRequestHandler):
"""View for the content of GCI program specific messages to be sent."""
def djangoURLPatterns(self):
diff --git a/app/soc/modules/gci/views/request.py b/app/soc/modules/gci/views/request.py
index 0406cbd..78c6c66 100644
--- a/app/soc/modules/gci/views/request.py
+++ b/app/soc/modules/gci/views/request.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the view for GCI request page.
-"""
-
+"""Module containing the view for GCI request page."""
from google.appengine.ext import db
@@ -33,23 +29,21 @@
from soc.modules.gci.models.profile import GCIProfile
from soc.modules.gci.models.request import GCIRequest
from soc.modules.gci.views import forms as gci_forms
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
class RequestForm(gci_forms.GCIModelForm):
- """Django form for the invite page.
- """
+ """Django form for the invite page."""
class Meta:
model = GCIRequest
css_prefix = 'gci_intivation'
fields = ['message']
-class SendRequestPage(RequestHandler):
- """Encapsulate all the methods required to generate Send Request page.
- """
+class SendRequestPage(GCIRequestHandler):
+ """Encapsulate all the methods required to generate Send Request page."""
def templatePath(self):
return 'v2/modules/gci/request/base.html'
@@ -125,9 +119,8 @@
self.redirect.id(request.key().id()).to(url_names.GCI_MANAGE_REQUEST)
-class ManageRequestPage(RequestHandler):
- """View to manage the invitation by the sender.
- """
+class ManageRequestPage(GCIRequestHandler):
+ """View to manage the invitation by the sender."""
def templatePath(self):
return 'v2/modules/gci/request/base.html'
@@ -206,9 +199,8 @@
return 'Resubmit'
-class RespondRequestPage(RequestHandler):
- """View to accept or reject requests by organization admins.
- """
+class RespondRequestPage(GCIRequestHandler):
+ """View to accept or reject requests by organization admins."""
def templatePath(self):
return 'v2/modules/gci/request/show.html'
@@ -224,7 +216,7 @@
key_name = self.data.kwargs['user']
user_key = db.Key.from_path('User', key_name)
-
+
# fetch the request entity based on the id and parent key
request_id = int(self.data.kwargs['id'])
self.data.request_entity = GCIRequest.get_by_id(
@@ -252,6 +244,13 @@
user_key = GCIRequest.user.get_value_for_datastore(
self.data.request_entity)
+ profile_key_name = '/'.join([
+ self.data.program.key().name(), user_key.name()])
+ profile_key = db.Key.from_path(
+ 'GCIProfile', profile_key_name, parent=user_key)
+
+ self.data.requester_profile = profile = db.get(profile_key)
+
if 'accept' in self.data.POST:
options = db.create_transaction_options(xg=True)
@@ -259,14 +258,8 @@
organization_key = self.data.organization.key()
messages = self.data.program.getProgramMessages()
- link_id = user_key.name()
- profile_key_name = '/'.join([self.data.program.key().name(), link_id])
- profile_key = db.Key.from_path(
- 'GCIProfile', profile_key_name, parent=user_key)
-
def accept_request_txn():
request = db.get(request_key)
- self.data.requester_profile = profile = db.get(profile_key)
request.status = 'accepted'
@@ -351,7 +344,7 @@
return 'v2/modules/gci/request/_request_list.html'
-class ListUserRequestsPage(RequestHandler):
+class ListUserRequestsPage(GCIRequestHandler):
"""View for the page that lists all the requests which have been sent by
the current user.
"""
diff --git a/app/soc/modules/gci/views/student_forms.py b/app/soc/modules/gci/views/student_forms.py
index d503dc9..5a3ccdf 100644
--- a/app/soc/modules/gci/views/student_forms.py
+++ b/app/soc/modules/gci/views/student_forms.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for students in GCI to upload their forms.
-"""
-
+"""Module for students in GCI to upload their forms."""
from google.appengine.ext import blobstore
from google.appengine.dist import httplib
@@ -32,7 +28,7 @@
from soc.modules.gci.logic import profile as profile_logic
from soc.modules.gci.models.profile import GCIStudentInfo
from soc.modules.gci.views import forms as gci_forms
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -67,8 +63,10 @@
"""
super(UploadForm, self).__init__(*args, **kwargs)
- base_url = request_data.redirect.program().urlOf(
- url_names.GCI_STUDENT_FORM_UPLOAD)
+ # TODO(nathaniel): make this .program() call unnecessary.
+ request_data.redirect.program()
+
+ base_url = request_data.redirect.urlOf(url_names.GCI_STUDENT_FORM_UPLOAD)
self['consent_form'].field.widget = gci_forms.AsyncFileInput(
download_url='%s?%s' % (base_url, url_names.CONSENT_FORM_GET_PARAM),
@@ -82,7 +80,7 @@
self['consent_form'].field.help_text,
request_data.program.form_translations_url))
self['student_id_form'].field.help_text = (
- DEF_STUDENT_ID_FORM_TEXT_HELP %
+ DEF_STUDENT_ID_FORM_TEXT_HELP %
request_data.program.form_translations_url)
def clean(self):
@@ -114,9 +112,8 @@
return student_info
-class StudentFormUpload(RequestHandler):
- """View for uploading your student forms.
- """
+class StudentFormUpload(GCIRequestHandler):
+ """View for uploading your student forms."""
def djangoURLPatterns(self):
"""The URL pattern for the view.
@@ -138,14 +135,16 @@
return 'v2/modules/gci/student_forms/base.html'
def jsonContext(self):
- url = self.redirect.program().urlOf('gci_student_form_upload', secure=True)
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.redirect.program()
+
+ url = self.redirect.urlOf('gci_student_form_upload', secure=True)
return {
'upload_link': blobstore.create_upload_url(url),
}
def get(self):
- """Handles download of the forms otherwise resumes normal rendering.
- """
+ """Handles download of the forms otherwise resumes normal rendering."""
if 'consent_form' in self.data.GET:
download = self.data.student_info.consent_form
elif 'student_id_form' in self.data.GET:
@@ -154,14 +153,13 @@
return super(StudentFormUpload, self).get()
# download has been requested
- if not download:
- self.error(httplib.NOT_FOUND, 'File not found')
-
- self.response = bs_helper.sendBlob(download)
+ if download:
+ self.response = bs_helper.sendBlob(download)
+ else:
+ self.response = self.error(httplib.NOT_FOUND, message='File not found')
def context(self):
- """Handler for default HTTP GET request.
- """
+ """Handler for default HTTP GET request."""
context = {
'page_name': 'Student form upload'
}
@@ -215,30 +213,28 @@
form.save()
- self.redirect.program().to('gci_student_form_upload')
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.redirect.program()
+
+ self.redirect.to('gci_student_form_upload')
-class StudentFormDownload(RequestHandler):
- """View for uploading your student forms.
- """
+class StudentFormDownload(GCIRequestHandler):
+ """View for uploading your student forms."""
def djangoURLPatterns(self):
- """The URL pattern for the view.
- """
+ """The URL pattern for the view."""
return [
url(r'student/forms/%s$' % url_patterns.PROFILE, self,
name=url_names.GCI_STUDENT_FORM_DOWNLOAD)]
def checkAccess(self):
- """Denies access if you are not a host.
- """
+ """Denies access if you are not a host."""
self.check.isHost()
self.mutator.studentFromKwargs()
def get(self):
- """Allows hosts to download the student forms.
- """
- download = None
+ """Allows hosts to download the student forms."""
if url_names.CONSENT_FORM_GET_PARAM in self.data.GET:
download = self.data.url_student_info.consent_form
elif url_names.STUDENT_ID_FORM_GET_PARAM in self.data.GET:
@@ -247,7 +243,7 @@
raise BadRequest('No file requested')
# download has been requested
- if not download:
- self.error(httplib.NOT_FOUND, 'File not found')
-
- self.response = bs_helper.sendBlob(download)
+ if download:
+ self.response = bs_helper.sendBlob(download)
+ else:
+ self.response = self.error(httplib.NOT_FOUND, 'File not found')
diff --git a/app/soc/modules/gci/views/students_info.py b/app/soc/modules/gci/views/students_info.py
index 5b54a31..1934b76 100644
--- a/app/soc/modules/gci/views/students_info.py
+++ b/app/soc/modules/gci/views/students_info.py
@@ -12,34 +12,25 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the Students Info view for the admin.
-"""
+"""Module containing the Students Info view for the admin."""
-
-from google.appengine.ext import db
-
-from django.utils.dateformat import format
from django.utils.translation import ugettext
-from soc.logic.exceptions import AccessViolation
+from soc.logic import exceptions
from soc.views.helper import lists
from soc.views.helper import url_patterns
-from soc.modules.gci.models.profile import GCIStudentInfo
-from soc.modules.gci.models.score import GCIScore
-from soc.modules.gci.templates.student_list import StudentList
-from soc.modules.gci.views.base import RequestHandler
-from soc.modules.gci.views.helper.url_patterns import url
+from soc.modules.gci.templates import student_list
+from soc.modules.gci.views import base
from soc.modules.gci.views.helper import url_names
+from soc.modules.gci.views.helper import url_patterns as gci_url_patterns
-class AllParticipatingStudentList(StudentList):
- """Component for listing all the students participating in GCI.
- """
+class AllParticipatingStudentsList(student_list.StudentList):
+ """Component for listing all the students participating in GCI."""
def __init__(self, request, data):
- super(AllParticipatingStudentList, self).__init__(request, data)
-
+ super(AllParticipatingStudentsList, self).__init__(request, data)
def formVerified(verified_prop):
"""Returns Yes/No based on whether the form verified property's value."""
@@ -63,46 +54,45 @@
sp[e.parent_key()].link_id).urlOf(url_names.GCI_PROFILE_SHOW_ADMIN))
def context(self):
- list = lists.ListConfigurationResponse(
+ all_participating_students_list = lists.ListConfigurationResponse(
self.data, self._list_config, idx=self.idx)
return {
'name': 'students',
'title': 'Participating students',
- 'lists': [list],
- 'description': ugettext(
- 'List of all participating students'),
+ 'lists': [all_participating_students_list],
+ 'description': ugettext('List of all participating students'),
}
-class StudentsInfoPage(RequestHandler):
- """View for the students info page for the admin.
- """
+class StudentsInfoPage(base.GCIRequestHandler):
+ """View for the students info page for the admin."""
def templatePath(self):
return 'v2/modules/gci/students_info/base.html'
def djangoURLPatterns(self):
return [
- url(r'admin/students_info/%s$' % url_patterns.PROGRAM, self,
- name=url_names.GCI_STUDENTS_INFO),
+ gci_url_patterns.url(r'admin/students_info/%s$' % url_patterns.PROGRAM,
+ self, name=url_names.GCI_STUDENTS_INFO),
]
def checkAccess(self):
self.check.isHost()
def jsonContext(self):
- list_content = AllParticipatingStudentList(
- self.request, self.data).getListData()
+ all_participating_students_list = AllParticipatingStudentsList(
+ self.request, self.data)
+ list_content = all_participating_students_list.getListData()
- if not list_content:
- raise AccessViolation(
- 'You do not have access to this data')
- return list_content.content()
+ if list_content:
+ return list_content.content()
+ else:
+ raise exceptions.AccessViolation('You do not have access to this data')
def context(self):
return {
- 'page_name': "List of Students for %s" % self.data.program.name,
- 'students_info_list': AllParticipatingStudentList(
+ 'page_name': 'List of Students for %s' % self.data.program.name,
+ 'students_info_list': AllParticipatingStudentsList(
self.request, self.data),
}
diff --git a/app/soc/modules/gci/views/task.py b/app/soc/modules/gci/views/task.py
index 155ece0..f6de11c 100644
--- a/app/soc/modules/gci/views/task.py
+++ b/app/soc/modules/gci/views/task.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,11 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Views for the GCI Task view page.
-"""
-
+"""Views for the GCI Task view page."""
import datetime
+import httplib
from google.appengine.ext import blobstore
from google.appengine.ext import db
@@ -44,7 +41,7 @@
from soc.modules.gci.models.task import UNPUBLISHED
from soc.modules.gci.models.work_submission import GCIWorkSubmission
from soc.modules.gci.views import forms as gci_forms
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_patterns
from soc.modules.gci.views.helper.url_patterns import url
from soc.modules.gci.views.helper import url_names
@@ -62,14 +59,14 @@
DEF_NO_URL = ugettext(
'An error occurred, please submit a valid URL.')
-DEF_NO_WORK_FOUND = ugettext('No submission found with id %i')
+DEF_NO_WORK_FOUND = ugettext('No submission found with id %s')
DEF_NOT_ALLOWED_TO_DELETE = ugettext(
'You are not allowed to delete this submission')
DEF_CANT_SEND_FOR_REVIEW = ugettext(
- 'Only a task that you own and that has submitted work can be send in '
- 'for review.')
+ 'Only a task that you own and that has submitted work and that has '
+ 'not been closed can be send in for review.')
class CommentForm(gci_forms.GCIModelForm):
@@ -176,9 +173,8 @@
return url
-class TaskViewPage(RequestHandler):
- """View for the GCI Task view page where all the actions happen.
- """
+class TaskViewPage(GCIRequestHandler):
+ """View for the GCI Task view page where all the actions happen."""
def djangoURLPatterns(self):
"""URL pattern for this view.
@@ -231,7 +227,8 @@
if 'send_for_review' in self.data.GET:
self.check.isBeforeAllWorkStopped()
if not task_logic.isOwnerOfTask(self.data.task, self.data.profile) or \
- not self.data.work_submissions:
+ not self.data.work_submissions or \
+ self.data.task.status not in TASK_IN_PROGRESS:
self.check.fail(DEF_CANT_SEND_FOR_REVIEW)
if 'delete_submission' in self.data.GET:
@@ -304,7 +301,7 @@
elif 'work_file_submit' in self.data.POST or 'submit_work' in self.data.GET:
return self._postSubmitWork()
else:
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
def _postComment(self):
"""Handles the POST call for the form that creates comments.
@@ -435,11 +432,13 @@
def _postDeleteSubmission(self):
"""POST handler to delete a GCIWorkSubmission.
"""
- id = self._submissionId()
- work = GCIWorkSubmission.get_by_id(id, parent=self.data.task)
+ submission_id = self._submissionId()
+ work = GCIWorkSubmission.get_by_id(submission_id, parent=self.data.task)
if not work:
- return self.error(400, DEF_NO_WORK_FOUND %id)
+ self.response = self.error(
+ httplib.BAD_REQUEST, message=DEF_NO_WORK_FOUND % submission_id)
+ return
# Deletion of blobs always runs separately from transaction so it has no
# added value to use it here.
@@ -520,7 +519,7 @@
is_owner = task_logic.isOwnerOfTask(task, profile)
if is_org_admin:
- can_unpublish = (task.status in CLAIMABLE) and not task.student
+ can_unpublish = task.status == 'Open' and not task.student
context['button_unpublish'] = can_unpublish
can_publish = task.status in UNPUBLISHED
@@ -540,7 +539,10 @@
if is_student:
if not self.data.timeline.tasksClaimEnded():
if not profile_logic.hasStudentFormsUploaded(profile.student_info):
- context['student_forms_link'] = self.data.redirect.program().urlOf(
+ # TODO(nathaniel): make this .program() call unnecessary.
+ self.data.redirect.program()
+
+ context['student_forms_link'] = self.data.redirect.urlOf(
url_names.GCI_STUDENT_FORM_UPLOAD)
# TODO(lennie): Separate the access check out in to different
# methods and add a context variable to show separate messages.
@@ -699,33 +701,28 @@
return context
def _commentingAllowed(self):
- """Returns true iff the comments are allowed to be posted at this time.
- """
+ """Returns true iff the comments are allowed to be posted at this time."""
return not self.data.timeline.allWorkStopped() or (
not self.data.timeline.allReviewsStopped() and
self.data.mentorFor(self.data.task.org))
def templatePath(self):
- """Returns the path to the template that should be used in render().
- """
+ """Returns the path to the template that should be used in render()."""
return 'v2/modules/gci/task/_comments.html'
-class WorkSubmissionDownload(RequestHandler):
- """Request handler for downloading blobs from a GCIWorkSubmission.
- """
+class WorkSubmissionDownload(GCIRequestHandler):
+ """Request handler for downloading blobs from a GCIWorkSubmission."""
def djangoURLPatterns(self):
- """URL pattern for this view.
- """
+ """URL pattern for this view."""
return [
url(r'work/download/%s$' % url_patterns.TASK, self,
name='gci_download_work'),
]
def checkAccess(self):
- """Checks whether this task is visible to the public.
- """
+ """Checks whether this task is visible to the public."""
self.mutator.taskFromKwargs()
self.check.isTaskVisible()
@@ -733,13 +730,13 @@
"""Attempts to download the blob in the worksubmission that is specified
in the GET argument.
"""
- id_s = self.request.GET.get('id', '')
- id = int(id_s) if id_s.isdigit() else -1
+ id_string = self.request.GET.get('id', '')
+ submission_id = int(id_string) if id_string.isdigit() else -1
- work = GCIWorkSubmission.get_by_id(id, self.data.task)
+ work = GCIWorkSubmission.get_by_id(submission_id, self.data.task)
- if not work or not work.upload_of_work:
- return self.error(400, DEF_NO_WORK_FOUND %id)
-
- upload = work.upload_of_work
- self.response = bs_helper.sendBlob(upload)
+ if work and work.upload_of_work:
+ self.response = bs_helper.sendBlob(work.upload_of_work)
+ else:
+ self.response = self.error(
+ httplib.BAD_REQUEST, message=DEF_NO_WORK_FOUND % id_string)
diff --git a/app/soc/modules/gci/views/task_create.py b/app/soc/modules/gci/views/task_create.py
index daa12a8..5ccf15c 100644
--- a/app/soc/modules/gci/views/task_create.py
+++ b/app/soc/modules/gci/views/task_create.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Views for creating/editing GCI Tasks.
-"""
-
+"""Views for creating/editing GCI Tasks."""
import datetime
@@ -35,7 +31,7 @@
from soc.modules.gci.models import task
from soc.modules.gci.models.task import DifficultyLevel
from soc.modules.gci.views import forms as gci_forms
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper.url_patterns import url
@@ -289,9 +285,8 @@
return "v2/modules/gci/task_create/_full_edit.html"
-class TaskCreatePage(RequestHandler):
- """View to create a new task.
- """
+class TaskCreatePage(GCIRequestHandler):
+ """View to create a new task."""
def djangoURLPatterns(self):
return [
diff --git a/app/soc/modules/gci/views/task_list.py b/app/soc/modules/gci/views/task_list.py
index ffae9d3..9afab77 100644
--- a/app/soc/modules/gci/views/task_list.py
+++ b/app/soc/modules/gci/views/task_list.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,22 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for GCI historic task page.
-"""
-
+"""Module containing the views for GCI historic task page."""
from soc.logic.exceptions import AccessViolation
from soc.views.helper import url_patterns
from soc.views.helper import lists
-from soc.views.helper.access_checker import isSet
from soc.views.template import Template
from soc.modules.gci.logic import task as task_logic
from soc.modules.gci.models.task import GCITask
from soc.modules.gci.templates.org_list import BasicOrgList
from soc.modules.gci.templates.task_list import TaskList
-from soc.modules.gci.views.base import RequestHandler
-from soc.modules.gci.views.forms import GCIModelForm
+from soc.modules.gci.views.base import GCIRequestHandler
#from soc.modules.gci.views.base_templates import ProgramSelect
from soc.modules.gci.views.helper import url_names
from soc.modules.gci.views.helper.url_patterns import url
@@ -80,7 +74,7 @@
return 'v2/modules/gci/task/_task_list.html'
-class TaskListPage(RequestHandler):
+class TaskListPage(GCIRequestHandler):
"""View for the list task page.
"""
@@ -131,7 +125,7 @@
self.data.url_profile, self.data.organization, 'Closed')
-class StudentTasksForOrganizationPage(RequestHandler):
+class StudentTasksForOrganizationPage(GCIRequestHandler):
"""View for the list of student tasks for organization.
"""
@@ -172,15 +166,17 @@
def _getRedirect(self):
def redirect(e, *args):
- r = self.data.redirect
- return r.organization(e).urlOf(url_names.GCI_ORG_TASKS_ALL)
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.data.redirect.organization(organization=e)
+
+ return r.urlOf(url_names.GCI_ORG_TASKS_ALL)
return redirect
def _getDescription(self):
return 'Choose an organization for which to display tasks.'
-class ChooseOrganizationPage(RequestHandler):
+class ChooseOrganizationPage(GCIRequestHandler):
"""View with a list of organizations. When a user clicks on one of them,
he or she is moved to the organization tasks for this organization.
"""
@@ -229,7 +225,7 @@
return task_logic.queryForOrganization(self.data.organization)
-class AllOrganizationTasksPage(RequestHandler):
+class AllOrganizationTasksPage(GCIRequestHandler):
"""View for program admins to see all tasks created by an organization
which is specified in the URL.
"""
diff --git a/app/soc/modules/gsoc/logic/helper/notifications.py b/app/soc/modules/gsoc/logic/helper/notifications.py
index d040fef..e42562d 100644
--- a/app/soc/modules/gsoc/logic/helper/notifications.py
+++ b/app/soc/modules/gsoc/logic/helper/notifications.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2012 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,15 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Notifications for the GSoC module.
-"""
+"""Notifications for the GSoC module."""
from django.utils.translation import ugettext
from soc.logic.helper.notifications import getContext
from soc.views.helper.access_checker import isSet
-
DEF_NEW_PROPOSAL_SUBJECT = ugettext(
'[%(org)s] New proposal by %(proposer_name)s: %(proposal_name)s')
@@ -155,8 +151,10 @@
slot_transfer: entity that holds the slot transfer request information
update: True if the request was updated, False if the new one was created
"""
+ # TODO(nathaniel): make unnecessary this .program() call.
+ data.redirect.program()
- slot_transfer_admin_url = data.redirect.program().urlOf(
+ slot_transfer_admin_url = data.redirect.urlOf(
'gsoc_admin_slots_transfer', full=True)
message_properties = {
diff --git a/app/soc/modules/gsoc/views/accept_proposals.py b/app/soc/modules/gsoc/views/accept_proposals.py
index 3f4fd52..fc4dced 100644
--- a/app/soc/modules/gsoc/views/accept_proposals.py
+++ b/app/soc/modules/gsoc/views/accept_proposals.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for GSoC accept proposals.
-"""
-
+"""Module containing the views for GSoC accept proposals."""
from google.appengine.api import taskqueue
@@ -25,13 +21,12 @@
from soc.views.helper import url_patterns
from soc.modules.gsoc.logic import accept_proposals as conversion_logic
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper.url_patterns import url
-class AcceptProposalsPage(RequestHandler):
- """View for the host to trigger proposals to projets conversion.
- """
+class AcceptProposalsPage(GSoCRequestHandler):
+ """View for the host to trigger proposals to projets conversion."""
def templatePath(self):
return 'v2/modules/gsoc/accept_proposals/base.html'
diff --git a/app/soc/modules/gsoc/views/accept_withdraw_projects.py b/app/soc/modules/gsoc/views/accept_withdraw_projects.py
index c041933..fc8d1ac 100644
--- a/app/soc/modules/gsoc/views/accept_withdraw_projects.py
+++ b/app/soc/modules/gsoc/views/accept_withdraw_projects.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -37,7 +35,7 @@
from soc.modules.gsoc.models.profile import GSoCStudentInfo
from soc.modules.gsoc.models.project import GSoCProject
from soc.modules.gsoc.models.proposal import GSoCProposal
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -218,7 +216,7 @@
return "v2/modules/gsoc/accept_withdraw_projects/_project_list.html"
-class AcceptProposals(RequestHandler):
+class AcceptProposals(GSoCRequestHandler):
"""View for accepting individual proposals.
"""
@@ -429,7 +427,7 @@
return "v2/modules/gsoc/accept_withdraw_projects/_project_list.html"
-class WithdrawProjects(RequestHandler):
+class WithdrawProjects(GSoCRequestHandler):
"""View methods for withdraw projects
"""
diff --git a/app/soc/modules/gsoc/views/accepted_orgs.py b/app/soc/modules/gsoc/views/accepted_orgs.py
index 0d9da8e..03bc260 100644
--- a/app/soc/modules/gsoc/views/accepted_orgs.py
+++ b/app/soc/modules/gsoc/views/accepted_orgs.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for GSoC accepted orgs.
-"""
-
+"""Module containing the views for GSoC accepted orgs."""
from django.conf.urls.defaults import url as django_url
@@ -28,25 +24,30 @@
from soc.views.helper import url_patterns
from soc.modules.gsoc.models.organization import GSoCOrganization
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper.url_patterns import url
class AcceptedOrgsList(Template):
- """Template for list of accepted organizations.
- """
+ """Template for list of accepted organizations."""
def __init__(self, request, data):
self.request = request
self.data = data
- r = data.redirect
+
+ # TODO(nathaniel): reduce this back to a lambda expression
+ # inside the setRowAction call below.
+ def RowAction(e, *args):
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.data.redirect.organization(e)
+
+ return self.data.redirect.urlOf('gsoc_org_home')
list_config = lists.ListConfiguration()
list_config.addColumn('name', 'Name',
lambda e, *args: e.name.strip())
list_config.addSimpleColumn('link_id', 'Link ID', hidden=True)
- list_config.setRowAction(
- lambda e, *args: r.organization(e).urlOf('gsoc_org_home'))
+ list_config.setRowAction(RowAction)
list_config.addColumn('tags', 'Tags',
lambda e, *args: ", ".join(e.tags))
list_config.addColumn(
@@ -88,7 +89,7 @@
return "v2/modules/gsoc/accepted_orgs/_project_list.html"
-class AcceptedOrgsPage(RequestHandler):
+class AcceptedOrgsPage(GSoCRequestHandler):
"""View for the accepted organizations page.
"""
diff --git a/app/soc/modules/gsoc/views/admin.py b/app/soc/modules/gsoc/views/admin.py
index 8db1160..0010568 100644
--- a/app/soc/modules/gsoc/views/admin.py
+++ b/app/soc/modules/gsoc/views/admin.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the admin pages.
-"""
-
+"""Module for the admin pages."""
import logging
@@ -53,7 +49,7 @@
from soc.modules.gsoc.models.proposal import GSoCProposal
from soc.modules.gsoc.models.proposal_duplicates import GSoCProposalDuplicate
from soc.modules.gsoc.views import forms as gsoc_forms
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.dashboard import BIRTHDATE_FORMAT
from soc.modules.gsoc.views.helper import url_names
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -111,7 +107,7 @@
return r.urlOf('gsoc_admin_dashboard')
-class DashboardPage(RequestHandler):
+class DashboardPage(GSoCRequestHandler):
"""Dashboard for admins.
"""
@@ -772,7 +768,7 @@
}
-class LookupLinkIdPage(RequestHandler):
+class LookupLinkIdPage(GSoCRequestHandler):
"""View for the participant profile.
"""
@@ -837,15 +833,21 @@
list_config.addColumn('org_admin', 'Org Admins',
(lambda e, *args: args[0][e.key()]))
- r = self.data.redirect
- list_config.setRowAction(
- lambda e, *args: r.organization(e).urlOf('gsoc_org_home'))
+ # TODO(nathaniel): squeeze this back into a lambda expression
+ # in the call to setRowAction below.
+ def RowAction(e, *args):
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.data.redirect.organization(organization=e)
+
+ return self.data.redirect.urlOf('gsoc_org_home')
+
+ list_config.setRowAction(RowAction)
return list_config
def context(self):
description = 'List of organizations accepted into %s' % (
- self.data.program.name)
+ self.data.program.name)
list = lists.ListConfigurationResponse(
self.data, self._list_config, 0, description)
@@ -887,14 +889,18 @@
"""
def extraColumn(self, list_config):
- use_cbox = False
- if self.request.GET.get('cbox'):
- use_cbox = True
+ use_cbox = bool(self.request.GET.get('cbox'))
+
+ # TODO(nathaniel): squeeze this back into a lambda expression in the
+ # call to setRowAction below.
+ def RowAction(e, *args):
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.data.redirect.organization(organization=e)
+
+ return self.data.redirect.urlOf('gsoc_proposals_org', cbox=use_cbox)
r = self.data.redirect
- list_config.setRowAction(
- lambda e, *args: r.organization(e).urlOf('gsoc_proposals_org',
- cbox=use_cbox))
+ list_config.setRowAction(RowAction)
list_config.addSimpleColumn('slots_desired', 'min', width=20)
list_config.addSimpleColumn('max_slots_desired', 'max', width=20)
list_config.addSimpleColumn('slots', 'Slots', width=20)
@@ -914,7 +920,7 @@
}
-class ProposalsAcceptedOrgsPage(RequestHandler):
+class ProposalsAcceptedOrgsPage(GSoCRequestHandler):
"""View for accepted orgs.
"""
@@ -952,14 +958,18 @@
"""
def extraColumn(self, list_config):
- use_cbox = False
- if self.request.GET.get('cbox'):
- use_cbox = True
+ use_cbox = bool(self.request.GET.get('cbox'))
+
+ # TODO(nathaniel): squeeze this back into a lambda expression in
+ # the call to setRowAction below.
+ def RowAction(e, *args):
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.data.redirect.organization(organization=e)
+
+ return self.data.redirect.urlOf('gsoc_projects_org', cbox=use_cbox)
r = self.data.redirect
- list_config.setRowAction(
- lambda e, *args: r.organization(e).urlOf('gsoc_projects_org',
- cbox=use_cbox))
+ list_config.setRowAction(RowAction)
list_config.addSimpleColumn('slots_desired', 'min', width=20)
list_config.addSimpleColumn('max_slots_desired', 'max', width=20)
list_config.addSimpleColumn('slots', 'Slots', width=20)
@@ -988,7 +998,7 @@
}
-class ProjectsAcceptedOrgsPage(RequestHandler):
+class ProjectsAcceptedOrgsPage(GSoCRequestHandler):
"""View for accepted orgs.
"""
@@ -1031,7 +1041,6 @@
self.request = request
self.data = data
- r = data.redirect
list_config = lists.ListConfiguration(add_key_column=False)
list_config.addColumn('key', 'Key', (lambda ent, *args: "%s/%s" % (
ent.parent().key().name(), ent.key().id())), hidden=True)
@@ -1149,7 +1158,7 @@
return response_builder.build(accepted, duplicates)
-class ProposalsPage(RequestHandler):
+class ProposalsPage(GSoCRequestHandler):
"""View for proposals for particular org.
"""
@@ -1251,7 +1260,7 @@
return "v2/modules/gsoc/admin/_projects_list.html"
-class ProjectsPage(RequestHandler):
+class ProjectsPage(GSoCRequestHandler):
"""View for projects of particular org.
"""
@@ -1402,7 +1411,7 @@
return response_builder.build()
-class SlotsPage(RequestHandler):
+class SlotsPage(GSoCRequestHandler):
"""View for the participant profile.
"""
@@ -1441,7 +1450,7 @@
}
-class SurveyReminderPage(RequestHandler):
+class SurveyReminderPage(GSoCRequestHandler):
"""Page to send out reminder emails to fill out a Survey.
"""
@@ -1623,7 +1632,7 @@
}
-class StudentsListPage(RequestHandler):
+class StudentsListPage(GSoCRequestHandler):
"""View that lists all the students associated with the program.
"""
@@ -1655,7 +1664,7 @@
}
-class ProjectsListPage(RequestHandler):
+class ProjectsListPage(GSoCRequestHandler):
"""View that lists all the projects associated with the program.
"""
@@ -1692,7 +1701,7 @@
}
-class OrgsListPage(RequestHandler):
+class OrgsListPage(GSoCRequestHandler):
"""View that lists all the projects associated with the program.
"""
diff --git a/app/soc/modules/gsoc/views/base.py b/app/soc/modules/gsoc/views/base.py
index b9c9a5d..02da11d 100644
--- a/app/soc/modules/gsoc/views/base.py
+++ b/app/soc/modules/gsoc/views/base.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,26 +12,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the boiler plate required to construct GSoC views.
-"""
+"""Module containing the boiler plate required to construct GSoC views."""
+import httplib
-from soc.views.base import RequestHandler
+from django import http
+
+from soc.views import base
from soc.modules.gsoc.views import base_templates
from soc.modules.gsoc.views.helper import access_checker
-from soc.modules.gsoc.views.helper.request_data import RequestData
-from soc.modules.gsoc.views.helper.request_data import RedirectHelper
+from soc.modules.gsoc.views.helper import request_data
-class RequestHandler(RequestHandler):
- """Customization required by GSoC to handle HTTP requests.
- """
+class GSoCRequestHandler(base.RequestHandler):
+ """Customization required by GSoC to handle HTTP requests."""
def render(self, template_path, context):
"""Renders the page using the specified context.
- See soc.views.base.RequestHandler.
+ See soc.views.base.RequestHandler for specification.
The context object is extended with the following values:
base_layout: path to the base template. cbox is for a page that need
@@ -56,11 +54,11 @@
context['header'] = base_templates.Header(self.data)
context['mainmenu'] = base_templates.MainMenu(self.data)
context['footer'] = base_templates.Footer(self.data)
- super(RequestHandler, self).render(template_path, context)
+ return super(GSoCRequestHandler, self).render(template_path, context)
def init(self, request, args, kwargs):
- self.data = RequestData()
- self.redirect = RedirectHelper(self.data, self.response)
+ self.data = request_data.RequestData()
+ self.redirect = request_data.RedirectHelper(self.data, self.response)
self.data.populate(self.redirect, request, args, kwargs)
if self.data.is_developer:
self.mutator = access_checker.DeveloperMutator(self.data)
@@ -68,20 +66,23 @@
else:
self.mutator = access_checker.Mutator(self.data)
self.check = access_checker.AccessChecker(self.data)
- super(RequestHandler, self).init(request, args, kwargs)
+ super(GSoCRequestHandler, self).init(request, args, kwargs)
def error(self, status, message=None):
+ """See base.RequestHandler.error for specification."""
if not self.data.program:
- return super(RequestHandler, self).error(status, message)
+ return super(GSoCRequestHandler, self).error(status, message)
- self.response.set_status(status, message=message)
+ # If message is not set, set it to the default associated with the
+ # given status (such as "Method Not Allowed" or "Service Unavailable").
+ message = message or httplib.responses.get(status, '')
- template_path = "v2/modules/gsoc/error.html"
+ template_path = 'v2/modules/gsoc/error.html'
context = {
- 'page_name': self.response.content,
- 'message': self.response.content,
+ 'page_name': message,
+ 'message': message,
'logged_in_msg': base_templates.LoggedInMsg(self.data, apply_link=False),
}
- self.response.content = ''
- self.render(template_path, context)
+ return http.HttpResponse(
+ content=self.render(template_path, context), status=status)
diff --git a/app/soc/modules/gsoc/views/dashboard.py b/app/soc/modules/gsoc/views/dashboard.py
index 9d691bc..9bb29a9 100644
--- a/app/soc/modules/gsoc/views/dashboard.py
+++ b/app/soc/modules/gsoc/views/dashboard.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GSoC participant dashboard.
-"""
-
+"""Module for the GSoC participant dashboard."""
import logging
@@ -57,7 +53,7 @@
from soc.modules.gsoc.models.proposal_duplicates import GSoCProposalDuplicate
from soc.modules.gsoc.models.request import GSoCRequest
from soc.modules.gsoc.models.score import GSoCScore
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.base_templates import LoggedInMsg
from soc.modules.gsoc.views.helper import url_names
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -134,7 +130,7 @@
}
-class DashboardPage(RequestHandler):
+class DashboardPage(GSoCRequestHandler):
"""View for the participant dashboard.
"""
@@ -1136,12 +1132,17 @@
"""
def __init__(self, request, data):
- """Initializes this component.
- """
- r = data.redirect
+ """Initializes this component."""
+ # TODO(nathaniel): put this back into a lambda expression in the
+ # setRowAction call below.
+ def RowAction(e, *args):
+ # TODO(nathaniel): make this .organization call unnecessary.
+ data.redirect.organization(organization=e)
+
+ return data.redirect.urlOf('gsoc_org_home')
+
list_config = lists.ListConfiguration()
- list_config.setRowAction(
- lambda e, *args: r.organization(e).urlOf('gsoc_org_home'))
+ list_config.setRowAction(RowAction)
if not data.program.allocations_visible:
list_config.addSimpleColumn('name', 'name')
@@ -1398,12 +1399,14 @@
def rowAction(d, *args):
key = d['key']
if key == 'tax_form':
- return data.redirect.program().urlOf('gsoc_tax_form', secure=True)
+ data.redirect.program()
+ return data.redirect.urlOf('gsoc_tax_form', secure=True)
if key == 'enrollment_form':
- return data.redirect.program().urlOf('gsoc_enrollment_form',
- secure=True)
+ data.redirect.program()
+ return data.redirect.urlOf('gsoc_enrollment_form', secure=True)
if key == 'school_name':
- url = data.redirect.program().urlOf('edit_gsoc_profile', secure=True)
+ data.redirect.program()
+ url = data.redirect.urlOf('edit_gsoc_profile', secure=True)
return url + '#form_row_school_name'
if key.isdigit():
project_id = int(key)
diff --git a/app/soc/modules/gsoc/views/document.py b/app/soc/modules/gsoc/views/document.py
index 99be5b7..7fc2568 100644
--- a/app/soc/modules/gsoc/views/document.py
+++ b/app/soc/modules/gsoc/views/document.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for GSoC documents page.
-"""
-
+"""Module containing the views for GSoC documents page."""
from django.conf.urls.defaults import url as django_url
@@ -28,7 +24,7 @@
from soc.views.helper import url_patterns
from soc.views.helper.access_checker import isSet
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.forms import GSoCModelForm
from soc.modules.gsoc.views.helper.url_patterns import url
from soc.modules.gsoc.views.helper import url_patterns as gsoc_url_patterns
@@ -46,7 +42,7 @@
]
-class EditDocumentPage(RequestHandler):
+class EditDocumentPage(GSoCRequestHandler):
"""Encapsulate all the methods required to edit documents.
"""
@@ -93,7 +89,7 @@
self.get()
-class DocumentPage(RequestHandler):
+class DocumentPage(GSoCRequestHandler):
"""Encapsulate all the methods required to show documents.
"""
@@ -127,7 +123,7 @@
}
-class EventsPage(RequestHandler):
+class EventsPage(GSoCRequestHandler):
"""Encapsulates all the methods required to show the events page.
"""
@@ -163,7 +159,7 @@
return 'v2/modules/gsoc/document/_document_list.html'
-class DocumentListPage(RequestHandler):
+class DocumentListPage(GSoCRequestHandler):
"""View for the list documents page.
"""
diff --git a/app/soc/modules/gsoc/views/duplicates.py b/app/soc/modules/gsoc/views/duplicates.py
index 9a2b292..0b95fd4 100644
--- a/app/soc/modules/gsoc/views/duplicates.py
+++ b/app/soc/modules/gsoc/views/duplicates.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for GSoC proposal duplicates.
-"""
-
+"""Module containing the views for GSoC proposal duplicates."""
from google.appengine.api import taskqueue
from google.appengine.ext import db
@@ -29,13 +25,12 @@
from soc.modules.gsoc.logic import duplicates as duplicates_logic
from soc.modules.gsoc.models.proposal_duplicates import GSoCProposalDuplicate
from soc.modules.gsoc.models.profile import GSoCProfile
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper.url_patterns import url
-class DuplicatesPage(RequestHandler):
- """View for the host to see duplicates.
- """
+class DuplicatesPage(GSoCRequestHandler):
+ """View for the host to see duplicates."""
def templatePath(self):
return 'v2/modules/gsoc/duplicates/base.html'
@@ -110,19 +105,19 @@
super(Duplicate, self).__init__(data)
def context(self):
- """Returns the context for the current template.
- """
- r = self.data.redirect
-
+ """Returns the context for the current template."""
context = {'duplicate': self.duplicate}
orgs = db.get(self.duplicate.orgs)
proposals = db.get(self.duplicate.duplicates)
orgs_details = {}
for org in orgs:
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.data.redirect.organization(organization=org)
+
orgs_details[org.key().id_or_name()] = {
'name': org.name,
- 'link': r.organization(org).urlOf('gsoc_org_home')
+ 'link': self.data.redirect.urlOf('gsoc_org_home')
}
q = GSoCProfile.all()
q.filter('org_admin_for', org)
@@ -142,9 +137,9 @@
orgs_details[org.key().id_or_name()]['proposals'].append({
'key': proposal.key().id_or_name(),
'title': proposal.title,
- 'link': r.review(proposal.key().id_or_name(),
- proposal.parent().link_id).urlOf(
- 'review_gsoc_proposal')
+ 'link': self.data.redirect.review(
+ proposal.key().id_or_name(),
+ proposal.parent().link_id).urlOf('review_gsoc_proposal'),
})
context['orgs'] = orgs_details
@@ -152,6 +147,5 @@
return context
def templatePath(self):
- """Returns the path to the template that should be used in render().
- """
+ """Returns the path to the template that should be used in render()."""
return 'v2/modules/gsoc/duplicates/proposal_duplicate.html'
diff --git a/app/soc/modules/gsoc/views/grading_record_details.py b/app/soc/modules/gsoc/views/grading_record_details.py
index 870f717..e9bc0d5 100644
--- a/app/soc/modules/gsoc/views/grading_record_details.py
+++ b/app/soc/modules/gsoc/views/grading_record_details.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for displaying GradingSurveyGroups and records.
-"""
-
+"""Module for displaying GradingSurveyGroups and records."""
import collections
@@ -32,12 +28,12 @@
from soc.modules.gsoc.logic import grading_record
from soc.modules.gsoc.models.grading_record import GSoCGradingRecord
from soc.modules.gsoc.views import forms as gsoc_forms
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper import url_patterns as gsoc_url_patterns
from soc.modules.gsoc.views.helper.url_patterns import url
-class GradingRecordsOverview(RequestHandler):
+class GradingRecordsOverview(GSoCRequestHandler):
"""View to display all GradingRecords for a single group.
"""
@@ -232,7 +228,7 @@
widgets = forms.choiceWidgets(GSoCGradingRecord, ['grade_decision'])
-class GradingRecordDetails(RequestHandler):
+class GradingRecordDetails(GSoCRequestHandler):
"""View to display GradingRecord details.
"""
diff --git a/app/soc/modules/gsoc/views/helper/access_checker.py b/app/soc/modules/gsoc/views/helper/access_checker.py
index 0969ae2..a36685b 100644
--- a/app/soc/modules/gsoc/views/helper/access_checker.py
+++ b/app/soc/modules/gsoc/views/helper/access_checker.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +16,6 @@
for checking access.
"""
-
from google.appengine.ext import db
from django.utils.translation import ugettext
@@ -440,8 +437,10 @@
gsoc_org = q.get()
if gsoc_org:
- edit_url = self.data.redirect.organization(gsoc_org).urlOf(
- 'edit_gsoc_org_profile')
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.data.redirect.organization(organization=gsoc_org)
+
+ edit_url = self.data.redirect.urlOf('edit_gsoc_org_profile')
raise AccessViolation(DEF_ORG_EXISTS % (org_id, edit_url))
diff --git a/app/soc/modules/gsoc/views/homepage.py b/app/soc/modules/gsoc/views/homepage.py
index 10533c3..c6f7eb8 100644
--- a/app/soc/modules/gsoc/views/homepage.py
+++ b/app/soc/modules/gsoc/views/homepage.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for GSoC home page.
-"""
-
+"""Module containing the views for GSoC home page."""
from django.conf.urls.defaults import url as django_url
@@ -26,7 +22,7 @@
from soc.modules.gsoc.logic import organization as org_logic
from soc.modules.gsoc.logic import project as project_logic
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.base_templates import LoggedInMsg
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -73,8 +69,7 @@
class Apply(Template):
- """Apply template.
- """
+ """Apply template."""
def __init__(self, data):
self.data = data
@@ -82,11 +77,12 @@
def context(self):
context = {}
accepted_orgs = None
- r = self.data.redirect.program()
+ redirector = self.data.redirect
+ redirector.program()
if self.data.timeline.orgsAnnounced():
# accepted orgs block
- accepted_orgs = r.urlOf('gsoc_accepted_orgs')
+ accepted_orgs = redirector.urlOf('gsoc_accepted_orgs')
nr_orgs = self.data.program.nr_accepted_orgs
context['nr_accepted_orgs'] = nr_orgs if nr_orgs else ""
context['accepted_orgs_link'] = accepted_orgs
@@ -94,13 +90,13 @@
current_orgs = org_logic.participating(self.data.program)
for org in current_orgs:
participating_orgs.append({
- 'link': r.orgHomepage(org.link_id).url(),
+ 'link': redirector.orgHomepage(org.link_id).url(),
'logo': org.logo_url,
'name': org.short_name,
})
context['participating_orgs'] = participating_orgs
- context['org_signup'] = self.data.timeline.orgSignup()
+ context['org_signup'] = self.data.timeline.orgSignup()
context['student_signup'] = self.data.timeline.studentSignup()
context['mentor_signup'] = self.data.timeline.mentorSignup()
@@ -112,23 +108,25 @@
# signup block
if signup and not self.data.gae_user:
- context['login_link'] = r.login().url()
+ context['login_link'] = redirector.login().url()
if signup and not self.data.profile:
if self.data.timeline.orgSignup():
- r.createProfile('org_admin')
+ redirector.createProfile('org_admin')
elif self.data.timeline.studentSignup():
- r.createProfile('mentor')
- context['mentor_profile_link'] = r.urlOf('create_gsoc_profile',
- secure=True)
- r.createProfile('student')
+ redirector.createProfile('mentor')
+ context['mentor_profile_link'] = redirector.urlOf(
+ 'create_gsoc_profile', secure=True)
+ redirector.createProfile('student')
elif self.data.timeline.mentorSignup():
- r.createProfile('mentor')
+ redirector.createProfile('mentor')
- context['profile_link'] = r.urlOf('create_gsoc_profile', secure=True)
+ context['profile_link'] = redirector.urlOf(
+ 'create_gsoc_profile', secure=True)
if self.data.timeline.orgSignup() and self.data.profile:
- context['org_apply_link'] = r.orgAppTake().urlOf('gsoc_take_org_app')
- context['dashboard_link'] = r.dashboard().url()
+ context['org_apply_link'] = redirector.orgAppTake().urlOf(
+ 'gsoc_take_org_app')
+ context['dashboard_link'] = redirector.dashboard().url()
if ((self.data.timeline.studentSignup() or
self.data.timeline.mentorSignup()) and self.data.profile):
@@ -194,7 +192,7 @@
return "v2/modules/gsoc/_connect_with_us.html"
-class Homepage(RequestHandler):
+class Homepage(GSoCRequestHandler):
"""Encapsulate all the methods required to generate GSoC Home page.
"""
diff --git a/app/soc/modules/gsoc/views/invite.py b/app/soc/modules/gsoc/views/invite.py
index 39ce1ab..6d3267d 100644
--- a/app/soc/modules/gsoc/views/invite.py
+++ b/app/soc/modules/gsoc/views/invite.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the view for GSoC invitation page.
-"""
-
+"""Module containing the view for GSoC invitation page."""
from google.appengine.ext import db
from google.appengine.api import users
@@ -34,7 +30,7 @@
from soc.modules.gsoc.models.profile import GSoCProfile
from soc.modules.gsoc.models.request import GSoCRequest
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper.url_patterns import url
from soc.modules.gsoc.views import forms as gsoc_forms
@@ -69,7 +65,7 @@
field.help_text = ugettext(
'The link_id or email address of the invitee, '
' separate multiple values with a comma')
-
+
def clean_link_id(self):
"""Accepts link_id of users which may be invited.
"""
@@ -155,7 +151,7 @@
raise djangoforms.ValidationError('That user already has this role.')
-class InvitePage(RequestHandler):
+class InvitePage(GSoCRequestHandler):
"""Encapsulate all the methods required to generate Invite page.
"""
@@ -201,7 +197,7 @@
assert isSet(self.data.organization)
invite_form = InviteForm(self.data, self.data.POST)
-
+
if not invite_form.is_valid():
return None
@@ -239,7 +235,7 @@
self.get()
-class ShowInvite(RequestHandler):
+class ShowInvite(GSoCRequestHandler):
"""Encapsulate all the methods required to generate Show Invite page.
"""
diff --git a/app/soc/modules/gsoc/views/mentor_evaluation.py b/app/soc/modules/gsoc/views/mentor_evaluation.py
index 7c39459..b019e93 100644
--- a/app/soc/modules/gsoc/views/mentor_evaluation.py
+++ b/app/soc/modules/gsoc/views/mentor_evaluation.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GSoC project evaluations.
-"""
-
+"""Module for the GSoC project evaluations."""
from django.utils.translation import ugettext
@@ -32,7 +28,7 @@
from soc.modules.gsoc.models.grading_project_survey_record import \
GSoCGradingProjectSurveyRecord
from soc.modules.gsoc.views import forms as gsoc_forms
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.base_templates import LoggedInMsg
from soc.modules.gsoc.views.helper import url_patterns
@@ -89,7 +85,7 @@
return True if grade == 'True' else False
-class GSoCMentorEvaluationEditPage(RequestHandler):
+class GSoCMentorEvaluationEditPage(GSoCRequestHandler):
"""View for creating/editing organization evaluation form.
"""
@@ -168,7 +164,7 @@
self.get()
-class GSoCMentorEvaluationTakePage(RequestHandler):
+class GSoCMentorEvaluationTakePage(GSoCRequestHandler):
"""View for the organization to submit student evaluation.
"""
@@ -255,7 +251,7 @@
self.get()
-class GSoCMentorEvaluationPreviewPage(RequestHandler):
+class GSoCMentorEvaluationPreviewPage(GSoCRequestHandler):
"""View for the host preview mentor evaluation.
"""
@@ -289,7 +285,7 @@
return context
-class GSoCMentorEvaluationRecordsList(RequestHandler):
+class GSoCMentorEvaluationRecordsList(GSoCRequestHandler):
"""View for listing all records of a GSoCGradingProjectSurveyRecord.
"""
@@ -358,7 +354,7 @@
survey_name = 'Mentor Evaluation'
-class GSoCMentorEvaluationShowPage(RequestHandler):
+class GSoCMentorEvaluationShowPage(GSoCRequestHandler):
"""View to display the readonly page for mentor evaluation.
"""
diff --git a/app/soc/modules/gsoc/views/org_app.py b/app/soc/modules/gsoc/views/org_app.py
index 2508f6e..463ae05 100644
--- a/app/soc/modules/gsoc/views/org_app.py
+++ b/app/soc/modules/gsoc/views/org_app.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for GSoC Organization Application.
-"""
-
+"""Module containing the views for GSoC Organization Application."""
import logging
@@ -32,7 +28,7 @@
from soc.logic import org_app as org_app_logic
from soc.modules.gsoc.views import forms as gsoc_forms
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper import url_names
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -71,7 +67,7 @@
return 'v2/modules/gsoc/_form.html'
-class GSoCOrgAppEditPage(RequestHandler):
+class GSoCOrgAppEditPage(GSoCRequestHandler):
"""View for creating/editing organization application.
"""
@@ -100,9 +96,12 @@
else:
page_name = 'Create new organization application'
+ # TODO(nathaniel): is this really necessary?
+ self.redirect.program()
+
context = {
'page_name': page_name,
- 'post_url': self.redirect.program().urlOf('gsoc_edit_org_app'),
+ 'post_url': self.redirect.urlOf('gsoc_edit_org_app'),
'forms': [form],
'error': bool(form.errors),
}
@@ -139,13 +138,14 @@
def post(self):
org_app = self.orgAppFromForm()
if org_app:
- r = self.redirect.program()
- r.to('gsoc_edit_org_app', validated=True)
+ # TODO(nathaniel): is this necessary?
+ self.redirect.program()
+ self.redirect.to('gsoc_edit_org_app', validated=True)
else:
self.get()
-class GSoCOrgAppPreviewPage(RequestHandler):
+class GSoCOrgAppPreviewPage(GSoCRequestHandler):
"""View for organizations to submit their application.
"""
@@ -177,7 +177,7 @@
return context
-class GSoCOrgAppTakePage(RequestHandler):
+class GSoCOrgAppTakePage(GSoCRequestHandler):
"""View for organizations to submit their application.
"""
@@ -265,12 +265,12 @@
self.get()
-class GSoCOrgAppRecordsList(org_app.OrgAppRecordsList, RequestHandler):
+class GSoCOrgAppRecordsList(org_app.OrgAppRecordsList, GSoCRequestHandler):
"""View for listing all records of a GSoC Organization application.
"""
def __init__(self, *args, **kwargs):
- RequestHandler.__init__(self, *args, **kwargs)
+ GSoCRequestHandler.__init__(self, *args, **kwargs)
org_app.OrgAppRecordsList.__init__(self, 'gsoc_show_org_app')
def djangoURLPatterns(self):
@@ -332,7 +332,7 @@
template_path = 'v2/modules/gsoc/org_app/readonly_template.html'
-class GSoCOrgAppShowPage(RequestHandler):
+class GSoCOrgAppShowPage(GSoCRequestHandler):
"""View to display the readonly page for organization application.
"""
@@ -363,9 +363,9 @@
if record:
context['record'] = OrgAppReadOnlyTemplate(record)
-
+
# admin info should be available only to the hosts
- if self.data.is_host:
+ if self.data.is_host:
context['main_admin_url'] = self.data.redirect.profile(
record.main_admin.link_id).urlOf(url_names.GSOC_PROFILE_SHOW)
context['backup_admin_url'] = self.data.redirect.profile(
@@ -376,7 +376,8 @@
context['update_link'] = self.data.redirect.id().urlOf(
'gsoc_retake_org_app')
else:
- context['create_link'] = self.data.redirect.program().urlOf(
- 'gsoc_take_org_app')
+ # TODO(nathaniel): is this program() necessary?
+ self.data.redirect.program()
+ context['create_link'] = self.data.redirect.urlOf('gsoc_take_org_app')
return context
diff --git a/app/soc/modules/gsoc/views/org_home.py b/app/soc/modules/gsoc/views/org_home.py
index 7fc81f1..23d69d8 100644
--- a/app/soc/modules/gsoc/views/org_home.py
+++ b/app/soc/modules/gsoc/views/org_home.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for GSoC Homepage.
-"""
-
+"""Module containing the views for GSoC Homepage."""
from django.conf.urls.defaults import url as django_url
from django.utils.translation import ugettext
@@ -36,7 +32,7 @@
from soc.modules.gsoc.models.organization import GSoCOrganization
from soc.modules.gsoc.models.profile import GSoCProfile
from soc.modules.gsoc.models.project import GSoCProject
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper import url_names
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -51,7 +47,6 @@
def context(self):
organization = self.data.organization
- r = self.data.redirect
context = {
'request_data': self.data,
@@ -67,21 +62,24 @@
if self.data.timeline.studentSignup():
context['student_apply_block'] = True
- profile_link = r.createProfile('student').urlOf('create_gsoc_profile',
- secure=True)
+ profile_link = self.data.redirect.createProfile('student').urlOf(
+ 'create_gsoc_profile', secure=True)
context['student_profile_link'] = profile_link + suffix
else:
context['mentor_apply_block'] = True
- profile_link = r.createProfile('mentor').urlOf('create_gsoc_profile',
- secure=True)
+ profile_link = self.data.redirect.createProfile('mentor').urlOf(
+ 'create_gsoc_profile', secure=True)
context['mentor_profile_link'] = profile_link + suffix
return context
if self.data.student_info:
if self.data.timeline.studentSignup():
context['student_apply_block'] = True
- submit_proposal_link = r.organization().urlOf('submit_gsoc_proposal')
+ # TODO(nathaniel): make this .organization() call unnecessary.
+ self.data.redirect.organization()
+
+ submit_proposal_link = self.data.redirect.urlOf('submit_gsoc_proposal')
context['submit_proposal_link'] = submit_proposal_link
return context
@@ -110,7 +108,10 @@
context['invited_role'] = 'an administrator'
return context
- request_mentor_link = r.organization().urlOf('gsoc_request')
+ # TODO(nathaniel): make this .organization() call unnecessary.
+ self.data.redirect.organization()
+
+ request_mentor_link = self.data.redirect.urlOf('gsoc_request')
context['mentor_request_link'] = request_mentor_link
return context
@@ -199,7 +200,7 @@
return "v2/modules/gsoc/org_home/_project_list.html"
-class GSoCBanOrgPost(BanOrgPost, RequestHandler):
+class GSoCBanOrgPost(BanOrgPost, GSoCRequestHandler):
"""Handles banning/unbanning of GSoC organizations.
"""
@@ -219,7 +220,7 @@
class GSoCHostActions(HostActions):
"""Template to render the left side host actions.
"""
-
+
DEF_BAN_ORGANIZATION_HELP = ugettext(
'When an organization is banned, it is not active in the program')
@@ -230,7 +231,7 @@
return self.DEF_BAN_ORGANIZATION_HELP
-class OrgHome(RequestHandler):
+class OrgHome(GSoCRequestHandler):
"""View methods for Organization Home page.
"""
@@ -290,15 +291,21 @@
context['ideas_link_trimmed'] = url_helper.trim_url_to(ideas, 50)
if self.data.orgAdminFor(organization):
- r = self.redirect
- r.organization(organization)
- context['edit_link'] = r.urlOf('edit_gsoc_org_profile')
- context['invite_admin_link'] = r.invite('org_admin').urlOf('gsoc_invite')
- context['invite_mentor_link'] = r.invite('mentor').urlOf('gsoc_invite')
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.redirect.organization(organization=organization)
+
+ context['edit_link'] = self.redirect.urlOf('edit_gsoc_org_profile')
+ context['invite_admin_link'] = self.redirect.invite('org_admin').urlOf(
+ 'gsoc_invite')
+ context['invite_mentor_link'] = self.redirect.invite('mentor').urlOf(
+ 'gsoc_invite')
if (self.data.program.allocations_visible and
self.data.timeline.beforeStudentsAnnounced()):
- context['slot_transfer_link'] = r.organization(organization).urlOf(
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.redirect.organization(organization=organization)
+
+ context['slot_transfer_link'] = self.redirect.urlOf(
'gsoc_slot_transfer')
if self.data.timeline.studentsAnnounced():
@@ -312,13 +319,12 @@
return context
def getCurrentTimeline(self, timeline, org_app):
- """Return where we are currently on the timeline.
- """
+ """Return where we are currently on the timeline."""
if timeline_helper.isActivePeriod(org_app, 'survey'):
return 'org_signup_period'
elif timeline_helper.isActivePeriod(timeline, 'student_signup'):
return 'student_signup_period'
elif timeline_helper.isActivePeriod(timeline, 'program'):
return 'program_period'
-
- return 'offseason'
+ else:
+ return 'offseason'
diff --git a/app/soc/modules/gsoc/views/org_profile.py b/app/soc/modules/gsoc/views/org_profile.py
index d84822e..33584ef 100644
--- a/app/soc/modules/gsoc/views/org_profile.py
+++ b/app/soc/modules/gsoc/views/org_profile.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GSoC organization profile page.
-"""
-
+"""Module for the GSoC organization profile page."""
from soc.views import forms
@@ -27,7 +23,7 @@
from soc.views import org_profile
from soc.modules.gsoc.models.organization import GSoCOrganization
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.base_templates import LoggedInMsg
from soc.modules.gsoc.views import forms as gsoc_forms
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -143,7 +139,7 @@
['contact_country', 'shipping_country'])
-class OrgProfilePage(RequestHandler):
+class OrgProfilePage(GSoCRequestHandler):
"""View for the Organization Profile page.
"""
@@ -191,18 +187,21 @@
}
if self.data.organization:
- r = self.data.redirect.organization()
- context['org_home_page_link'] = r.urlOf('gsoc_org_home')
+ # TODO(nathaniel): make this .organization() unnecessary.
+ self.data.redirect.organization()
+
+ context['org_home_page_link'] = self.data.redirect.urlOf('gsoc_org_home')
if (self.data.program.allocations_visible and
self.data.timeline.beforeStudentsAnnounced()):
- context['slot_transfer_page_link'] = r.urlOf('gsoc_slot_transfer')
+ context['slot_transfer_page_link'] = self.data.redirect.urlOf(
+ 'gsoc_slot_transfer')
return context
def post(self):
org_profile = self.createOrgProfileFromForm()
if org_profile:
- self.redirect.organization(org_profile)
+ self.redirect.organization(organization=org_profile)
self.redirect.to('edit_gsoc_org_profile', validated=True)
else:
self.get()
diff --git a/app/soc/modules/gsoc/views/profile.py b/app/soc/modules/gsoc/views/profile.py
index a13d67b..9f33d44 100644
--- a/app/soc/modules/gsoc/views/profile.py
+++ b/app/soc/modules/gsoc/views/profile.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GSoC profile page.
-"""
-
+"""Module for the GSoC profile page."""
from django.forms import fields
from django.core.urlresolvers import reverse
@@ -35,7 +31,7 @@
from soc.modules.gsoc.models.profile import GSoCProfile
from soc.modules.gsoc.models.profile import GSoCStudentInfo
from soc.modules.gsoc.views import forms as gsoc_forms
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.base_templates import LoggedInMsg
@@ -195,9 +191,8 @@
clean_school_home_page = cleaning.clean_url('school_home_page')
-class GSoCProfilePage(profile.ProfilePage, RequestHandler):
- """View for the GSoC participant profile.
- """
+class GSoCProfilePage(profile.ProfilePage, GSoCRequestHandler):
+ """View for the GSoC participant profile."""
def checkAccess(self):
self.check.isLoggedIn()
diff --git a/app/soc/modules/gsoc/views/profile_show.py b/app/soc/modules/gsoc/views/profile_show.py
index aab7629..73522c7 100644
--- a/app/soc/modules/gsoc/views/profile_show.py
+++ b/app/soc/modules/gsoc/views/profile_show.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for displaying the GSoC profile read only page.
-"""
-
+"""Module for displaying the GSoC profile read only page."""
from django.utils.translation import ugettext
@@ -29,7 +25,7 @@
from soc.modules.gsoc.models.profile import GSoCProfile
from soc.modules.gsoc.models.project import GSoCProject
from soc.modules.gsoc.views import readonly_template
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.base_templates import LoggedInMsg
from soc.modules.gsoc.views.helper import url_names
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -54,7 +50,7 @@
class GSoCHostActions(profile_show.HostActions):
"""Template to render the left side host actions.
"""
-
+
DEF_BAN_PROFILE_HELP = ugettext(
'When a profile is banned, the user cannot participate in the program')
@@ -65,7 +61,7 @@
return self.DEF_BAN_PROFILE_HELP
-class GSoCBanProfilePost(profile_show.BanProfilePost, RequestHandler):
+class GSoCBanProfilePost(profile_show.BanProfilePost, GSoCRequestHandler):
"""Handles banning/unbanning of GSoC profiles.
"""
@@ -82,7 +78,7 @@
return GSoCProfile
-class GSoCProfileShowPage(profile_show.ProfileShowPage, RequestHandler):
+class GSoCProfileShowPage(profile_show.ProfileShowPage, GSoCRequestHandler):
"""View to display the read-only profile page.
"""
@@ -99,7 +95,7 @@
return GSoCProfileReadOnlyTemplate(profile)
-class GSoCProfileAdminPage(RequestHandler):
+class GSoCProfileAdminPage(GSoCRequestHandler):
"""View to display the readonly profile page.
"""
diff --git a/app/soc/modules/gsoc/views/program.py b/app/soc/modules/gsoc/views/program.py
index 83b72a8..520b599 100644
--- a/app/soc/modules/gsoc/views/program.py
+++ b/app/soc/modules/gsoc/views/program.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,37 +12,32 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the program settings pages.
-"""
+"""Module for the program settings pages."""
+from soc.models import document
-from soc.models.document import Document
+from soc.views import program as soc_program_view
+from soc.views.helper import url_patterns as soc_url_patterns
-from soc.views import program as program_view
-from soc.views.helper import url_patterns
-
-from soc.modules.gsoc.models.program import GSoCProgram
-from soc.modules.gsoc.models.program import GSoCProgramMessages
-from soc.modules.gsoc.models.timeline import GSoCTimeline
-from soc.modules.gsoc.views.base import RequestHandler
-from soc.modules.gsoc.views.forms import GSoCModelForm
+from soc.modules.gsoc.models import program
+from soc.modules.gsoc.models import timeline
+from soc.modules.gsoc.views import base
+from soc.modules.gsoc.views import forms
from soc.modules.gsoc.views.helper import url_names
-from soc.modules.gsoc.views.helper.url_patterns import url
+from soc.modules.gsoc.views.helper import url_patterns
-class TimelineForm(GSoCModelForm):
- """Django form to edit timeline settings.
- """
+class TimelineForm(forms.GSoCModelForm):
+ """Django form to edit timeline settings."""
class Meta:
css_prefix = 'timeline_form'
- model = GSoCTimeline
+ model = timeline.GSoCTimeline
exclude = ['link_id', 'scope', 'scope_path']
-class ProgramForm(GSoCModelForm):
- """Django form for the program settings.
- """
+class ProgramForm(forms.GSoCModelForm):
+ """Django form for the program settings."""
def __init__(self, request_data, *args, **kwargs):
self.request_data = request_data
@@ -52,38 +45,36 @@
class Meta:
css_prefix = 'program_form'
- model = GSoCProgram
+ model = program.GSoCProgram
exclude = ['link_id', 'scope', 'scope_path', 'timeline',
'home', 'slots_allocation', 'student_max_age',
'min_slots']
-class GSoCProgramMessagesForm(GSoCModelForm):
- """Django form for the program settings.
- """
+class GSoCProgramMessagesForm(forms.GSoCModelForm):
+ """Django form for the program settings."""
def __init__(self, request_data, *args, **kwargs):
self.request_data = request_data
- super(GSoCProgramMessagesForm, self).__init__(*args, **kwargs)
+ super(program.GSoCProgramMessagesForm, self).__init__(*args, **kwargs)
class Meta:
css_prefix = 'program_messages_form'
- model = GSoCProgramMessages
+ model = program.GSoCProgramMessages
-class ProgramPage(RequestHandler):
- """View for the program profile.
- """
+class ProgramPage(base.GSoCRequestHandler):
+ """View for the program profile."""
def djangoURLPatterns(self):
return [
- url(r'program/%s$' % url_patterns.PROGRAM, self,
+ url_patterns.url(r'program/%s$' % soc_url_patterns.PROGRAM, self,
name='edit_gsoc_program'),
- url(r'program/edit/%s$' % url_patterns.PROGRAM, self),
+ url_patterns.url(r'program/edit/%s$' % soc_url_patterns.PROGRAM, self),
]
def jsonContext(self):
- q = Document.all()
+ q = document.Document.all()
q.filter('prefix', 'gsoc_program')
q.filter('scope', self.data.program.key())
@@ -115,19 +106,15 @@
program_form = ProgramForm(self.data, self.data.POST,
instance=self.data.program)
- if not program_form.is_valid():
+ if program_form.is_valid():
+ program_form.save()
+ return True
+ else:
return False
- program_form.save()
- return True
-
def post(self):
- """Handler for HTTP POST request.
- """
- if self.data.GET.get('cbox'):
- cbox = True
- else:
- cbox = False
+ """Handler for HTTP POST request."""
+ cbox = bool(self.data.GET.get('cbox'))
if self.validate():
self.redirect.program()
@@ -136,15 +123,14 @@
self.get()
-class TimelinePage(RequestHandler):
- """View for the participant profile.
- """
+class TimelinePage(base.GSoCRequestHandler):
+ """View for the participant profile."""
def djangoURLPatterns(self):
return [
- url(r'timeline/%s$' % url_patterns.PROGRAM, self,
+ url_patterns.url(r'timeline/%s$' % soc_url_patterns.PROGRAM, self,
name='edit_gsoc_timeline'),
- url(r'timeline/edit/%s$' % url_patterns.PROGRAM, self),
+ url_patterns.url(r'timeline/edit/%s$' % soc_url_patterns.PROGRAM, self),
]
def checkAccess(self):
@@ -166,19 +152,15 @@
timeline_form = TimelineForm(self.data.POST,
instance=self.data.program_timeline)
- if not timeline_form.is_valid():
+ if timeline_form.is_valid():
+ timeline_form.save()
+ return True
+ else:
return False
- timeline_form.save()
- return True
-
def post(self):
- """Handler for HTTP POST request.
- """
- if self.data.GET.get('cbox'):
- cbox = True
- else:
- cbox = False
+ """Handler for HTTP POST request."""
+ cbox = bool(self.data.GET.get('cbox'))
if self.validate():
self.redirect.program()
@@ -188,13 +170,13 @@
class GSoCProgramMessagesPage(
- program_view.ProgramMessagesPage, RequestHandler):
- """View for the content of GSoC program specific messages to be sent.
- """
+ soc_program_view.ProgramMessagesPage, base.GSoCRequestHandler):
+ """View for the content of GSoC program specific messages to be sent."""
def djangoURLPatterns(self):
return [
- url(r'program/messages/edit/%s$' % url_patterns.PROGRAM, self,
+ url_patterns.url(
+ r'program/messages/edit/%s$' % soc_url_patterns.PROGRAM, self,
name=self._getUrlName()),
]
@@ -202,11 +184,11 @@
return 'v2/modules/gsoc/program/messages.html'
def _getForm(self, entity):
- return GSoCProgramMessagesForm(self.data, self.data.POST or None,
+ return program.GSoCProgramMessagesForm(self.data, self.data.POST or None,
instance=entity)
def _getModel(self):
- return GSoCProgramMessages
+ return program.GSoCProgramMessages
def _getUrlName(self):
return url_names.GSOC_EDIT_PROGRAM_MESSAGES
diff --git a/app/soc/modules/gsoc/views/project_details.py b/app/soc/modules/gsoc/views/project_details.py
index 14af15c..fc0def6 100644
--- a/app/soc/modules/gsoc/views/project_details.py
+++ b/app/soc/modules/gsoc/views/project_details.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the view for GSoC project details page.
-"""
+"""Module containing the view for GSoC project details page."""
+import httplib
from google.appengine.ext import blobstore
from google.appengine.ext import db
@@ -37,7 +35,7 @@
from soc.modules.gsoc.models.code_sample import GSoCCodeSample
from soc.modules.gsoc.views import assign_mentor
from soc.modules.gsoc.views import forms as gsoc_forms
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper import url_names
from soc.modules.gsoc.views.helper import url_patterns
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -169,7 +167,7 @@
return 'v2/modules/gsoc/project_details/_upload_code_samples.html'
-class ProjectDetailsUpdate(RequestHandler):
+class ProjectDetailsUpdate(GSoCRequestHandler):
"""Encapsulate the methods required to generate Project Details update form.
"""
@@ -235,7 +233,7 @@
self.get()
-class CodeSampleUploadFilePost(RequestHandler):
+class CodeSampleUploadFilePost(GSoCRequestHandler):
"""Handler for POST requests to upload files with code samples.
"""
@@ -291,7 +289,7 @@
self.redirect.to('gsoc_project_details')
-class CodeSampleDownloadFileGet(RequestHandler):
+class CodeSampleDownloadFileGet(GSoCRequestHandler):
"""Handler for POST requests to download files with code samples.
"""
@@ -325,7 +323,7 @@
raise BadRequest('id argument in GET data is not a number')
-class CodeSampleDeleteFilePost(RequestHandler):
+class CodeSampleDeleteFilePost(GSoCRequestHandler):
"""Handler for POST requests to delete code sample files.
"""
@@ -423,7 +421,7 @@
return "v2/modules/gsoc/project_details/_user_action.html"
-class ProjectDetails(RequestHandler):
+class ProjectDetails(GSoCRequestHandler):
"""Encapsulate all the methods required to generate GSoC project
details page.
"""
@@ -441,21 +439,18 @@
]
def checkAccess(self):
- """Access checks for GSoC project details page.
- """
+ """Access checks for GSoC project details page."""
self.mutator.projectFromKwargs()
def context(self):
- """Handler to for GSoC project details page HTTP get request.
- """
- project = self.data.project
-
- r = self.redirect
+ """Handler to for GSoC project details page HTTP get request."""
+ # TODO(nathaniel): make this .organization call unnecessary?
+ self.redirect.organization(organization=self.data.project.org)
context = {
'page_name': 'Project details',
- 'project': project,
- 'org_home_link': r.organization(project.org).urlOf('gsoc_org_home'),
+ 'project': self.data.project,
+ 'org_home_link': self.redirect.urlOf('gsoc_org_home'),
}
if self.data.orgAdminFor(self.data.project.org):
@@ -464,7 +459,8 @@
user_is_owner = self.data.user and \
(self.data.user.key() == self.data.project_owner.parent_key())
if user_is_owner:
- context['update_link'] = r.project().urlOf(url_names.GSOC_PROJECT_UPDATE)
+ context['update_link'] = self.redirect.project().urlOf(
+ url_names.GSOC_PROJECT_UPDATE)
if len(self.data.project.passed_evaluations) >= \
project_logic.NUMBER_OF_EVALUATIONS:
@@ -473,7 +469,7 @@
return context
-class AssignMentors(RequestHandler):
+class AssignMentors(GSoCRequestHandler):
"""View which handles assigning mentor to a project.
"""
@@ -542,10 +538,10 @@
def get(self):
"""Special Handler for HTTP GET request since this view only handles POST.
"""
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
-class FeaturedProject(RequestHandler):
+class FeaturedProject(GSoCRequestHandler):
"""View which handles making the project featured by toggle button.
"""
@@ -597,4 +593,4 @@
def get(self):
"""Special Handler for HTTP GET request since this view only handles POST.
"""
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
diff --git a/app/soc/modules/gsoc/views/projects_list.py b/app/soc/modules/gsoc/views/projects_list.py
index d87614b..6c43769 100644
--- a/app/soc/modules/gsoc/views/projects_list.py
+++ b/app/soc/modules/gsoc/views/projects_list.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,7 +13,7 @@
# limitations under the License.
"""Module containing the views for listing all the projects accepted
-into a GSoC program, excluding those which have been withdrawn
+into a GSoC program, excluding those which have been withdrawn
or failed one of the evaluations.
"""
@@ -28,7 +26,7 @@
from soc.modules.gsoc.logic import project as project_logic
from soc.modules.gsoc.models.project import GSoCProject
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -38,7 +36,7 @@
def __init__(self, request, data, query, idx=0):
"""Initializes a new object.
-
+
Args:
request: request object
data: RequestData object associated with the request
@@ -117,7 +115,7 @@
return "v2/modules/gsoc/projects_list/_project_list.html"
-class ListProjects(RequestHandler):
+class ListProjects(GSoCRequestHandler):
"""View methods for listing all the projects accepted into a program.
"""
diff --git a/app/soc/modules/gsoc/views/proposal.py b/app/soc/modules/gsoc/views/proposal.py
index 7231d4f..6b29a7d 100644
--- a/app/soc/modules/gsoc/views/proposal.py
+++ b/app/soc/modules/gsoc/views/proposal.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GSoC proposal page.
-"""
-
+"""Module for the GSoC proposal page."""
from google.appengine.ext import db
@@ -30,7 +26,7 @@
from soc.modules.gsoc.logic.helper import notifications
from soc.modules.gsoc.models.proposal import GSoCProposal
from soc.modules.gsoc.models.profile import GSoCProfile
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.forms import GSoCModelForm
from soc.modules.gsoc.views.helper import url_patterns as gsoc_url_patterns
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -49,7 +45,7 @@
clean_content = cleaning.clean_html_content('content')
-class ProposalPage(RequestHandler):
+class ProposalPage(GSoCRequestHandler):
"""View for the submit proposal.
"""
@@ -139,7 +135,7 @@
self.get()
-class UpdateProposal(RequestHandler):
+class UpdateProposal(GSoCRequestHandler):
"""View for the update proposal page.
"""
diff --git a/app/soc/modules/gsoc/views/proposal_review.py b/app/soc/modules/gsoc/views/proposal_review.py
index 767df31..4b84568 100644
--- a/app/soc/modules/gsoc/views/proposal_review.py
+++ b/app/soc/modules/gsoc/views/proposal_review.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GSoC proposal page.
-"""
+"""Module for the GSoC proposal page."""
+import httplib
from google.appengine.ext import db
@@ -41,7 +39,7 @@
from soc.modules.gsoc.models.profile import GSoCProfile
from soc.modules.gsoc.models.score import GSoCScore
from soc.modules.gsoc.views import assign_mentor
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.forms import GSoCModelForm
from soc.modules.gsoc.views.helper import url_patterns
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -95,10 +93,7 @@
super(Duplicate, self).__init__(data)
def context(self):
- """The context for this template used in render().
- """
- r = self.data.redirect
-
+ """The context for this template used in render()."""
orgs = []
for org in db.get(self.duplicate.orgs):
q = GSoCProfile.all()
@@ -106,8 +101,11 @@
q.filter('status', 'active')
admins = q.fetch(1000)
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.data.redirect.organization(organization=org)
+
data = {'name': org.name,
- 'link': r.organization(org).urlOf('gsoc_org_home'),
+ 'link': self.data.redirect.urlOf('gsoc_org_home'),
'admins': admins}
orgs.append(data)
@@ -301,7 +299,7 @@
return "v2/modules/gsoc/proposal/_user_action.html"
-class ReviewProposal(RequestHandler):
+class ReviewProposal(GSoCRequestHandler):
"""View for the Propsal Review page.
"""
@@ -492,7 +490,7 @@
return context
-class PostComment(RequestHandler):
+class PostComment(GSoCRequestHandler):
"""View which handles publishing comments.
"""
@@ -585,10 +583,10 @@
def get(self):
"""Special Handler for HTTP GET request since this view only handles POST.
"""
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
-class PostScore(RequestHandler):
+class PostScore(GSoCRequestHandler):
"""View which handles posting scores.
"""
@@ -674,10 +672,10 @@
def get(self):
"""Special Handler for HTTP GET request since this view only handles POST.
"""
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
-class WishToMentor(RequestHandler):
+class WishToMentor(GSoCRequestHandler):
"""View handling wishing to mentor requests.
"""
@@ -738,10 +736,10 @@
def get(self):
"""Special Handler for HTTP GET request since this view only handles POST.
"""
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
-class AssignMentor(RequestHandler):
+class AssignMentor(GSoCRequestHandler):
"""View which handles assigning mentor to a proposal.
"""
@@ -826,10 +824,10 @@
def get(self):
"""Special Handler for HTTP GET request since this view only handles POST.
"""
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
-class IgnoreProposal(RequestHandler):
+class IgnoreProposal(GSoCRequestHandler):
"""View which allows org admins to ignore a proposal.
"""
@@ -884,10 +882,10 @@
def get(self):
"""Special Handler for HTTP GET request since this view only handles POST.
"""
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
-class ProposalModificationPostDeadline(RequestHandler):
+class ProposalModificationPostDeadline(GSoCRequestHandler):
"""View allowing mentors to allow students to modify the proposal.
"""
@@ -940,10 +938,10 @@
def get(self):
"""Special Handler for HTTP GET request since this view only handles POST.
"""
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
-class AcceptProposal(RequestHandler):
+class AcceptProposal(GSoCRequestHandler):
"""View allowing org admins to directly accept the proposal.
"""
@@ -995,10 +993,10 @@
def get(self):
"""Special Handler for HTTP GET request since this view only handles POST.
"""
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
-class ProposalPubliclyVisible(RequestHandler):
+class ProposalPubliclyVisible(GSoCRequestHandler):
"""View allowing the proposer to make the proposal publicly visible.
"""
@@ -1049,10 +1047,10 @@
def get(self):
"""Special Handler for HTTP GET request since this view only handles POST.
"""
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
-class WithdrawProposal(RequestHandler):
+class WithdrawProposal(GSoCRequestHandler):
"""View allowing the proposer to withdraw the proposal.
"""
@@ -1104,4 +1102,4 @@
def get(self):
"""Special Handler for HTTP GET request since this view only handles POST.
"""
- self.error(405)
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
diff --git a/app/soc/modules/gsoc/views/request.py b/app/soc/modules/gsoc/views/request.py
index c4f8390..4134f51 100644
--- a/app/soc/modules/gsoc/views/request.py
+++ b/app/soc/modules/gsoc/views/request.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the view for GSoC request page.
-"""
-
+"""Module containing the view for GSoC request page."""
from google.appengine.ext import db
@@ -33,7 +29,7 @@
from soc.modules.gsoc.models.profile import GSoCProfile
from soc.modules.gsoc.models.request import GSoCRequest
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.base_templates import LoggedInMsg
from soc.modules.gsoc.views.forms import GSoCModelForm
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -74,7 +70,7 @@
fields = ['message']
-class RequestPage(RequestHandler):
+class RequestPage(GSoCRequestHandler):
"""Encapsulate all the methods required to generate Request page.
"""
def templatePath(self):
@@ -169,7 +165,7 @@
return db.run_in_transaction(create_request_txn)
-class ShowRequest(RequestHandler):
+class ShowRequest(GSoCRequestHandler):
"""Encapsulate all the methods required to generate Show Request page.
"""
# maps actions with button names
diff --git a/app/soc/modules/gsoc/views/search.py b/app/soc/modules/gsoc/views/search.py
index 30bfa60..3d78f2d 100644
--- a/app/soc/modules/gsoc/views/search.py
+++ b/app/soc/modules/gsoc/views/search.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,21 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GSoC search page.
-"""
-
+"""Module for the GSoC search page."""
import os
from soc.views.helper import url_patterns
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views import base
from soc.modules.gsoc.views.helper.url_patterns import url
-class SearchGsocPage(RequestHandler):
- """View for the search gsoc page.
- """
+class SearchGsocPage(base.GSoCRequestHandler):
+ """View for the search gsoc page."""
def djangoURLPatterns(self):
return [
diff --git a/app/soc/modules/gsoc/views/slot_transfer.py b/app/soc/modules/gsoc/views/slot_transfer.py
index 0d5a7b5..a18ce6b 100644
--- a/app/soc/modules/gsoc/views/slot_transfer.py
+++ b/app/soc/modules/gsoc/views/slot_transfer.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GSoC slot transfer page.
-"""
-
+"""Module for the GSoC slot transfer page."""
from google.appengine.ext import db
@@ -31,7 +27,7 @@
from soc.modules.gsoc.logic.helper import notifications
from soc.modules.gsoc.models.slot_transfer import GSoCSlotTransfer
from soc.modules.gsoc.views import readonly_template
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views import forms
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -72,7 +68,7 @@
exclude = ['program']
-class SlotTransferPage(RequestHandler):
+class SlotTransferPage(GSoCRequestHandler):
"""View for transferring the slots.
"""
@@ -92,8 +88,10 @@
self.mutator.slotTransferEntities()
if not self.data.slot_transfer_entities:
if 'new' not in self.data.kwargs:
- r = self.data.redirect
- new_url = r.organization().urlOf('gsoc_update_slot_transfer')
+ # TODO(nathaniel): make this .organization() call unnecessary.
+ self.data.redirect.organization()
+
+ new_url = self.data.redirect.urlOf('gsoc_update_slot_transfer')
raise RedirectRequest(new_url)
def templatePath(self):
@@ -114,8 +112,10 @@
if (self.data.program.allocations_visible and
self.data.timeline.beforeStudentsAnnounced()):
- r = self.data.redirect.organization()
- edit_url = r.urlOf('gsoc_update_slot_transfer')
+ # TODO(nathaniel): make this .organization() call unnecessary.
+ self.data.redirect.organization()
+
+ edit_url = self.data.redirect.urlOf('gsoc_update_slot_transfer')
if require_new_link:
context['new_slot_transfer_page_link'] = edit_url
else:
@@ -124,7 +124,7 @@
return context
-class UpdateSlotTransferPage(RequestHandler):
+class UpdateSlotTransferPage(GSoCRequestHandler):
"""View for transferring the slots.
"""
@@ -170,9 +170,12 @@
'error': slot_transfer_form.errors
}
- r = self.data.redirect.organization()
- context['org_home_page_link'] = r.urlOf('gsoc_org_home')
- context['slot_transfer_page_link'] = r.urlOf('gsoc_slot_transfer')
+ # TODO(nathaniel): make this .organization() call unnecessary.
+ self.data.redirect.organization()
+
+ context['org_home_page_link'] = self.data.redirect.urlOf('gsoc_org_home')
+ context['slot_transfer_page_link'] = self.data.redirect.urlOf(
+ 'gsoc_slot_transfer')
return context
@@ -230,12 +233,13 @@
return db.run_in_transaction(create_or_update_slot_transfer_trx)
def post(self):
- """Handler for HTTP POST request.
- """
+ """Handler for HTTP POST request."""
slot_transfer_entity = self.createOrUpdateFromForm()
if slot_transfer_entity:
- self.redirect.organization(self.data.organization)
+ # TODO(nathaniel): make this .organization call unnecessary.
+ self.redirect.organization(organization=self.data.organization)
+
self.redirect.to('gsoc_update_slot_transfer', validated=True)
else:
self.get()
diff --git a/app/soc/modules/gsoc/views/slot_transfer_admin.py b/app/soc/modules/gsoc/views/slot_transfer_admin.py
index aec740d..ca28c58 100644
--- a/app/soc/modules/gsoc/views/slot_transfer_admin.py
+++ b/app/soc/modules/gsoc/views/slot_transfer_admin.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GSoC slot transfer admin page.
-"""
-
+"""Module for the GSoC slot transfer admin page."""
import logging
@@ -31,7 +27,7 @@
from soc.views.template import Template
from soc.modules.gsoc.models.slot_transfer import GSoCSlotTransfer
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -60,7 +56,7 @@
list_config.addSimpleColumn('admin_remarks', 'Admin remarks')
list_config.setColumnEditable('admin_remarks', True) #, edittype='textarea')
list_config.addColumn(
- 'slots_desired', 'Min desired',
+ 'slots_desired', 'Min desired',
(lambda e, *args: e.parent().slots_desired), width=25, hidden=True)
list_config.addColumn(
'max_slots_desired', 'Max desired',
@@ -110,7 +106,7 @@
if button_id == 'reject':
return self.postAccept(parsed, False)
-
+
if button_id == 'save':
return self.postSave(parsed)
@@ -125,7 +121,7 @@
slot_transfer = db.get(slot_transfer_key)
if not slot_transfer:
- logging.warning("Invalid slot_transfer_key '%s'" %
+ logging.warning("Invalid slot_transfer_key '%s'" %
slot_transfer_key)
return
@@ -222,7 +218,7 @@
return "v2/modules/gsoc/slot_transfer_admin/_list.html"
-class SlotsTransferAdminPage(RequestHandler):
+class SlotsTransferAdminPage(GSoCRequestHandler):
"""View for the the list of slot transfer requests.
"""
diff --git a/app/soc/modules/gsoc/views/statistic.py b/app/soc/modules/gsoc/views/statistic.py
index 8b3a4e1..0f16fbc 100644
--- a/app/soc/modules/gsoc/views/statistic.py
+++ b/app/soc/modules/gsoc/views/statistic.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,6 +14,8 @@
"""Module for the GSoC statistics page."""
+# TODO(nathaniel): as of 1 December 2012 this module is unused
+# (see soc.modules.gsoc.callback:62). Should it be removed?
from django.utils import simplejson
from django.core.urlresolvers import reverse
@@ -27,7 +27,7 @@
from soc.views.toggle_button import ToggleButtonTemplate
from soc.modules.gsoc.models.statistic_info import GSoCStatisticInfo
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper.url_patterns import url
from soc.modules.gsoc.statistics import mapping
@@ -36,8 +36,7 @@
class ManageActions(Template):
- """Template to render the left side admin actions.
- """
+ """Template to render the left side admin actions."""
IS_VISIBLE_HELP_MSG = ugettext(
'Whether this statistic is publicly visible to all users or not.')
@@ -52,7 +51,7 @@
labels = {
'checked': 'Yes',
'unchecked': 'No'})]
-
+
return {
'toggle_buttons': self.toggle_buttons
}
@@ -64,7 +63,7 @@
pass
-class StatisticDashboard(RequestHandler):
+class StatisticDashboard(GSoCRequestHandler):
"""View for the statistic page.
"""
@@ -121,7 +120,7 @@
manage_urls[name] = reverse(
'gsoc_statistic_manage', kwargs={'key_name': name})
return manage_urls
-
+
def _constructVisibilities(self, infos):
visibilities = {}
if self.isHost:
@@ -129,7 +128,7 @@
visibilities[str(info.name)] = True if info.is_visible else False
return simplejson.dumps(visibilities)
-class StatisticFetcher(RequestHandler):
+class StatisticFetcher(GSoCRequestHandler):
"""Loads data for a particular statistic.
"""
@@ -138,7 +137,7 @@
def checkAccess(self):
key_name = self.data.kwargs['key_name']
-
+
# TODO(dhans): check if the statistic is visible
pass
@@ -161,14 +160,14 @@
raise UnsupportedFormatException('Requested format is not supported.')
return self._presenter.get(key_name)
-
+
def jsonContext(self):
key_name = self.data.kwargs['key_name']
presentation = self._getPresentation(key_name)
return presentation
-class StatisticManager(RequestHandler):
+class StatisticManager(GSoCRequestHandler):
"""Manages the statistic entities.
"""
@@ -192,4 +191,3 @@
if statistic.getVisible() != value:
statistic.setVisible(value)
GSoCStatisticInfo.getInstance().updateStatistic(statistic)
-
\ No newline at end of file
diff --git a/app/soc/modules/gsoc/views/student_evaluation.py b/app/soc/modules/gsoc/views/student_evaluation.py
index 67c7764..4adeb10 100644
--- a/app/soc/modules/gsoc/views/student_evaluation.py
+++ b/app/soc/modules/gsoc/views/student_evaluation.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GSoC project student survey.
-"""
-
+"""Module for the GSoC project student survey."""
from soc.views import forms
from soc.views import survey
@@ -33,7 +29,7 @@
from soc.modules.gsoc.models.project_survey_record import \
GSoCProjectSurveyRecord
from soc.modules.gsoc.views import forms as gsoc_forms
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.base_templates import LoggedInMsg
from soc.modules.gsoc.views.helper import url_patterns
@@ -66,7 +62,7 @@
exclude = ['project', 'org', 'user', 'survey', 'created', 'modified']
-class GSoCStudentEvaluationEditPage(RequestHandler):
+class GSoCStudentEvaluationEditPage(GSoCRequestHandler):
"""View for creating/editing student evalution.
"""
@@ -144,7 +140,7 @@
else:
self.get()
-class GSoCStudentEvaluationTakePage(RequestHandler):
+class GSoCStudentEvaluationTakePage(GSoCRequestHandler):
"""View for students to submit their evaluation.
"""
@@ -238,7 +234,7 @@
self.get()
-class GSoCStudentEvaluationPreviewPage(RequestHandler):
+class GSoCStudentEvaluationPreviewPage(GSoCRequestHandler):
"""View for the host to preview the evaluation.
"""
@@ -272,7 +268,7 @@
return context
-class GSoCStudentEvaluationRecordsList(RequestHandler):
+class GSoCStudentEvaluationRecordsList(GSoCRequestHandler):
"""View for listing all records of a GSoCGProjectSurveyRecord.
"""
@@ -339,7 +335,7 @@
survey_name = 'Student Evaluation'
-class GSoCStudentEvaluationShowPage(RequestHandler):
+class GSoCStudentEvaluationShowPage(GSoCRequestHandler):
"""View to display the readonly page for student evaluation.
"""
diff --git a/app/soc/modules/gsoc/views/student_forms.py b/app/soc/modules/gsoc/views/student_forms.py
index 74f2171..086073c 100644
--- a/app/soc/modules/gsoc/views/student_forms.py
+++ b/app/soc/modules/gsoc/views/student_forms.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the GSoC student forms.
-"""
-
+"""Module for the GSoC student forms."""
from google.appengine.ext import blobstore
@@ -26,7 +22,7 @@
from soc.views.helper import url_patterns
from soc.modules.gsoc.models.profile import GSoCStudentInfo
-from soc.modules.gsoc.views.base import RequestHandler
+from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.forms import GSoCModelForm
from soc.modules.gsoc.views.helper.url_patterns import url
@@ -53,8 +49,11 @@
return self.request_data.kwargs['admin']
def _r(self):
- r = self.request_data.redirect
- return r.profile() if self._admin() else r.program()
+ if self._admin:
+ self.request_data.redirect.profile()
+ else:
+ self.request_data.redirect.program()
+ return self.request_data.redirect
def _urlName(self):
if self._admin():
@@ -97,8 +96,11 @@
return self.request_data.kwargs['admin']
def _r(self):
- r = self.request_data.redirect
- return r.profile() if self._admin() else r.program()
+ if self._admin():
+ self.request_data.redirect.profile()
+ else:
+ self.request_data.redirect.program()
+ return self.request_data.redirect
def _urlName(self):
if self._admin():
@@ -119,7 +121,7 @@
field._link = self._r().urlOf(self._urlName())
-class FormPage(RequestHandler):
+class FormPage(GSoCRequestHandler):
"""View to upload student forms.
"""
@@ -195,7 +197,11 @@
return 'gsoc_enrollment_form'
def _r(self):
- return self.redirect.profile() if self._admin() else self.redirect.program()
+ if self._admin():
+ self.redirect.profile()
+ else:
+ self.redirect.program()
+ return self.redirect
def jsonContext(self):
url = self._r().urlOf(self._urlName(), secure=True)
@@ -230,7 +236,7 @@
self._r().to(self._urlName(), validated=True)
-class DownloadForm(RequestHandler):
+class DownloadForm(GSoCRequestHandler):
"""View for downloading a student form.
"""
diff --git a/app/soc/templates/v2/modules/gci/homepage/_how_it_works.html b/app/soc/templates/v2/modules/gci/homepage/_how_it_works.html
index 4357f43..3cdfab4 100644
--- a/app/soc/templates/v2/modules/gci/homepage/_how_it_works.html
+++ b/app/soc/templates/v2/modules/gci/homepage/_how_it_works.html
@@ -48,6 +48,9 @@
{% if example_tasks_link %}
<a class="example-tasks" href="{{ example_tasks_link }}">Or see example tasks</a>
{% endif %}
+ {% if all_tasks_link %}
+ <a class="all-tasks-tasks" href="{{ all_tasks_link }}">Or see list of tasks</a>
+ {% endif %}
</div>
</div>
{% endif %}
diff --git a/app/soc/templates/v2/modules/gci/homepage/_participating_orgs.html b/app/soc/templates/v2/modules/gci/homepage/_participating_orgs.html
index 4522d49..cb5452e 100644
--- a/app/soc/templates/v2/modules/gci/homepage/_participating_orgs.html
+++ b/app/soc/templates/v2/modules/gci/homepage/_participating_orgs.html
@@ -14,7 +14,7 @@
{% endfor %}
</table>
</div>
- <div class="block-footer"></div>
+ <div class="block-footer"><a href="{{ org_list_url }}">{% if all_participating_orgs %}Participating organization details{% else %}See all participating organizations{% endif %}</a></div>
</div>
{% endif %}
<!-- end .block.block-home-participating-orgs -->
diff --git a/app/soc/templates/v2/modules/gci/moderate_delete_account/base.html b/app/soc/templates/v2/modules/gci/moderate_delete_account/base.html
new file mode 100644
index 0000000..6d7322f
--- /dev/null
+++ b/app/soc/templates/v2/modules/gci/moderate_delete_account/base.html
@@ -0,0 +1,73 @@
+{% extends "v2/modules/gci/base.html" %}
+{% comment %}
+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.
+{% endcomment %}
+
+{% block stylesheets %}
+ {{ block.super }}
+ <link rel="stylesheet" type="text/css" media="screen" href="/soc/content/{{ app_version }}/css/v2/gci/account_deletion.css" />
+{% endblock stylesheets %}
+
+{% block page_content %}
+{% if posted %}
+<div class="block block-user-message">
+ <p>You have requested your account to be deleted. All your information will be removed shortly.</p>
+</div>
+{% endif %}
+<div class="block block-page block-delete-account">
+ <div class="block-form-title">
+ <span class="title">Moderate account delete request</span>
+ </div>
+ <div class="block-content clearfix">
+ <div class="delete-info">
+ Name: {{ profile.name }}
+ </div>
+ <div class="delete-info">
+ Link ID: {{ profile.link_id }}
+ </div>
+ <div class="delete-info">
+ User has tasks assigned/closed: {{ has_tasks|yesno:"Yes,No,Maybe" }}
+ </div>
+ <div class="delete-info">
+ User has created or modified tasks: {{ has_created_or_modified_tasks|yesno:"Yes,No,Maybe" }}
+ </div>
+ <div class="delete-info">
+ User has task comments: {{ has_task_comments|yesno:"Yes,No,Maybe" }}
+ </div>
+ <div class="delete-info">
+ User has profiles in previous GCIs: {{ has_other_gci_profiles|yesno:"Yes,No,Maybe" }}
+ </div>
+ <div class="delete-info">
+ User has profiles in previous GSoCs: {{ has_other_gsoc_profiles|yesno:"Yes,No,Maybe" }}
+ </div>
+ <div class="moderate-delete-message">
+ {% if has_tasks or has_created_or_modified_tasks or has_task_comments %}
+ Deleting this profile will replace the current user's profile or
+ user entity with a dummy melange_deleted_user entity.
+ {% endif %}
+ </div>
+ <div class="moderate-delete-message">
+ {% if has_other_gci_profiles or has_other_gsoc_profiles %}
+ Confirming delete will only delete the profile entity for this program
+ and not the corresponding user entity because the user has profiles
+ for other programs.
+ {% endif %}
+ </div>
+ <form action="#" method="post" class="clearfix">
+ <p class="delete-btn-p">
+ <input value="Confirm delete" class="delete-btn" type="submit">
+ </p>
+ </form>
+ </div>
+</div>
+{% endblock page_content %}
\ No newline at end of file
diff --git a/app/soc/views/base.py b/app/soc/views/base.py
index 127a9f0..19fa5e6 100644
--- a/app/soc/views/base.py
+++ b/app/soc/views/base.py
@@ -16,25 +16,20 @@
module is largely based on appengine's webapp framework's code.
"""
-
+import httplib
import urllib
from google.appengine.ext import db
+from django import http
from django.utils import simplejson
from django.template import loader
-from soc.logic.exceptions import LoginRequest
-from soc.logic.exceptions import RedirectRequest
-from soc.logic.exceptions import AccessViolation
-from soc.logic.exceptions import GDocsLoginRequest
-from soc.logic.exceptions import MaintainceMode
-from soc.logic.exceptions import Error
+from soc.logic import exceptions
from soc.views.helper import access_checker
-from soc.views.helper.response import Response
from soc.views.helper import context as context_helper
-from soc.views.helper.request_data import RequestData
-from soc.views.helper.request_data import RedirectHelper
+from soc.views.helper import request_data
+from soc.views.helper import response as response_helper
class RequestHandler(object):
@@ -50,12 +45,14 @@
those to render to construct the page.
"""
context = self.context()
- self.render(self.templatePath(), context)
+ template_path = self.templatePath()
+ response_content = self.render(template_path, context)
+ self.response.write(response_content)
def json(self):
"""Handler for HTTP GET request with a 'fmt=json' parameter."""
- if not self.request.GET.get('plain'):
+ if not self.data.request.GET.get('plain'):
self.response['Content-Type'] = 'application/json'
# if the browser supports HTTP/1.1
@@ -68,7 +65,7 @@
context = self.jsonContext()
- if self.request.GET.get('marker'):
+ if self.data.request.GET.get('marker'):
# allow the django test framework to capture the context dictionary
loader.render_to_string('json_marker.html', dictionary=context)
@@ -84,130 +81,175 @@
with 'fmt=json' parameter.
"""
return {
- 'error': "json() method not implemented",
+ 'error': 'json() method not implemented',
}
def post(self):
- """Handler for HTTP POST request.
- """
- self.error(405)
+ """Handler for HTTP POST request."""
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
def head(self):
"""Handler for HTTP HEAD request.
+
+ Returns:
+ An http.HttpResponse appropriate for this RequestHandler's request
+ object.
"""
- self.error(405)
+ return self.error(httplib.METHOD_NOT_ALLOWED)
def options(self):
"""Handler for HTTP OPTIONS request.
+
+ Returns:
+ An http.HttpResponse appropriate for this RequestHandler's request
+ object.
"""
- self.error(405)
+ return self.error(httplib.METHOD_NOT_ALLOWED)
def put(self):
- """Handler for HTTP PUT request.
- """
- self.error(405)
+ """Handler for HTTP PUT request."""
+ self.response = self.error(httplib.METHOD_NOT_ALLOWED)
def delete(self):
"""Handler for HTTP DELETE request.
+
+ Returns:
+ An http.HttpResponse appropriate for this RequestHandler's request
+ object.
"""
- self.error(405)
+ return self.error(httplib.METHOD_NOT_ALLOWED)
def trace(self):
"""Handler for HTTP TRACE request.
+
+ Returns:
+ An http.HttpResponse appropriate for this RequestHandler's request
+ object.
"""
- self.error(405)
+ return self.error(httplib.METHOD_NOT_ALLOWED)
def error(self, status, message=None):
- """Sets the error response code and message when an error is encountered.
+ """Constructs an HttpResponse indicating an error.
Args:
- status: the HTTP status error code
- message: the message to set, uses default if None
+ status: The HTTP status code for the error.
+ message: A message to display to the user. If not supplied, a default
+ appropriate for the given status code (such as "Bad Gateway" or
+ "Payment Required") will be used.
+
+ Returns:
+ An http.HttpResponse indicating an error.
"""
- self.response.set_status(status, message=message)
- template_path = "error.html"
+ message = message or httplib.responses.get(status, '')
+
+ template_path = 'error.html'
context = {
- 'page_name': self.response.content,
- 'message': self.response.content,
+ 'page_name': message,
+ 'message': message,
}
- self.response.content = ''
- self.render(template_path, context)
+ return http.HttpResponse(
+ content=self.render(template_path, context), status=status)
def djangoURLPatterns(self):
- """Returns a list of Django URL pattern tuples."""
- return []
+ """Returns a list of Django URL pattern tuples.
+
+ Implementing subclasses must override this method.
+ """
+ raise NotImplementedError()
def checkAccess(self):
- """Raise an exception if the user doesn't have access to the
- requested URL.
+ # TODO(nathaniel): eliminate this - it doesn't actually simplify
+ # the HTTP method implementations all that much to have it
+ # separated out.
+ """Ensure that the user's request should be satisfied.
+
+ Implementing subclasses must override this method.
+
+ Implementations must not mutate any of this RequestHandler's state and
+ should merely raise an exception if the user's request should not be
+ satisfied or return normally if the user's request should be satisfied.
+
+ Raises:
+ exceptions.Error: If the user's request should not be satisfied for
+ any reason.
"""
- self.error(401, "checkAccess in base RequestHandler has not been changed "
- "to grant access")
+ raise NotImplementedError()
def render(self, template_path, render_context):
- """Renders the page using the specified context.
+ """Renders the page content from the specified template and context.
- The page is rendered using the template and context specified and
- is written to the response object.
-
- The context object is extended with the values from helper.context.default.
+ Values supplied by helper.context.default are used in the rendering in
+ addition to those supplied by render_context (render_context overrides
+ in cases of conflict).
Args:
- template_path: the path of the template that should be used
- render_context: the context that should be used
+ template_path: The path of the template that should be used.
+ render_context: The context dictionary that should be used.
+
+ Returns:
+ The page content.
"""
context = context_helper.default(self.data)
context.update(render_context)
- rendered = loader.render_to_string(template_path, dictionary=context)
- self.response.write(rendered)
+ return loader.render_to_string(template_path, dictionary=context)
def templatePath(self):
"""Returns the path to the template that should be used in render().
- Subclasses should override this method.
+ Implementing subclasses must override this method.
"""
raise NotImplementedError()
- def accessViolation(self, status, message):
- """Default access violation handler."""
- self.error(status, message)
-
def _dispatch(self):
- """Dispatches the HTTP request to its respective handler method."""
- if self.request.method == 'GET':
- if self.request.GET.get('fmt') == 'json':
+ """Dispatches the HTTP request to its respective handler method.
+
+ Returns:
+ An http.HttpResponse appropriate for this RequestHandler's request
+ object.
+ """
+ if self.data.request.method == 'GET':
+ if self.data.request.GET.get('fmt') == 'json':
self.json()
else:
self.get()
- elif self.request.method == 'POST':
+ return self.response
+ elif self.data.request.method == 'POST':
if db.WRITE_CAPABILITY.is_enabled():
self.post()
else:
- referrer = self.request.META.get('HTTP_REFERER', '')
+ referrer = self.data.request.META.get('HTTP_REFERER', '')
params = urllib.urlencode({'dsw_disabled': 1})
url_with_params = '%s?%s' % (referrer, params)
self.redirect.toUrl(url_with_params)
- elif self.request.method == 'HEAD':
- self.head()
- elif self.request.method == 'OPTIONS':
- self.options()
- elif self.request.method == 'PUT':
+ return self.response
+ elif self.data.request.method == 'HEAD':
+ return self.head()
+ elif self.data.request.method == 'OPTIONS':
+ return self.options()
+ elif self.data.request.method == 'PUT':
self.put()
- elif self.request.method == 'DELETE':
- self.delete()
- elif self.request.method == 'TRACE':
- self.trace()
+ return self.response
+ elif self.data.request.method == 'DELETE':
+ return self.delete()
+ elif self.data.request.method == 'TRACE':
+ return self.trace()
else:
- self.error(501)
+ return self.error(httplib.NOT_IMPLEMENTED)
+ # TODO(nathaniel): Note that while this says that it sets the "data" and
+ # "check" attributes, this implementation makes use of the "data" attribute
+ # without having set it. Therefore extending classes must set at least the
+ # "data" attribute before calling this superclass implementation if they
+ # choose to do so (they do). This is an obstacle just waiting to cause
+ # bigger problems.
def init(self, request, args, kwargs):
"""Initializes the RequestHandler.
Sets the data and check fields.
"""
if self.data.site.maintenance_mode and not self.data.is_developer:
- raise MaintainceMode(
+ raise exceptions.MaintainceMode(
'The site is currently in maintenance mode. Please try again later.')
def __call__(self, request, *args, **kwargs):
@@ -224,24 +266,22 @@
self.args = args
self.kwargs = kwargs
- self.response = Response()
+ self.response = response_helper.Response()
try:
self.init(request, args, kwargs)
self.checkAccess()
- self._dispatch()
- except LoginRequest, e:
+ self.response = self._dispatch()
+ except exceptions.LoginRequest, e:
request.get_full_path().encode('utf-8')
self.redirect.login().to()
- except RedirectRequest, e:
+ except exceptions.RedirectRequest, e:
self.redirect.toUrl(e.url)
- except AccessViolation, e:
- self.accessViolation(e.status, e.args[0])
- except GDocsLoginRequest, e:
+ except exceptions.GDocsLoginRequest, e:
self.redirect.toUrl('%s?%s' % (self.redirect.urlOf(e.url_name),
urllib.urlencode({'next':e.next})))
- except Error, e:
- self.error(e.status, message=e.args[0])
+ except exceptions.Error, e:
+ self.response = self.error(e.status, message=e.args[0])
finally:
response = self.response
self.response = None
@@ -257,12 +297,11 @@
class SiteRequestHandler(RequestHandler):
- """Customization required by global site pages to handle HTTP requests.
- """
+ """Customization required by global site pages to handle HTTP requests."""
def init(self, request, args, kwargs):
- self.data = RequestData()
- self.redirect = RedirectHelper(self.data, self.response)
+ self.data = request_data.RequestData()
+ self.redirect = request_data.RedirectHelper(self.data, self.response)
self.data.populate(None, request, args, kwargs)
if self.data.is_developer:
self.mutator = access_checker.DeveloperMutator(self.data)
diff --git a/app/soc/views/base_templates.py b/app/soc/views/base_templates.py
index 0e621e6..665f89e 100644
--- a/app/soc/views/base_templates.py
+++ b/app/soc/views/base_templates.py
@@ -1,13 +1,11 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 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.
@@ -16,7 +14,6 @@
"""This module contains the view for the site menus."""
-
from soc.models.site import Site
from soc.views.template import Template
@@ -66,12 +63,16 @@
def context(self):
def url(program):
- r = self.data.redirect.program(program)
- return r.urlOf(self.url_name)
+ # TODO(nathaniel): make this .program call unnecessary.
+ self.data.redirect.program(program=program)
+
+ return self.data.redirect.urlOf(self.url_name)
+
def attr(program):
if program.key() == self.data.program.key():
return "selected=selected"
- return ""
+ else:
+ return ""
program_key = Site.active_program.get_value_for_datastore(self.data.site)
diff --git a/app/soc/views/helper/blobstore.py b/app/soc/views/helper/blobstore.py
index f1a7bd5..644c1b8 100644
--- a/app/soc/views/helper/blobstore.py
+++ b/app/soc/views/helper/blobstore.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2008 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,10 +25,11 @@
import cgi
import logging
+from django import http
+
from google.appengine.ext import blobstore
-from soc.logic.exceptions import BadRequest
-from soc.views.helper.response import Response
+from soc.logic import exceptions
def _parseField(request, key, field):
@@ -106,17 +105,25 @@
def sendBlob(blob_info):
- """Send a blob-response based on a blob_key.
+ """Constructs a response that App Engine will interpret as a blob send.
- Sets the correct response header for serving a blob. If BlobInfo
- is provided and no content_type specified, will set request content type
- to BlobInfo's content type.
+ The returned http.HttpResponse will have a "Content-Disposition" header
+ based on blob_info's stored file name, a "Content-Type" header based on
+ blob_info's stored content type, and a blobstore.BLOB_KEY_HEADER header
+ storing blob key of the blob to be sent to the user.
+
+ The returned http.HttpResponse may also have other headers set.
Args:
- blob_info: BlobInfo record to serve
+ blob_info: BlobInfo record representing the blob to be received by
+ the user.
+
+ Returns:
+ An http.HttpResponse object with at least "Content-Type",
+ "Content-Disposition", and blobstore.BLOB_KEY_HEADER headers set.
Raises:
- BadRequest: on missing filename in blob_info
+ exceptions.BadRequest: If blob_info is missing a file name.
"""
logging.debug(blob_info)
assert isinstance(blob_info, blobstore.BlobInfo)
@@ -130,14 +137,19 @@
content_type = content_type.encode('utf-8')
if not filename:
- raise BadRequest('No filename in blob_info.')
+ raise exceptions.BadRequest('No filename in blob_info.')
if isinstance(filename, unicode):
filename = filename.encode('utf-8')
- response = Response()
- response['Content-Type'] = content_type
- response['Content-Disposition'] = (CONTENT_DISPOSITION % filename)
+ response = http.HttpResponse(content_type=content_type)
+ # We set the cache control to disable all kinds of caching hoping
+ # that Appengine does not cache the blob keys in the header if we
+ # set this.
+ response['Cache-Control'] = (
+ 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0')
+
+ response['Content-Disposition'] = CONTENT_DISPOSITION % filename
response[blobstore.BLOB_KEY_HEADER] = str(blob_info.key())
return response
diff --git a/app/soc/views/helper/context.py b/app/soc/views/helper/context.py
index e44a84d..0e4807b 100644
--- a/app/soc/views/helper/context.py
+++ b/app/soc/views/helper/context.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the boiler plate required to construct templates
-"""
-
+"""Module containing the boiler plate required to construct templates."""
from soc.logic import system
from soc.logic.helper import xsrfutil
@@ -35,8 +31,11 @@
google_api_key: the google api key for this website
ga_tracking_num: the google tracking number for this website
ds_write_disabled: if datastore writes are disabled
- css_path: part of the path to the css files to distinguish modules
+ css_path: part of the path to the css files to distinguish modules
+ gdata_is_logged_in: the string "true" or "false" as appropriate
"""
+ app_version = system.getMelangeVersion()
+
posted = data.request.POST or 'validated' in data.request.GET
xsrf_secret_key = site.xsrfSecretKey(data.site)
@@ -47,23 +46,19 @@
else:
google_api_key = data.site.google_api_key
- if data.user and oauth_helper.getAccessToken(data.user):
- gdata_is_logged_in = 'true'
- else:
- gdata_is_logged_in = 'false'
-
css_path = '/'.join([
- 'soc', 'content', system.getMelangeVersion(), 'css', 'v2',
- data.css_path])
+ 'soc', 'content', app_version, 'css', 'v2', data.css_path])
+
+ gdata_is_logged_in = data.user and oauth_helper.getAccessToken(data.user)
return {
- 'app_version': system.getMelangeVersion(),
+ 'app_version': app_version,
'is_local': system.isLocal(),
'posted': posted,
'xsrf_token': xsrf_token,
'google_api_key': google_api_key,
'ga_tracking_num': data.site.ga_tracking_num,
'ds_write_disabled': data.ds_write_disabled,
- 'gdata_is_logged_in': gdata_is_logged_in,
- 'css_path': css_path
+ 'css_path': css_path,
+ 'gdata_is_logged_in': str(bool(gdata_is_logged_in)).lower(),
}
diff --git a/app/soc/views/helper/request_data.py b/app/soc/views/helper/request_data.py
index f3f2a60..eb045cb 100644
--- a/app/soc/views/helper/request_data.py
+++ b/app/soc/views/helper/request_data.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,18 +16,18 @@
request in the GSoC module.
"""
-
import datetime
from google.appengine.api import users
from google.appengine.ext import db
-from django.core.urlresolvers import reverse
+from django.core import urlresolvers
+from django.utils import encoding
from soc.logic import system
from soc.logic import site
from soc.logic import user
-from soc.views.helper.access_checker import isSet
+from soc.views.helper import access_checker
def isBefore(date):
@@ -49,8 +47,7 @@
def isBetween(start, end):
- """Returns True iff utcnow() is between start and end.
- """
+ """Returns True iff utcnow() is between start and end."""
return isAfter(start) and isBefore(end)
@@ -67,13 +64,11 @@
self.org_app = org_app
def currentPeriod(self):
- """Return where we are currently on the timeline.
- """
+ """Return where we are currently on the timeline."""
pass
def nextDeadline(self):
- """Determines the next deadline on the timeline.
- """
+ """Determines the next deadline on the timeline."""
pass
def orgsAnnouncedOn(self):
@@ -169,8 +164,7 @@
"""
def __init__(self):
- """Constructs an empty RequestData object.
- """
+ """Constructs an empty RequestData object."""
self.site = None
self.user = None
self.request = None
@@ -189,24 +183,21 @@
@property
def login_url(self):
- """Memoizes and returns the login_url for the current path.
- """
+ """Memoizes and returns the login_url for the current path."""
if not self._login_url:
self._login_url = users.create_login_url(self.full_path)
return self._login_url
@property
def logout_url(self):
- """Memoizes and returns the logout_url for the current path.
- """
+ """Memoizes and returns the logout_url for the current path."""
if not self._logout_url:
self._logout_url = users.create_logout_url(self.full_path)
return self._logout_url
@property
def ds_write_disabled(self):
- """Memoizes and returns whether datastore writes are disabled.
- """
+ """Memoizes and returns whether datastore writes are disabled."""
if self._ds_write_disabled is not None:
return self._ds_write_disabled
@@ -267,20 +258,18 @@
return invite.role if invite else None
+# TODO(nathaniel): This should be immutable.
class RedirectHelper(object):
- """Helper for constructing redirects.
- """
+ """Helper for constructing redirects."""
def __init__(self, data, response):
- """Initializes the redirect helper.
- """
+ """Initializes the redirect helper."""
self._data = data
self._response = response
self._clear()
def _clear(self):
- """Clears the internal state.
- """
+ """Clears the internal state."""
self._no_url = False
self._url_name = None
self._url = None
@@ -288,38 +277,31 @@
self.kwargs = {}
def sponsor(self, program=None):
- """Sets kwargs for an url_patterns.SPONSOR redirect.
- """
+ """Sets kwargs for an url_patterns.SPONSOR redirect."""
if not program:
- assert isSet(self._data.program)
+ assert access_checker.isSet(self._data.program)
program = self._data.program
self._clear()
self.kwargs['sponsor'] = program.scope_path
- return self
def program(self, program=None):
- """Sets kwargs for an url_patterns.PROGRAM redirect.
- """
+ """Sets kwargs for an url_patterns.PROGRAM redirect."""
if not program:
- assert isSet(self._data.program)
+ assert access_checker.isSet(self._data.program)
program = self._data.program
self.sponsor(program)
self.kwargs['program'] = program.link_id
- return self
def organization(self, organization=None):
- """Sets the kwargs for an url_patterns.ORG redirect.
- """
+ """Sets the kwargs for an url_patterns.ORG redirect."""
if not organization:
- assert isSet(self._data.organization)
+ assert access_checker.isSet(self._data.organization)
organization = self._data.organization
self.program()
self.kwargs['organization'] = organization.link_id
- return self
def id(self, id=None):
- """Sets the kwargs for an url_patterns.ID redirect.
- """
+ """Sets the kwargs for an url_patterns.ID redirect."""
if not id:
assert 'id' in self._data.kwargs
id = self._data.kwargs['id']
@@ -328,8 +310,7 @@
return self
def key(self, key=None):
- """Sets the kwargs for an url_patterns.KEY redirect.
- """
+ """Sets the kwargs for an url_patterns.KEY redirect."""
if not key:
assert 'key' in self._data.kwargs
key = self._data.kwargs['key']
@@ -338,15 +319,13 @@
return self
def createProfile(self, role):
- """Sets args for an url_patterns.CREATE_PROFILE redirect.
- """
+ """Sets args for an url_patterns.CREATE_PROFILE redirect."""
self.program()
self.kwargs['role'] = role
return self
def profile(self, user=None):
- """Sets args for an url_patterns.PROFILE redirect.
- """
+ """Sets args for an url_patterns.PROFILE redirect."""
if not user:
assert 'user' in self._data.kwargs
user = self._data.kwargs['user']
@@ -374,14 +353,13 @@
return self
def userOrg(self, user=None, organization=None):
- """Sets args for an url_patterns.USER_ORG redirect.
- """
+ """Sets args for an url_patterns.USER_ORG redirect."""
if not user:
assert 'user' in self._data.kwargs
user = self._data.kwargs['user']
if not organization:
- assert isSet(self._data.organization)
+ assert access_checker.isSet(self._data.organization)
organization = self._data.organization
self.program()
@@ -391,8 +369,7 @@
def userId(self, user=None, id=None):
- """Sets args for url_patterns.USER_ID redirect.
- """
+ """Sets args for url_patterns.USER_ID redirect."""
if not user:
assert 'user' in self._data.kwargs
user = self._data.kwargs['user']
@@ -411,20 +388,24 @@
Uses internal state for args and kwargs.
"""
+ # TODO(nathaniel): Why isn't this just "url = reverse(name, args=self.args,
+ # kwargs=self.kwargs)"? Current suspicion: it's because there's a
+ # there's a difference in behavior between passing None and passing empty
+ # dicts. It's also curious that there isn't an "if self.args and
+ # self.kwargs" case at the top.
if self.args:
- url = reverse(name, args=self.args)
+ url = urlresolvers.reverse(name, args=self.args)
elif self.kwargs:
- url = reverse(name, kwargs=self.kwargs)
+ url = urlresolvers.reverse(name, kwargs=self.kwargs)
else:
- url = reverse(name)
+ url = urlresolvers.reverse(name)
url = self._appendGetArgs(url, cbox=cbox, extra_get_args=extra)
return self._fullUrl(url, full, secure)
def url(self, full=False, secure=False):
- """Returns the url of the current state.
- """
+ """Returns the url of the current state."""
if self._no_url:
return None
assert self._url or self._url_name
@@ -440,6 +421,7 @@
if (not full) and (system.isLocal() or not secure):
return url
+ # TODO(nathaniel): consider using scheme-relative urls here?
if secure:
protocol = 'https'
hostname = system.getSecureHostname()
@@ -450,18 +432,17 @@
return '%s://%s%s' % (protocol, hostname, url)
def _appendAnchor(self, url, anchor=None):
- """Appends the anchor to the URL.
- """
+ """Appends the anchor to the URL."""
if anchor:
url = '%s#%s' % (url, anchor)
return url
- def _appendGetArgs(self, url, cbox=False, validated=False,
- extra_get_args=[]):
- """Appends GET arguments to the specified URL.
- """
- get_args = extra_get_args[:]
+ # TODO(nathaniel): Django's got to have a utility function for most of this.
+ def _appendGetArgs(
+ self, url, cbox=False, validated=False, extra_get_args=None):
+ """Appends GET arguments to the specified URL."""
+ get_args = extra_get_args or []
if cbox:
get_args.append('cbox=true')
@@ -509,30 +490,25 @@
self.toUrl(url, full=full, secure=secure)
def toUrl(self, url, full=False, secure=False):
- """Redirects to the specified url.
- """
- from django.utils.encoding import iri_to_uri
+ """Redirects to the specified url."""
url = self._fullUrl(url, full, secure)
self._response.status_code = 302
- self._response["Location"] = iri_to_uri(url)
+ self._response["Location"] = encoding.iri_to_uri(url)
def login(self):
- """Sets the _url to the login url.
- """
+ """Sets the _url to the login url."""
self._clear()
self._url = self._data.login_url
return self
def logout(self):
- """Sets the _url to the logout url.
- """
+ """Sets the _url to the logout url."""
self._clear()
self._url = self._data.logout_url
return self
def acceptedOrgs(self):
- """Sets the _url_name to the list of all accepted orgs.
- """
+ """Sets the _url_name to the list of all accepted orgs."""
self.program()
return self
@@ -546,26 +522,22 @@
return self
def searchpage(self):
- """Sets the _url_name for the searchpage of the current program.
- """
+ """Sets the _url_name for the searchpage of the current program."""
self.program()
return self
def orgHomepage(self, link_id):
- """Sets the _url_name for the specified org homepage
- """
+ """Sets the _url_name for the specified org homepage."""
self.program()
self.kwargs['organization'] = link_id
return self
def dashboard(self):
- """Sets the _url_name for dashboard page of the current program.
- """
+ """Sets the _url_name for dashboard page of the current program."""
self.program()
return self
def events(self):
- """Sets the _url_name for the events page, if it is set.
- """
+ """Sets the _url_name for the events page, if it is set."""
self.program()
return self
diff --git a/app/soc/views/helper/requests.py b/app/soc/views/helper/requests.py
deleted file mode 100644
index 15a9d87..0000000
--- a/app/soc/views/helper/requests.py
+++ /dev/null
@@ -1,184 +0,0 @@
-#!/usr/bin/env python2.5
-#
-# Copyright 2008 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.
-
-"""Helpers for manipulating HTTP requests.
-"""
-
-
-import urlparse
-
-from soc.logic import system
-
-
-def getSingleIndexedParamValue(request, param_name, values=()):
- """Returns a value indexed by a query parameter in the HTTP request.
-
- Args:
- request: the Django HTTP request object
- param_name: name of the query parameter in the HTTP request
- values: list (or tuple) of ordered values; one of which is
- retrieved by the index value of the param_name argument in
- the HTTP request
-
- Returns:
- None if the query parameter was not present, was not an integer, or
- was an integer that is not a valid [0..len(values)-1] index into
- the values list.
- Otherwise, returns values[int(param_name value)]
- """
- value_idx = request.GET.get(param_name)
-
- if isinstance(value_idx, (tuple, list)):
- # keep only the first argument if multiple are present
- value_idx = value_idx[0]
-
- try:
- # GET parameter 'param_name' should be an integer value index
- value_idx = int(value_idx) if value_idx is not None else -1
- except ValueError:
- # ignore bogus or missing parameter values, so return None (no message)
- return None
-
- if value_idx < 0:
- # value index out of range, so return None (no value)
- return None
-
- if value_idx >= len(values):
- # value index out of range, so return None (no value)
- return None
-
- # return value associated with valid value index
- return values[value_idx]
-
-
-def getSingleIndexedParamValueIfMissing(value, request, param_name,
- values=()):
- """Returns missing value indexed by a query parameter in the HTTP request.
-
- Args:
- value: an existing value, or a "False" value such as None
- request, param_name, values: see getSingleIndexParamValue()
-
- Returns:
- value, if value is "non-False"
- Otherwise, returns getSingleIndexedParamValue() result.
- """
- if value:
- # value already present, so return it
- return value
-
- return getSingleIndexedParamValue(request, param_name, values=values)
-
-
-# TODO(tlarsen): write getMultipleIndexParamValues() that returns a
-# list of values if present, omitting those values that are
-# out of range
-
-
-def isReferrerSelf(request,
- expected_prefix=None, suffix=None, url_name=None):
- """Returns True if HTTP referrer path starts with the HTTP request path.
-
- Args:
- request: the Django HTTP request object; request.path is used if
- expected_path is not supplied (the most common usage)
- expected_prefix: optional HTTP path to use instead of the one in
- request.path; default is None (use request.path)
- suffix: suffix to remove from the HTTP request path before comparing
- it to the HTTP referrer path in the HTTP request object headers
- (this is often an link ID, for example, that may be changing from
- a POST referrer to a GET redirect target)
- url_name: url name of the entity that is being created
-
- Returns:
- True if HTTP referrer path begins with the HTTP request path (either
- request.path or expected_prefix instead if it was supplied), after
- any suffix was removed from that request path
- False otherwise
-
- """
- http_from = request.META.get('HTTP_REFERER')
-
- if not http_from:
- # no HTTP referrer, so cannot possibly start with expected prefix
- return False
-
- http_host = 'http://%s/%s' % (system.getHostname(), url_name)
-
- if http_from.startswith(http_host):
- return True
-
- from_path = urlparse.urlparse(http_from).path
-
- if not expected_prefix:
- # use HTTP request path, since expected_prefix was not supplied
- expected_prefix = request.path
-
- if suffix:
- # remove suffix (such as a link ID) before comparison
- chars_to_remove = len(suffix)
-
- if not suffix.startswith('/'):
- chars_to_remove = chars_to_remove + 1
-
- expected_prefix = expected_prefix[:-chars_to_remove]
-
- if not from_path.startswith(expected_prefix):
- # expected prefix did not match first part of HTTP referrer path
- return False
-
- # HTTP referrer started with (possibly truncated) expected prefix
- return True
-
-
-def replaceSuffix(path, old_suffix, new_suffix=None, params=None):
- """Replace the last part of a URL path with something else.
-
- Also appends an optional list of query parameters. Used for
- replacing, for example, one link ID at the end of a relative
- URL path with another.
-
- Args:
- path: HTTP request relative URL path (with no query arguments)
- old_suffix: expected suffix at the end of request.path component;
- if any False value (such as None), the empty string '' is used
- new_suffix: if non-False, appended to request.path along with a
- '/' separator (after removing old_suffix if necessary)
- params: an optional dictionary of query parameters to append to
- the redirect target; appended as ?<key1>=<value1>&<key2>=...
-
- Returns:
- /path/with/new_suffix?a=1&b=2
- """
- if not old_suffix:
- old_suffix = ''
-
- old_suffix = '/' + old_suffix
-
- if path.endswith(old_suffix):
- # also removes any trailing '/' if old_suffix was empty
- path = path[:-len(old_suffix)]
-
- if new_suffix:
- # if present, appends new_suffix, after '/' separator
- path = '%s/%s' % (path, new_suffix)
-
- if params:
- # appends any query parameters, after a '?' and separated by '&'
- path = '%s?%s' % (path, '&'.join(
- ['%s=%s' % (p,v) for p,v in params.iteritems()]))
-
- return path
diff --git a/app/soc/views/helper/response.py b/app/soc/views/helper/response.py
index 72d8c8a..342653c 100644
--- a/app/soc/views/helper/response.py
+++ b/app/soc/views/helper/response.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,84 +12,27 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+"""Module containing the Response object."""
-"""Module containing the Response object.
-"""
+import httplib
from django import http
+# TODO(nathaniel): eliminate this in favor of using its superclass everywhere.
class Response(http.HttpResponse):
"""Response class that wraps the Django's HttpResponse class but
with message for every possible HTTP response code.
"""
- DEFAULT_CONTENT_TYPE = 'text/html'
+ def set_status(self, status):
+ """Sets the HTTP status and content for this response.
- HTTP_STATUS_MESSAGES = {
- 100: 'Continue',
- 101: 'Switching Protocols',
- 200: 'OK',
- 201: 'Created',
- 202: 'Accepted',
- 203: 'Non-Authoritative Information',
- 204: 'No Content',
- 205: 'Reset Content',
- 206: 'Partial Content',
- 300: 'Multiple Choices',
- 301: 'Moved Permanently',
- 302: 'Moved Temporarily',
- 303: 'See Other',
- 304: 'Not Modified',
- 305: 'Use Proxy',
- 306: 'Unused',
- 307: 'Temporary Redirect',
- 400: 'Bad Request',
- 401: 'Unauthorized',
- 402: 'Payment Required',
- 403: 'Forbidden',
- 404: 'Not Found',
- 405: 'Method Not Allowed',
- 406: 'Not Acceptable',
- 407: 'Proxy Authentication Required',
- 408: 'Request Time-out',
- 409: 'Conflict',
- 410: 'Gone',
- 411: 'Length Required',
- 412: 'Precondition Failed',
- 413: 'Request Entity Too Large',
- 414: 'Request-URI Too Large',
- 415: 'Unsupported Media Type',
- 416: 'Requested Range Not Satisfiable',
- 417: 'Expectation Failed',
- 500: 'Internal Server Error',
- 501: 'Not Implemented',
- 502: 'Bad Gateway',
- 503: 'Service Unavailable',
- 504: 'Gateway Time-out',
- 505: 'HTTP Version not supported'
- }
-
- def __init__(self, content='', mimetype=None, status=200,
- content_type=DEFAULT_CONTENT_TYPE):
- """Default constructor for an empty 200 response.
- """
- super(Response, self).__init__(content, mimetype,
- status, content_type)
-
- def set_status(self, status, message=None):
- """Sets the HTTP status and message for this response.
+ The content of this Response will be set to the standard response for
+ the given status as found in httplib.responses.
Args:
- status: HTTP status code
- message: the HTTP status string to use
-
- If no status string is given, we use the default from the HTTP/1.1
- specification defined in the dictionary HTTP_STATUS_MESSAGE.
+ status: HTTP status code.
"""
- if not message:
- message = self.HTTP_STATUS_MESSAGES.get(status, '')
-
self.status_code = status
- self.content = message
-
+ self.content = httplib.responses.get(status, '')
diff --git a/app/soc/views/oauth.py b/app/soc/views/oauth.py
index dd459c2..00614c0 100644
--- a/app/soc/views/oauth.py
+++ b/app/soc/views/oauth.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,20 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing views for Open Auth.
-"""
-
+"""Module containing views for Open Auth."""
from django.conf.urls.defaults import url as django_url
from soc.views.helper.gdata_apis import oauth as oauth_helper
-from soc.modules.gsoc.views.base import RequestHandler
+# TODO(nathaniel): modules-gsoc code being imported in non modules-gsoc code.
+from soc.modules.gsoc.views.base import GSoCRequestHandler
-class OAuthRedirectPage(RequestHandler):
- """Redirect page to Google Documents.
- """
+class OAuthRedirectPage(GSoCRequestHandler):
+ """Redirect page to Google Documents."""
def djangoURLPatterns(self):
patterns = [
@@ -57,9 +53,8 @@
pass
-class OAuthVerifyToken(RequestHandler):
- """Verify request token and redirect user.
- """
+class OAuthVerifyToken(GSoCRequestHandler):
+ """Verify request token and redirect user."""
def djangoURLPatterns(self):
patterns = [
@@ -75,9 +70,8 @@
return self.response
-class PopupOAuthRedirectPage(RequestHandler):
- """Redirects popup page to Google Documents.
- """
+class PopupOAuthRedirectPage(GSoCRequestHandler):
+ """Redirects popup page to Google Documents."""
def djangoURLPatterns(self):
patterns = [
@@ -104,9 +98,8 @@
return self.response
-class PopupOAuthVerified(RequestHandler):
- """ Calls parent window's methods to indicate successful login.
- """
+class PopupOAuthVerified(GSoCRequestHandler):
+ """ Calls parent window's methods to indicate successful login."""
def djangoURLPatterns(self):
patterns = [
diff --git a/app/soc/views/org_home.py b/app/soc/views/org_home.py
index fb2df46..fd05387 100644
--- a/app/soc/views/org_home.py
+++ b/app/soc/views/org_home.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python
-#
# Copyright 2012 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module containing the views for Organization Homepage.
-"""
-
+"""Module containing the views for Organization Homepage."""
from google.appengine.ext import db
@@ -27,8 +23,7 @@
class BanOrgPost(object):
- """Handles banning/unbanning of organizations.
- """
+ """Handles banning/unbanning of organizations."""
def djangoURLPatterns(self):
return [
@@ -43,7 +38,7 @@
def post(self):
assert isSet(self.data.organization)
-
+
value = self.data.POST.get('value')
org_key = self.data.organization.key()
@@ -83,12 +78,14 @@
def context(self):
assert isSet(self.data.organization)
- r = self.data.redirect.organization()
+ # TODO(nathaniel): make this .organization() call unnecessary.
+ self.data.redirect.organization()
+
is_banned = self.data.organization.status == 'invalid'
org_banned = ToggleButtonTemplate(
self.data, 'on_off', 'Banned', 'organization-banned',
- r.urlOf(self._getActionURLName()),
+ self.data.redirect.urlOf(self._getActionURLName()),
checked=is_banned,
help_text=self._getHelpText(),
labels={
diff --git a/app/soc/views/profile_show.py b/app/soc/views/profile_show.py
index 255c850..7d618d4 100644
--- a/app/soc/views/profile_show.py
+++ b/app/soc/views/profile_show.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2012 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for displaying the Profile read-only page.
-"""
-
+"""Module for displaying the Profile read-only page."""
from google.appengine.ext import db
@@ -27,8 +23,6 @@
from soc.views.template import Template
from soc.views.toggle_button import ToggleButtonTemplate
-from soc.modules.gsoc.views.base import RequestHandler
-
class UserReadOnlyTemplate(readonly_template.ModelReadOnlyTemplate):
"""Template to construct readonly Profile data.
@@ -105,7 +99,7 @@
def post(self):
assert isSet(self.data.url_profile)
-
+
value = self.data.POST.get('value')
profile_key = self.data.url_profile.key()
diff --git a/app/soc/views/site.py b/app/soc/views/site.py
index 52ea178..9e923ef 100644
--- a/app/soc/views/site.py
+++ b/app/soc/views/site.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python2.5
-#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,33 +12,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Module for the site global pages.
-"""
-
+"""Module for the site global pages."""
import os
from google.appengine.api import users
+from django import http
from django.conf.urls.defaults import url as django_url
from django.forms import widgets as django_widgets
from django.utils.functional import lazy
from django.utils.translation import ugettext
-from soc.models.document import Document
from soc.logic import cleaning
-from soc.logic import site
-from soc.logic.exceptions import AccessViolation
-from soc.logic.exceptions import Error
-from soc.models.site import Site
-from soc.views.base import Response
-from soc.views.base import SiteRequestHandler
-from soc.views.forms import ModelForm
-
+from soc.logic import exceptions
+from soc.logic import site as site_logic
+from soc.models import document
+from soc.models import site
+from soc.views import base
+from soc.views import forms as views_forms
from soc.modules import callback
-
DEF_NO_DEVELOPER = ugettext(
'This page is only accessible to developers.')
@@ -51,12 +44,11 @@
return choices
-class SiteForm(ModelForm):
- """Django form for the site settings.
- """
+class SiteForm(views_forms.ModelForm):
+ """Django form for the site settings."""
class Meta:
- model = Site
+ model = site.Site
exclude = ['link_id', 'scope', 'scope_path', 'home', 'xsrf_secret_key']
widgets = {
'active_program': django_widgets.Select(
@@ -74,9 +66,8 @@
clean_noreply_email = cleaning.clean_empty_field('noreply_email')
-class EditSitePage(SiteRequestHandler):
- """View for the participant profile.
- """
+class EditSitePage(base.SiteRequestHandler):
+ """View for the participant profile."""
def djangoURLPatterns(self):
return [
@@ -84,7 +75,7 @@
]
def jsonContext(self):
- entities = Document.all().filter('prefix', 'site')
+ entities = document.Document.all().filter('prefix', 'site')
data = [{'key': str(i.key()),
'link_id': i.link_id,
@@ -95,7 +86,7 @@
def checkAccess(self):
if not self.data.is_developer:
- raise AccessViolation(DEF_NO_DEVELOPER)
+ raise exceptions.AccessViolation(DEF_NO_DEVELOPER)
def templatePath(self):
# TODO: make this specific to the current active program
@@ -123,17 +114,15 @@
site_form.save()
def post(self):
- """Handler for HTTP POST request.
- """
+ """Handler for HTTP POST request."""
if self.validate():
self.redirect.to('edit_site_settings')
else:
self.get()
-class SiteHomepage(SiteRequestHandler):
- """View for the site home page.
- """
+class SiteHomepage(base.SiteRequestHandler):
+ """View for the site home page."""
def djangoURLPatterns(self):
return [
@@ -143,29 +132,31 @@
]
def __call__(self, request, *args, **kwargs):
- """Custom call implementation.
-
- This avoids looking up unneeded data.
- """
- self.response = Response()
-
+ """Custom call implementation that avoids looking up unneeded data."""
+ # TODO(nathaniel): eliminate this - the RedirectHelper (self.redirect)
+ # should simply return a newly-crafted HttpResponse.
+ # TODO(nathaniel): this blocks (and is part of) issue 1665.
+ self.response = http.HttpResponse()
try:
self.init(request, args, kwargs)
-
+
action = args[0] if args else ''
-
+
if action == 'login':
self.redirect.toUrl(users.create_login_url('/'))
elif action == 'logout':
self.redirect.toUrl(users.create_logout_url('/'))
else:
- settings = site.singleton()
+ settings = site_logic.singleton()
program = settings.active_program
if program:
- self.redirect.program(program).to(program.homepage_url_name)
+ # TODO(nathaniel): make this .program call unnecessary.
+ self.redirect.program(program=program)
+
+ self.redirect.to(program.homepage_url_name)
else:
self.redirect.to('edit_site_settings')
- except Error, e:
- self.error(e.status, message=e.args[0])
- return self.response
+ return self.response
+ except exceptions.Error, e:
+ return self.error(e.status, message=e.args[0])
diff --git a/app/soc/views/sitemap/__init__.py b/app/soc/views/sitemap/__init__.py
deleted file mode 100644
index 79b8182..0000000
--- a/app/soc/views/sitemap/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Copyright 2008 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.
-
-"""This module contains sitemap and sidebar related submodules."""
\ No newline at end of file
diff --git a/app/soc/views/sitemap/sitemap.py b/app/soc/views/sitemap/sitemap.py
deleted file mode 100644
index 245ba1c..0000000
--- a/app/soc/views/sitemap/sitemap.py
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env python2.5
-#
-# Copyright 2008 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 contains sidemap related functions.
-"""
-
-
-def getDjangoURLPatterns(params):
- """Retrieves a list of sidebar entries for this View.
-
- Params usage:
- The params dictionary is passed to the getKeyFieldsPatterns
- method, see it's docstring on how it is used.
-
- django_patterns: The django_patterns value is returned directly
- if it is non-False.
- django_patterns_defaults: The dajngo_patterns_defaults value is
- used to construct the url patterns. It is expected to be a
- list of tuples. The tuples should contain an url, a module
- name, and the name of the url. The name is used as the
- page_name passed as keyword argument, but also as the name
- by which the url is known to Django internally.
- url_name: The url_name argument is passed as argument to each
- url, together with the link_id pattern, the link_id core
- pattern, and the key fields for this View.
-
- Args:
- params: a dict with params for this View
- """
-
- # Return the found result
- if params['django_patterns']:
- return params['django_patterns']
-
- # Construct defaults manualy
- default_django_patterns = params['django_patterns_defaults']
- default_patterns = default_django_patterns[:]
- default_patterns += params['extra_django_patterns']
-
- patterns = []
-
- for url, module, name in default_patterns:
- name = name % params
- module = module % params
-
- url = url % {
- 'url_name': params['url_name'],
- 'lnp': params['link_id_arg_pattern'],
- 'ulnp': params['link_id_pattern_core'],
- 'key_fields': params['key_fields_pattern'],
- 'scope': params['scope_path_pattern'],
- 'sans_link_id': params['sans_link_id_pattern'],
- }
-
- kwargs = {'page_name': name}
-
- item = (url, module, kwargs, name)
- patterns.append(item)
-
- return patterns
diff --git a/tests/app/soc/modules/gci/views/helper/test_redirect_helper.py b/tests/app/soc/modules/gci/views/helper/test_redirect_helper.py
index e2c00b4..18fc6c4 100644
--- a/tests/app/soc/modules/gci/views/helper/test_redirect_helper.py
+++ b/tests/app/soc/modules/gci/views/helper/test_redirect_helper.py
@@ -3,9 +3,9 @@
# 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.
@@ -14,11 +14,10 @@
"""This module unit tests for RedirectHelper class."""
-
from soc.views.helper.access_checker import unset
from soc.views.helper.response import Response
-from soc.modules.gci.views.base import RequestHandler
+from soc.modules.gci.views.base import GCIRequestHandler
from soc.modules.gci.views.helper import url_names
from tests.test_utils import GCITestCase
@@ -33,7 +32,7 @@
self.init()
request = MockRequest(path="/")
- self.handler = RequestHandler()
+ self.handler = GCIRequestHandler()
self.handler.response = Response()
self.handler.init(request, (), {})
diff --git a/tests/app/soc/modules/gci/views/test_task.py b/tests/app/soc/modules/gci/views/test_task.py
index 1d4c4f4..5339096 100644
--- a/tests/app/soc/modules/gci/views/test_task.py
+++ b/tests/app/soc/modules/gci/views/test_task.py
@@ -131,6 +131,25 @@
self.assertResponseRedirect(response)
self.assertEqual(task.status, 'Unpublished')
+ def testPostButtonUnpublishReopenedTaskForbidden(self):
+ """Tests the unpublish button on.
+ """
+ self.data.createOrgAdmin(self.org)
+
+ url = self._taskPageUrl(self.task)
+
+ # try to unpublish a reopened task
+ task = GCITask.get(self.task.key())
+ task.status = 'Reopened'
+ task.put()
+
+ response = self.buttonPost(url, 'button_unpublish')
+
+ task = GCITask.get(self.task.key())
+
+ self.assertResponseForbidden(response)
+ self.assertEqual(task.status, 'Reopened')
+
def testPostButtonUnpublishByUserWithNoRole(self):
"""Tests the unpublish button by a user with no role.
"""
@@ -514,19 +533,43 @@
self.task.status = 'Claimed'
self.task.student = self.data.profile
# set deadline to far future
- self.task.deadline = datetime.datetime.utcnow() + datetime.timedelta(days=1)
+ self.task.deadline = datetime.datetime.utcnow() + \
+ datetime.timedelta(days=1)
self.task.put()
GCITaskHelper(self.program).createWorkSubmission(
self.task, self.data.profile)
- url = '%s?send_for_review' %self._taskPageUrl(self.task)
+ url = '%s?send_for_review' % self._taskPageUrl(self.task)
response = self.post(url)
task = GCITask.get(self.task.key())
self.assertResponseRedirect(response)
self.assertEqual(task.status, 'NeedsReview')
+ def testPostSendForReviewClosedTaskForbidden(self):
+ """Tests for submitting work for a task whose status is Closed.
+ """
+ self.data.createStudent()
+
+ self.task.status = 'Closed'
+ self.task.student = self.data.profile
+ # set deadline to far future
+ self.task.deadline = datetime.datetime.utcnow() + \
+ datetime.timedelta(days=1)
+ self.task.put()
+
+ GCITaskHelper(self.program).createWorkSubmission(
+ self.task, self.data.profile)
+
+ url = '%s?send_for_review' % self._taskPageUrl(self.task)
+ response = self.post(url)
+
+ self.assertResponseForbidden(response)
+
+ task = GCITask.get(self.task.key())
+ self.assertEqual(task.status, 'Closed')
+
def testPostDeleteSubmission(self):
"""Tests for deleting work.
"""