Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/registerpages.py b/registerpages.py
new file mode 100644
index 0000000..db3dd75
--- /dev/null
+++ b/registerpages.py
@@ -0,0 +1,474 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed 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."""
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+
+import logging
+import webapp2
+import settings
+
+from components import prpc
+
+from features import autolink
+from features import dateaction
+from features import banspammer
+from features import hotlistcreate
+from features import hotlistdetails
+from features import hotlistissues
+from features import hotlistissuescsv
+from features import hotlistpeople
+from features import filterrules
+from features import pubsub
+from features import userhotlists
+from features import inboundemail
+from features import notify
+from features import rerankhotlist
+from features import savedqueries
+from features import spammodel
+from features import spamtraining
+from features import componentexport
+
+from framework import banned
+from framework import clientmon
+from framework import csp_report
+from framework import deleteusers
+from framework import excessiveactivity
+from framework import trimvisitedpages
+from framework import framework_bizobj
+from framework import reap
+from framework import registerpages_helpers
+from framework import ts_mon_js
+from framework import urls
+from framework import warmup
+
+from project import peopledetail
+from project import peoplelist
+from project import project_constants
+from project import projectadmin
+from project import projectadminadvanced
+from project import projectexport
+from project import projectsummary
+from project import projectupdates
+from project import redirects
+
+from search import backendnonviewable
+from search import backendsearch
+
+from services import cachemanager_svc
+from services import client_config_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 usersettings
+from sitewide import userclearbouncing
+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 webcomponentspage
+from tracker import issuedetailezt
+from tracker import issueentry
+from tracker import issueentryafterlogin
+from tracker import issueexport
+from tracker import issueimport
+from tracker import issueoriginal
+from tracker import issuereindex
+from tracker import issuetips
+from tracker import spam
+from tracker import templatecreate
+from tracker import templatedetail
+from tracker import fltconversion
+
+from api import api_routes as api_routes_v0
+from api.v3 import api_routes as api_routes_v3
+
+
+class ServletRegistry(object):
+
+ _PROJECT_NAME_REGEX = project_constants.PROJECT_NAME_PATTERN
+ _USERNAME_REGEX = r'[-+\w=.%]+(@([-a-z0-9]+\.)*[a-z0-9]+)?'
+ _HOTLIST_ID_NAME_REGEX = r'\d+|[a-zA-Z][-0-9a-zA-Z\.]*'
+
+ 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 _SetupUserHotlistServlets(self, spec_dict, post_does_write=True):
+ """ Register given user hotlist servlets in the user URI space."""
+ self._SetupServlets(
+ spec_dict,
+ base ='/u/<viewed_username:%s>/hotlists/<hotlist_id:%s>'
+ % (self._USERNAME_REGEX, self._HOTLIST_ID_NAME_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._RegisterWebComponentsHanders()
+ self._RegisterRedirects()
+ self._RegisterInboundMail()
+
+ # Register pRPC API routes
+ prpc_server = prpc.Server(
+ allowed_origins=client_config_svc.GetAllowedOriginsSet())
+ api_routes_v0.RegisterApiHandlers(prpc_server, services)
+ api_routes_v3.RegisterApiHandlers(prpc_server, services)
+ self.routes.extend(prpc_server.get_routes())
+
+ autolink.RegisterAutolink(services)
+ # Error pages should be the last to register.
+ self._RegisterErrorPages()
+ return self.routes
+
+ def _RegisterProjectHandlers(self):
+ """Register page and form handlers that operate within a project."""
+
+ self._SetupServlets({
+ # Note: the following are at URLS that are not externally accessible.
+ urls.NOTIFY_RULES_DELETED_TASK: notify.NotifyRulesDeletedTask,
+ })
+ self._SetupProjectServlets({
+ urls.ADMIN_INTRO: projectsummary.ProjectSummary,
+ urls.PEOPLE_LIST: peoplelist.PeopleList,
+ urls.PEOPLE_DETAIL: peopledetail.PeopleDetail,
+ 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: 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.NOTIFY_APPROVAL_CHANGE_TASK: notify.NotifyApprovalChangeTask,
+ urls.OUTBOUND_EMAIL_TASK: notify.OutboundEmailTask,
+ urls.SPAM_DATA_EXPORT_TASK: spammodel.TrainingDataExportTask,
+ urls.DATE_ACTION_CRON: dateaction.DateActionCron,
+ urls.SPAM_TRAINING_CRON: spamtraining.TrainSpamModelCron,
+ urls.PUBLISH_PUBSUB_ISSUE_CHANGE_TASK:
+ pubsub.PublishPubsubIssueChangeTask,
+ urls.ISSUE_DATE_ACTION_TASK: dateaction.IssueDateActionTask,
+ urls.COMPONENT_DATA_EXPORT_CRON:
+ componentexport.ComponentTrainingDataExport,
+ urls.COMPONENT_DATA_EXPORT_TASK:
+ componentexport.ComponentTrainingDataExportTask,
+ urls.FLT_ISSUE_CONVERSION_TASK: fltconversion.FLTConvertTask,
+ })
+
+ self._SetupProjectServlets(
+ {
+ urls.ISSUE_APPROVAL:
+ registerpages_helpers.MakeRedirectInScope(
+ urls.ISSUE_DETAIL, 'p', keep_qs=True),
+ urls.ISSUE_LIST:
+ webcomponentspage.WebComponentsPage,
+ urls.ISSUE_LIST_NEW_TEMP:
+ registerpages_helpers.MakeRedirectInScope(
+ urls.ISSUE_LIST, 'p', keep_qs=True),
+ urls.ISSUE_REINDEX:
+ issuereindex.IssueReindex,
+ urls.ISSUE_DETAIL_FLIPPER_NEXT:
+ issuedetailezt.FlipperNext,
+ urls.ISSUE_DETAIL_FLIPPER_PREV:
+ issuedetailezt.FlipperPrev,
+ urls.ISSUE_DETAIL_FLIPPER_LIST:
+ issuedetailezt.FlipperList,
+ urls.ISSUE_DETAIL_FLIPPER_INDEX:
+ issuedetailezt.FlipperIndex,
+ urls.ISSUE_DETAIL_LEGACY:
+ registerpages_helpers.MakeRedirectInScope(
+ urls.ISSUE_DETAIL, 'p', keep_qs=True),
+ urls.ISSUE_WIZARD:
+ webcomponentspage.WebComponentsPage,
+ urls.ISSUE_ENTRY:
+ issueentry.IssueEntry,
+ urls.ISSUE_ENTRY_NEW:
+ webcomponentspage.WebComponentsPage,
+ urls.ISSUE_ENTRY_AFTER_LOGIN:
+ issueentryafterlogin.IssueEntryAfterLogin,
+ urls.ISSUE_TIPS:
+ issuetips.IssueSearchTips,
+ urls.ISSUE_ATTACHMENT:
+ issueattachment.AttachmentPage,
+ urls.ISSUE_ATTACHMENT_TEXT:
+ issueattachmenttext.AttachmentText,
+ urls.ISSUE_BULK_EDIT:
+ issuebulkedit.IssueBulkEdit,
+ urls.COMPONENT_CREATE:
+ componentcreate.ComponentCreate,
+ urls.COMPONENT_DETAIL:
+ componentdetail.ComponentDetail,
+ urls.FIELD_CREATE:
+ fieldcreate.FieldCreate,
+ urls.FIELD_DETAIL:
+ fielddetail.FieldDetail,
+ urls.TEMPLATE_CREATE:
+ templatecreate.TemplateCreate,
+ urls.TEMPLATE_DETAIL:
+ templatedetail.TemplateDetail,
+ urls.WIKI_LIST:
+ redirects.WikiRedirect,
+ urls.WIKI_PAGE:
+ redirects.WikiRedirect,
+ urls.SOURCE_PAGE:
+ redirects.SourceRedirect,
+ 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,
+ })
+
+ # GETs for /issues/detail are now handled by the web components page.
+ base = '/p/<project_name:%s>' % self._PROJECT_NAME_REGEX
+ self._AddRoute(base + urls.ISSUE_DETAIL,
+ webcomponentspage.WebComponentsPage, 'GET')
+
+ self._SetupUserServlets({
+ urls.SAVED_QUERIES: savedqueries.SavedQueries,
+ urls.HOTLISTS: userhotlists.UserHotlists,
+ })
+
+ user_hotlists_redir = registerpages_helpers.MakeRedirectInScope(
+ urls.HOTLISTS, 'u', keep_qs=True)
+ self._SetupUserServlets({
+ '/hotlists/': user_hotlists_redir,
+ })
+
+ # 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', keep_qs=True)
+ 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,
+
+ # These are only shown to users if specific conditions are met.
+ 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,
+ urls.LOAD_API_CLIENT_CONFIGS_CRON: (
+ client_config_svc.LoadApiClientConfigs),
+ urls.CLIENT_MON: clientmon.ClientMonitor,
+ urls.TRIM_VISITED_PAGES_CRON: trimvisitedpages.TrimVisitedPages,
+ urls.TS_MON_JS: ts_mon_js.MonorailTSMonJSHandler,
+ urls.WARMUP: warmup.Warmup,
+ urls.START: warmup.Start,
+ urls.STOP: warmup.Stop
+ })
+
+ def _RegisterSitewideHandlers(self):
+ """Register page and form handlers that aren't associated with projects."""
+ self._SetupServlets({
+ urls.PROJECT_CREATE: projectcreate.ProjectCreate,
+ # The user settings page is a site-wide servlet, not under /u/.
+ urls.USER_SETTINGS: usersettings.UserSettings,
+ urls.HOSTING_HOME: hostinghome.HostingHome,
+ urls.GROUP_CREATE: groupcreate.GroupCreate,
+ urls.GROUP_LIST: grouplist.GroupList,
+ urls.GROUP_DELETE: grouplist.GroupList,
+ urls.HOTLIST_CREATE: hotlistcreate.HotlistCreate,
+ urls.BAN_SPAMMER_TASK: banspammer.BanSpammerTask,
+ urls.WIPEOUT_SYNC_CRON: deleteusers.WipeoutSyncCron,
+ urls.SEND_WIPEOUT_USER_LISTS_TASK: deleteusers.SendWipeoutUserListsTask,
+ urls.DELETE_WIPEOUT_USERS_TASK: deleteusers.DeleteWipeoutUsersTask,
+ urls.DELETE_USERS_TASK: deleteusers.DeleteUsersTask,
+ })
+
+ self._SetupUserServlets({
+ urls.USER_PROFILE: userprofile.UserProfile,
+ urls.USER_PROFILE_POLYMER: userprofile.UserProfilePolymer,
+ urls.BAN_USER: userprofile.BanUser,
+ urls.BAN_SPAMMER: banspammer.BanSpammer,
+ urls.USER_CLEAR_BOUNCING: userclearbouncing.UserClearBouncing,
+ urls.USER_UPDATES_PROJECTS: userupdates.UserUpdatesProjects,
+ urls.USER_UPDATES_DEVELOPERS: userupdates.UserUpdatesDevelopers,
+ urls.USER_UPDATES_MINE: userupdates.UserUpdatesIndividual,
+ })
+
+ self._SetupUserHotlistServlets({
+ urls.HOTLIST_ISSUES: hotlistissues.HotlistIssues,
+ urls.HOTLIST_ISSUES_CSV: hotlistissuescsv.HotlistIssuesCsv,
+ urls.HOTLIST_PEOPLE: hotlistpeople.HotlistPeopleList,
+ urls.HOTLIST_DETAIL: hotlistdetails.HotlistDetails,
+ urls.HOTLIST_RERANK_JSON: rerankhotlist.RerankHotlistIssue,
+ })
+
+ 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 _RegisterWebComponentsHanders(self):
+ """Register page handlers that are handled by WebComponentsPage."""
+ self._AddRoute('/', webcomponentspage.ProjectListPage, 'GET')
+ self._AddRoute(
+ '/hotlists<unused:.*>', webcomponentspage.WebComponentsPage, 'GET')
+ self._AddRoute('/users<unused:.*>', webcomponentspage.WebComponentsPage,
+ 'GET')
+
+ def _RegisterRedirects(self):
+ """Register redirects among pages inside monorail."""
+ redirect = registerpages_helpers.MakeRedirect('/')
+ self._SetupServlets(
+ {
+ '/projects/': redirect,
+ '/projects': redirect,
+ '/hosting/': redirect,
+ '/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 and email bounces."""
+ self.routes.append(webapp2.Route(
+ '/_ah/mail/<project_addr:.+>',
+ handler=inboundemail.InboundEmail,
+ methods=['POST', 'GET']))
+ self.routes.append(webapp2.Route(
+ '/_ah/bounce',
+ handler=inboundemail.BouncedEmail,
+ 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')