Index: appengine/monorail/registerpages.py |
diff --git a/appengine/monorail/registerpages.py b/appengine/monorail/registerpages.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1fba7d91d5737768aa7245f1d9cdadbd9758cc23 |
--- /dev/null |
+++ b/appengine/monorail/registerpages.py |
@@ -0,0 +1,336 @@ |
+# Copyright 2016 The Chromium Authors. All rights reserved. |
+# Use of this source code is govered by a BSD-style |
+# license that can be found in the LICENSE file or at |
+# https://developers.google.com/open-source/licenses/bsd |
+ |
+"""This file sets up all the urls for monorail pages.""" |
+ |
+import logging |
+ |
+import webapp2 |
+ |
+import settings |
+ |
+from features import autolink |
+from features import cues |
+from features import filterrules |
+from features import inboundemail |
+from features import notify |
+from features import savedqueries |
+from features import spammodel |
+from features import stars |
+ |
+from framework import artifactcollision |
+from framework import banned |
+from framework import csp_report |
+from framework import excessiveactivity |
+from framework import reap |
+from framework import registerpages_helpers |
+from framework import tokenrefresh |
+from framework import urls |
+ |
+from project import peopledetail |
+from project import peoplelist |
+from project import projectadmin |
+from project import projectadminadvanced |
+from project import projectexport |
+from project import projectsummary |
+from project import projectupdates |
+from project import wikiredirect |
+ |
+from search import backendnonviewable |
+from search import backendsearch |
+ |
+from services import cachemanager_svc |
+ |
+from sitewide import custom_404 |
+from sitewide import groupadmin |
+from sitewide import groupcreate |
+from sitewide import groupdetail |
+from sitewide import grouplist |
+from sitewide import hostinghome |
+from sitewide import moved |
+from sitewide import projectcreate |
+from sitewide import userprofile |
+from sitewide import userprojects |
+from sitewide import usersettings |
+from sitewide import userupdates |
+ |
+from tracker import componentcreate |
+from tracker import componentdetail |
+from tracker import fieldcreate |
+from tracker import fielddetail |
+from tracker import issueadmin |
+from tracker import issueadvsearch |
+from tracker import issueattachment |
+from tracker import issueattachmenttext |
+from tracker import issuebulkedit |
+from tracker import issuedetail |
+from tracker import issueentry |
+from tracker import issuelist |
+from tracker import issuelistcsv |
+from tracker import issueoptions |
+from tracker import issueoriginal |
+from tracker import issueexport |
+from tracker import issueimport |
+from tracker import issuereindex |
+from tracker import issuetips |
+from tracker import spam |
+ |
+ |
+class ServletRegistry(object): |
+ |
+ _PROJECT_NAME_REGEX = r'[a-z0-9][-a-z0-9]*[a-z0-9]' |
+ _USERNAME_REGEX = r'[-+\w=.%]+(@([a-z0-9]+\.)*[a-z0-9]+)?' |
+ |
+ def __init__(self): |
+ self.routes = [] |
+ |
+ def _AddRoute(self, path_regex, servlet_class, method, does_write=False): |
+ """Add a GET or POST handler to our webapp2 route list. |
+ |
+ Args: |
+ path_regex: string with webapp2 URL template regex. |
+ servlet_class: a subclass of class Servlet. |
+ method: string 'GET' or 'POST'. |
+ does_write: True if the servlet could write to the database, we skip |
+ registering such servlets when the site is in read_only mode. GET |
+ handlers never write. Most, but not all, POST handlers do write. |
+ """ |
+ if settings.read_only and does_write: |
+ logging.info('Not registring %r because site is read-only', path_regex) |
+ # TODO(jrobbins): register a helpful error page instead. |
+ else: |
+ self.routes.append( |
+ webapp2.Route(path_regex, handler=servlet_class, methods=[method])) |
+ |
+ def _SetupServlets(self, spec_dict, base='', post_does_write=True): |
+ """Register each of the given servlets.""" |
+ for get_uri, servlet_class in spec_dict.items(): |
+ self._AddRoute(base + get_uri, servlet_class, 'GET') |
+ post_uri = get_uri + ('edit.do' if get_uri.endswith('/') else '.do') |
+ self._AddRoute(base + post_uri, servlet_class, 'POST', |
+ does_write=post_does_write) |
+ |
+ def _SetupProjectServlets(self, spec_dict, post_does_write=True): |
+ """Register each of the given servlets in the project URI space.""" |
+ self._SetupServlets( |
+ spec_dict, base='/p/<project_name:%s>' % self._PROJECT_NAME_REGEX, |
+ post_does_write=post_does_write) |
+ |
+ def _SetupUserServlets(self, spec_dict, post_does_write=True): |
+ """Register each of the given servlets in the user URI space.""" |
+ self._SetupServlets( |
+ spec_dict, base='/u/<viewed_username:%s>' % self._USERNAME_REGEX, |
+ post_does_write=post_does_write) |
+ |
+ def _SetupGroupServlets(self, spec_dict, post_does_write=True): |
+ """Register each of the given servlets in the user group URI space.""" |
+ self._SetupServlets( |
+ spec_dict, base='/g/<viewed_username:%s>' % self._USERNAME_REGEX, |
+ post_does_write=post_does_write) |
+ |
+ def Register(self, services): |
+ """Register all the monorail request handlers.""" |
+ self._RegisterFrameworkHandlers() |
+ self._RegisterSitewideHandlers() |
+ self._RegisterProjectHandlers() |
+ self._RegisterIssueHandlers() |
+ self._RegisterRedirects() |
+ self._RegisterInboundMail() |
+ autolink.RegisterAutolink(services) |
+ # Error pages should be the last to register. |
+ self._RegisterErrorPages() |
+ logging.info('Finished registering monorail handlers.') |
+ return self.routes |
+ |
+ def _RegisterProjectHandlers(self): |
+ """Register page and form handlers that operate within a project.""" |
+ self._SetupProjectServlets({ |
+ urls.ADMIN_INTRO: projectsummary.ProjectSummary, |
+ urls.PEOPLE_LIST: peoplelist.PeopleList, |
+ urls.PEOPLE_DETAIL: peopledetail.PeopleDetail, |
+ urls.PEOPLE_DETAIL_PREFS_JSON: peopledetail.PagePrefs, |
+ urls.UPDATES_LIST: projectupdates.ProjectUpdates, |
+ urls.ADMIN_META: projectadmin.ProjectAdmin, |
+ urls.ADMIN_ADVANCED: projectadminadvanced.ProjectAdminAdvanced, |
+ urls.ADMIN_EXPORT: projectexport.ProjectExport, |
+ urls.ADMIN_EXPORT_JSON: projectexport.ProjectExportJSON, |
+ }) |
+ |
+ def _RegisterIssueHandlers(self): |
+ """Register page and form handlers for the issue tracker.""" |
+ self._SetupServlets({ |
+ # Note: there is both a site-wide and per-project issue list. |
+ urls.ISSUE_LIST: issuelist.IssueList, |
+ |
+ # Note: the following are at URLs that are not externaly accessible. |
+ urls.BACKEND_SEARCH: backendsearch.BackendSearch, |
+ urls.BACKEND_NONVIEWABLE: backendnonviewable.BackendNonviewable, |
+ urls.RECOMPUTE_DERIVED_FIELDS_TASK: |
+ filterrules.RecomputeDerivedFieldsTask, |
+ urls.REINDEX_QUEUE_CRON: filterrules.ReindexQueueCron, |
+ urls.NOTIFY_ISSUE_CHANGE_TASK: notify.NotifyIssueChangeTask, |
+ urls.NOTIFY_BLOCKING_CHANGE_TASK: notify.NotifyBlockingChangeTask, |
+ urls.NOTIFY_BULK_CHANGE_TASK: notify.NotifyBulkChangeTask, |
+ urls.OUTBOUND_EMAIL_TASK: notify.OutboundEmailTask, |
+ urls.SPAM_DATA_EXPORT_TASK: spammodel.TrainingDataExportTask, |
+ }) |
+ |
+ self._SetupProjectServlets({ |
+ urls.ISSUE_LIST: issuelist.IssueList, |
+ urls.ISSUE_LIST_CSV: issuelistcsv.IssueListCsv, |
+ urls.ISSUE_REINDEX: issuereindex.IssueReindex, |
+ urls.ISSUE_DETAIL: issuedetail.IssueDetail, |
+ urls.ISSUE_COMMENT_DELETION_JSON: issuedetail.IssueCommentDeletion, |
+ urls.ISSUE_ATTACHMENT_DELETION_JSON: |
+ issueattachment.IssueAttachmentDeletion, |
+ urls.ISSUE_FLAGSPAM_JSON: spam.FlagSpamForm, |
+ urls.ISSUE_SETSTAR_JSON: issuedetail.SetStarForm, |
+ urls.ISSUE_DELETE_JSON: issuedetail.IssueDeleteForm, |
+ urls.ISSUE_ENTRY: issueentry.IssueEntry, |
+ urls.ISSUE_OPTIONS_JSON: issueoptions.IssueOptionsJSON, |
+ urls.ISSUE_TIPS: issuetips.IssueSearchTips, |
+ urls.ISSUE_ATTACHMENT: issueattachment.AttachmentPage, |
+ urls.ISSUE_ATTACHMENT_TEXT: issueattachmenttext.AttachmentText, |
+ urls.ISSUE_BULK_EDIT: issuebulkedit.IssueBulkEdit, |
+ urls.COMPONENT_CHECKNAME_JSON: componentcreate.CheckComponentNameJSON, |
+ urls.COMPONENT_CREATE: componentcreate.ComponentCreate, |
+ urls.COMPONENT_DETAIL: componentdetail.ComponentDetail, |
+ urls.FIELD_CHECKNAME_JSON: fieldcreate.CheckFieldNameJSON, |
+ urls.FIELD_CREATE: fieldcreate.FieldCreate, |
+ urls.FIELD_DETAIL: fielddetail.FieldDetail, |
+ urls.WIKI_LIST: wikiredirect.WikiRedirect, |
+ urls.WIKI_PAGE: wikiredirect.WikiRedirect, |
+ urls.ADMIN_STATUSES: issueadmin.AdminStatuses, |
+ urls.ADMIN_LABELS: issueadmin.AdminLabels, |
+ urls.ADMIN_RULES: issueadmin.AdminRules, |
+ urls.ADMIN_TEMPLATES: issueadmin.AdminTemplates, |
+ urls.ADMIN_COMPONENTS: issueadmin.AdminComponents, |
+ urls.ADMIN_VIEWS: issueadmin.AdminViews, |
+ urls.ISSUE_ORIGINAL: issueoriginal.IssueOriginal, |
+ urls.ISSUE_EXPORT: issueexport.IssueExport, |
+ urls.ISSUE_EXPORT_JSON: issueexport.IssueExportJSON, |
+ urls.ISSUE_IMPORT: issueimport.IssueImport, |
+ urls.SPAM_MODERATION_QUEUE: spam.ModerationQueue, |
+ }) |
+ |
+ self._SetupUserServlets({ |
+ urls.SAVED_QUERIES: savedqueries.SavedQueries, |
+ }) |
+ |
+ # These servlets accept POST, but never write to the database, so they can |
+ # still be used when the site is read-only. |
+ self._SetupProjectServlets({ |
+ urls.ISSUE_ADVSEARCH: issueadvsearch.IssueAdvancedSearch, |
+ }, post_does_write=False) |
+ |
+ list_redir = registerpages_helpers.MakeRedirectInScope( |
+ urls.ISSUE_LIST, 'p') |
+ self._SetupProjectServlets({ |
+ '': list_redir, |
+ '/': list_redir, |
+ '/issues': list_redir, |
+ '/issues/': list_redir, |
+ }) |
+ |
+ list_redir = registerpages_helpers.MakeRedirect(urls.ISSUE_LIST) |
+ self._SetupServlets({ |
+ '/issues': list_redir, |
+ '/issues/': list_redir, |
+ }) |
+ |
+ def _RegisterFrameworkHandlers(self): |
+ """Register page and form handlers for framework functionality.""" |
+ self._SetupServlets({ |
+ urls.CSP_REPORT: csp_report.CSPReportPage, |
+ urls.TOKEN_REFRESH: tokenrefresh.TokenRefresh, |
+ |
+ # These are only shown to users iff specific conditions are met. |
+ urls.NONPROJECT_COLLISION: artifactcollision.ArtifactCollision, |
+ urls.EXCESSIVE_ACTIVITY: excessiveactivity.ExcessiveActivity, |
+ urls.BANNED: banned.Banned, |
+ urls.PROJECT_MOVED: moved.ProjectMoved, |
+ |
+ # These are not externally accessible |
+ urls.RAMCACHE_CONSOLIDATE_CRON: cachemanager_svc.RamCacheConsolidate, |
+ urls.REAP_CRON: reap.Reap, |
+ urls.SPAM_DATA_EXPORT_CRON: spammodel.TrainingDataExport, |
+ }) |
+ |
+ self._SetupProjectServlets({ |
+ # Collisions can happen on artifacts within a project or outside. |
+ urls.ARTIFACT_COLLISION: artifactcollision.ArtifactCollision, |
+ }) |
+ |
+ def _RegisterSitewideHandlers(self): |
+ """Register page and form handlers that aren't associated with projects.""" |
+ self._SetupServlets({ |
+ urls.PROJECT_CREATE: projectcreate.ProjectCreate, |
+ urls.CHECK_PROJECT_NAME_JSON: projectcreate.CheckProjectNameJSON, |
+ # The user settings page is a site-wide servlet, not under /u/. |
+ urls.USER_SETTINGS: usersettings.UserSettings, |
+ urls.USER_PROJECTS_JSON: userprojects.ProjectsJsonFeed, |
+ urls.HOSTING_HOME: hostinghome.HostingHome, |
+ urls.STARS_JSON: stars.SetStarsFeed, |
+ urls.CUES_JSON: cues.SetCuesFeed, |
+ urls.GROUP_CREATE: groupcreate.GroupCreate, |
+ urls.GROUP_LIST: grouplist.GroupList, |
+ urls.GROUP_DELETE: grouplist.GroupList, |
+ }) |
+ |
+ self._SetupUserServlets({ |
+ urls.USER_PROFILE: userprofile.UserProfile, |
+ urls.USER_UPDATES_PROJECTS: userupdates.UserUpdatesProjects, |
+ urls.USER_UPDATES_DEVELOPERS: userupdates.UserUpdatesDevelopers, |
+ urls.USER_UPDATES_MINE: userupdates.UserUpdatesIndividual, |
+ }) |
+ |
+ profile_redir = registerpages_helpers.MakeRedirectInScope( |
+ urls.USER_PROFILE, 'u') |
+ self._SetupUserServlets({'': profile_redir}) |
+ |
+ self._SetupGroupServlets({ |
+ urls.GROUP_DETAIL: groupdetail.GroupDetail, |
+ urls.GROUP_ADMIN: groupadmin.GroupAdmin, |
+ }) |
+ |
+ def _RegisterRedirects(self): |
+ """Register redirects among pages inside monorail.""" |
+ redirect = registerpages_helpers.MakeRedirect('/hosting/') |
+ self._SetupServlets({ |
+ '/hosting': redirect, |
+ '/p': redirect, |
+ '/p/': redirect, |
+ '/u': redirect, |
+ '/u/': redirect, |
+ '/': redirect, |
+ }) |
+ |
+ redirect = registerpages_helpers.MakeRedirectInScope( |
+ urls.PEOPLE_LIST, 'p') |
+ self._SetupProjectServlets({ |
+ '/people': redirect, |
+ '/people/': redirect, |
+ }) |
+ |
+ redirect = registerpages_helpers.MakeRedirect(urls.GROUP_LIST) |
+ self._SetupServlets({'/g': redirect}) |
+ |
+ group_redir = registerpages_helpers.MakeRedirectInScope( |
+ urls.USER_PROFILE, 'g') |
+ self._SetupGroupServlets({'': group_redir}) |
+ |
+ def _RegisterInboundMail(self): |
+ """Register a handler for inbound email.""" |
+ self.routes.append(webapp2.Route( |
+ '/_ah/mail/<project_addr:.+>', |
+ handler=inboundemail.InboundEmail, |
+ methods=['POST', 'GET'])) |
+ |
+ def _RegisterErrorPages(self): |
+ """Register handlers for errors.""" |
+ self._AddRoute( |
+ '/p/<project_name:%s>/<unrecognized:.+>' % self._PROJECT_NAME_REGEX, |
+ custom_404.ErrorPage, 'GET') |
+ |