| 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')
|
| +
|
|
|