Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 1 | # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style |
| 3 | # license that can be found in the LICENSE file or at |
| 4 | # https://developers.google.com/open-source/licenses/bsd |
| 5 | |
| 6 | """This file sets up all the urls for monorail pages.""" |
| 7 | from __future__ import print_function |
| 8 | from __future__ import division |
| 9 | from __future__ import absolute_import |
| 10 | |
| 11 | import logging |
| 12 | import webapp2 |
| 13 | import settings |
| 14 | |
| 15 | from components import prpc |
| 16 | |
| 17 | from features import autolink |
| 18 | from features import dateaction |
| 19 | from features import banspammer |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 20 | from features import hotlistdetails |
| 21 | from features import hotlistissues |
| 22 | from features import hotlistissuescsv |
| 23 | from features import hotlistpeople |
| 24 | from features import filterrules |
| 25 | from features import pubsub |
| 26 | from features import userhotlists |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 27 | from features import notify |
| 28 | from features import rerankhotlist |
| 29 | from features import savedqueries |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 30 | |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 31 | from framework import csp_report |
| 32 | from framework import deleteusers |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 33 | from framework import trimvisitedpages |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 34 | from framework import reap |
| 35 | from framework import registerpages_helpers |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 36 | from framework import urls |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 37 | |
| 38 | from project import peopledetail |
| 39 | from project import peoplelist |
| 40 | from project import project_constants |
| 41 | from project import projectadmin |
| 42 | from project import projectadminadvanced |
| 43 | from project import projectexport |
| 44 | from project import projectsummary |
| 45 | from project import projectupdates |
| 46 | from project import redirects |
| 47 | |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 48 | from services import cachemanager_svc |
| 49 | from services import client_config_svc |
| 50 | |
| 51 | from sitewide import custom_404 |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 52 | from sitewide import userprofile |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 53 | from sitewide import userclearbouncing |
| 54 | from sitewide import userupdates |
| 55 | |
| 56 | from tracker import componentcreate |
| 57 | from tracker import componentdetail |
| 58 | from tracker import fieldcreate |
| 59 | from tracker import fielddetail |
| 60 | from tracker import issueadmin |
| 61 | from tracker import issueadvsearch |
| 62 | from tracker import issueattachment |
| 63 | from tracker import issueattachmenttext |
| 64 | from tracker import issuebulkedit |
| 65 | from tracker import webcomponentspage |
| 66 | from tracker import issuedetailezt |
| 67 | from tracker import issueentry |
| 68 | from tracker import issueentryafterlogin |
| 69 | from tracker import issueexport |
| 70 | from tracker import issueimport |
| 71 | from tracker import issueoriginal |
| 72 | from tracker import issuereindex |
| 73 | from tracker import issuetips |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 74 | from tracker import templatecreate |
| 75 | from tracker import templatedetail |
| 76 | from tracker import fltconversion |
| 77 | |
| 78 | from api import api_routes as api_routes_v0 |
| 79 | from api.v3 import api_routes as api_routes_v3 |
| 80 | |
| 81 | |
| 82 | class ServletRegistry(object): |
| 83 | |
| 84 | _PROJECT_NAME_REGEX = project_constants.PROJECT_NAME_PATTERN |
| 85 | _USERNAME_REGEX = r'[-+\w=.%]+(@([-a-z0-9]+\.)*[a-z0-9]+)?' |
| 86 | _HOTLIST_ID_NAME_REGEX = r'\d+|[a-zA-Z][-0-9a-zA-Z\.]*' |
| 87 | |
| 88 | def __init__(self): |
| 89 | self.routes = [] |
| 90 | |
| 91 | def _AddRoute(self, path_regex, servlet_class, method, does_write=False): |
| 92 | """Add a GET or POST handler to our webapp2 route list. |
| 93 | |
| 94 | Args: |
| 95 | path_regex: string with webapp2 URL template regex. |
| 96 | servlet_class: a subclass of class Servlet. |
| 97 | method: string 'GET' or 'POST'. |
| 98 | does_write: True if the servlet could write to the database, we skip |
| 99 | registering such servlets when the site is in read_only mode. GET |
| 100 | handlers never write. Most, but not all, POST handlers do write. |
| 101 | """ |
| 102 | if settings.read_only and does_write: |
| 103 | logging.info('Not registring %r because site is read-only', path_regex) |
| 104 | # TODO(jrobbins): register a helpful error page instead. |
| 105 | else: |
| 106 | self.routes.append( |
| 107 | webapp2.Route(path_regex, handler=servlet_class, methods=[method])) |
| 108 | |
| 109 | def _SetupServlets(self, spec_dict, base='', post_does_write=True): |
| 110 | """Register each of the given servlets.""" |
| 111 | for get_uri, servlet_class in spec_dict.items(): |
| 112 | self._AddRoute(base + get_uri, servlet_class, 'GET') |
| 113 | post_uri = get_uri + ('edit.do' if get_uri.endswith('/') else '.do') |
| 114 | self._AddRoute(base + post_uri, servlet_class, 'POST', |
| 115 | does_write=post_does_write) |
| 116 | |
| 117 | def _SetupProjectServlets(self, spec_dict, post_does_write=True): |
| 118 | """Register each of the given servlets in the project URI space.""" |
| 119 | self._SetupServlets( |
| 120 | spec_dict, base='/p/<project_name:%s>' % self._PROJECT_NAME_REGEX, |
| 121 | post_does_write=post_does_write) |
| 122 | |
| 123 | def _SetupUserServlets(self, spec_dict, post_does_write=True): |
| 124 | """Register each of the given servlets in the user URI space.""" |
| 125 | self._SetupServlets( |
| 126 | spec_dict, base='/u/<viewed_username:%s>' % self._USERNAME_REGEX, |
| 127 | post_does_write=post_does_write) |
| 128 | |
| 129 | def _SetupGroupServlets(self, spec_dict, post_does_write=True): |
| 130 | """Register each of the given servlets in the user group URI space.""" |
| 131 | self._SetupServlets( |
| 132 | spec_dict, base='/g/<viewed_username:%s>' % self._USERNAME_REGEX, |
| 133 | post_does_write=post_does_write) |
| 134 | |
| 135 | def _SetupUserHotlistServlets(self, spec_dict, post_does_write=True): |
| 136 | """ Register given user hotlist servlets in the user URI space.""" |
| 137 | self._SetupServlets( |
| 138 | spec_dict, |
| 139 | base ='/u/<viewed_username:%s>/hotlists/<hotlist_id:%s>' |
| 140 | % (self._USERNAME_REGEX, self._HOTLIST_ID_NAME_REGEX), |
| 141 | post_does_write=post_does_write) |
| 142 | |
| 143 | def Register(self, services): |
| 144 | """Register all the monorail request handlers.""" |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 145 | self._RegisterSitewideHandlers() |
| 146 | self._RegisterProjectHandlers() |
| 147 | self._RegisterIssueHandlers() |
| 148 | self._RegisterWebComponentsHanders() |
| 149 | self._RegisterRedirects() |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 150 | |
| 151 | # Register pRPC API routes |
| 152 | prpc_server = prpc.Server( |
| 153 | allowed_origins=client_config_svc.GetAllowedOriginsSet()) |
| 154 | api_routes_v0.RegisterApiHandlers(prpc_server, services) |
| 155 | api_routes_v3.RegisterApiHandlers(prpc_server, services) |
| 156 | self.routes.extend(prpc_server.get_routes()) |
| 157 | |
| 158 | autolink.RegisterAutolink(services) |
| 159 | # Error pages should be the last to register. |
| 160 | self._RegisterErrorPages() |
| 161 | return self.routes |
| 162 | |
| 163 | def _RegisterProjectHandlers(self): |
| 164 | """Register page and form handlers that operate within a project.""" |
| 165 | |
Adrià Vilanova MartÃnez | de94280 | 2022-07-15 14:06:55 +0200 | [diff] [blame] | 166 | self._SetupProjectServlets( |
| 167 | { |
| 168 | urls.ADMIN_INTRO: projectsummary.ProjectSummary, |
| 169 | urls.PEOPLE_LIST: peoplelist.PeopleList, |
| 170 | urls.PEOPLE_DETAIL: peopledetail.PeopleDetail, |
| 171 | urls.UPDATES_LIST: projectupdates.ProjectUpdates, |
| 172 | urls.ADMIN_META: projectadmin.ProjectAdmin, |
| 173 | urls.ADMIN_ADVANCED: projectadminadvanced.ProjectAdminAdvanced, |
| 174 | urls.ADMIN_EXPORT: projectexport.ProjectExport, |
| 175 | urls.ADMIN_EXPORT_JSON: projectexport.ProjectExportJSON, |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 176 | }) |
| 177 | |
| 178 | def _RegisterIssueHandlers(self): |
| 179 | """Register page and form handlers for the issue tracker.""" |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 180 | |
| 181 | self._SetupProjectServlets( |
| 182 | { |
| 183 | urls.ISSUE_APPROVAL: |
| 184 | registerpages_helpers.MakeRedirectInScope( |
| 185 | urls.ISSUE_DETAIL, 'p', keep_qs=True), |
| 186 | urls.ISSUE_LIST: |
| 187 | webcomponentspage.WebComponentsPage, |
| 188 | urls.ISSUE_LIST_NEW_TEMP: |
| 189 | registerpages_helpers.MakeRedirectInScope( |
| 190 | urls.ISSUE_LIST, 'p', keep_qs=True), |
| 191 | urls.ISSUE_REINDEX: |
| 192 | issuereindex.IssueReindex, |
| 193 | urls.ISSUE_DETAIL_FLIPPER_NEXT: |
| 194 | issuedetailezt.FlipperNext, |
| 195 | urls.ISSUE_DETAIL_FLIPPER_PREV: |
| 196 | issuedetailezt.FlipperPrev, |
| 197 | urls.ISSUE_DETAIL_FLIPPER_LIST: |
| 198 | issuedetailezt.FlipperList, |
| 199 | urls.ISSUE_DETAIL_FLIPPER_INDEX: |
| 200 | issuedetailezt.FlipperIndex, |
| 201 | urls.ISSUE_DETAIL_LEGACY: |
| 202 | registerpages_helpers.MakeRedirectInScope( |
| 203 | urls.ISSUE_DETAIL, 'p', keep_qs=True), |
| 204 | urls.ISSUE_WIZARD: |
| 205 | webcomponentspage.WebComponentsPage, |
| 206 | urls.ISSUE_ENTRY: |
| 207 | issueentry.IssueEntry, |
| 208 | urls.ISSUE_ENTRY_NEW: |
| 209 | webcomponentspage.WebComponentsPage, |
| 210 | urls.ISSUE_ENTRY_AFTER_LOGIN: |
| 211 | issueentryafterlogin.IssueEntryAfterLogin, |
| 212 | urls.ISSUE_TIPS: |
| 213 | issuetips.IssueSearchTips, |
| 214 | urls.ISSUE_ATTACHMENT: |
| 215 | issueattachment.AttachmentPage, |
| 216 | urls.ISSUE_ATTACHMENT_TEXT: |
| 217 | issueattachmenttext.AttachmentText, |
| 218 | urls.ISSUE_BULK_EDIT: |
| 219 | issuebulkedit.IssueBulkEdit, |
| 220 | urls.COMPONENT_CREATE: |
| 221 | componentcreate.ComponentCreate, |
| 222 | urls.COMPONENT_DETAIL: |
| 223 | componentdetail.ComponentDetail, |
| 224 | urls.FIELD_CREATE: |
| 225 | fieldcreate.FieldCreate, |
| 226 | urls.FIELD_DETAIL: |
| 227 | fielddetail.FieldDetail, |
| 228 | urls.TEMPLATE_CREATE: |
| 229 | templatecreate.TemplateCreate, |
| 230 | urls.TEMPLATE_DETAIL: |
| 231 | templatedetail.TemplateDetail, |
| 232 | urls.WIKI_LIST: |
| 233 | redirects.WikiRedirect, |
| 234 | urls.WIKI_PAGE: |
| 235 | redirects.WikiRedirect, |
| 236 | urls.SOURCE_PAGE: |
| 237 | redirects.SourceRedirect, |
| 238 | urls.ADMIN_STATUSES: |
| 239 | issueadmin.AdminStatuses, |
| 240 | urls.ADMIN_LABELS: |
| 241 | issueadmin.AdminLabels, |
| 242 | urls.ADMIN_RULES: |
| 243 | issueadmin.AdminRules, |
| 244 | urls.ADMIN_TEMPLATES: |
| 245 | issueadmin.AdminTemplates, |
| 246 | urls.ADMIN_COMPONENTS: |
| 247 | issueadmin.AdminComponents, |
| 248 | urls.ADMIN_VIEWS: |
| 249 | issueadmin.AdminViews, |
| 250 | urls.ISSUE_ORIGINAL: |
| 251 | issueoriginal.IssueOriginal, |
| 252 | urls.ISSUE_EXPORT: |
| 253 | issueexport.IssueExport, |
| 254 | urls.ISSUE_EXPORT_JSON: |
| 255 | issueexport.IssueExportJSON, |
| 256 | urls.ISSUE_IMPORT: |
| 257 | issueimport.IssueImport, |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 258 | }) |
| 259 | |
| 260 | # GETs for /issues/detail are now handled by the web components page. |
| 261 | base = '/p/<project_name:%s>' % self._PROJECT_NAME_REGEX |
| 262 | self._AddRoute(base + urls.ISSUE_DETAIL, |
| 263 | webcomponentspage.WebComponentsPage, 'GET') |
| 264 | |
| 265 | self._SetupUserServlets({ |
| 266 | urls.SAVED_QUERIES: savedqueries.SavedQueries, |
| 267 | urls.HOTLISTS: userhotlists.UserHotlists, |
| 268 | }) |
| 269 | |
| 270 | user_hotlists_redir = registerpages_helpers.MakeRedirectInScope( |
| 271 | urls.HOTLISTS, 'u', keep_qs=True) |
| 272 | self._SetupUserServlets({ |
| 273 | '/hotlists/': user_hotlists_redir, |
| 274 | }) |
| 275 | |
| 276 | # These servlets accept POST, but never write to the database, so they can |
| 277 | # still be used when the site is read-only. |
| 278 | self._SetupProjectServlets({ |
| 279 | urls.ISSUE_ADVSEARCH: issueadvsearch.IssueAdvancedSearch, |
| 280 | }, post_does_write=False) |
| 281 | |
| 282 | list_redir = registerpages_helpers.MakeRedirectInScope( |
| 283 | urls.ISSUE_LIST, 'p', keep_qs=True) |
| 284 | self._SetupProjectServlets({ |
| 285 | '': list_redir, |
| 286 | '/': list_redir, |
| 287 | '/issues': list_redir, |
| 288 | '/issues/': list_redir, |
| 289 | }) |
| 290 | |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 291 | def _RegisterSitewideHandlers(self): |
| 292 | """Register page and form handlers that aren't associated with projects.""" |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 293 | |
| 294 | self._SetupUserServlets({ |
| 295 | urls.USER_PROFILE: userprofile.UserProfile, |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 296 | urls.BAN_USER: userprofile.BanUser, |
| 297 | urls.BAN_SPAMMER: banspammer.BanSpammer, |
| 298 | urls.USER_CLEAR_BOUNCING: userclearbouncing.UserClearBouncing, |
| 299 | urls.USER_UPDATES_PROJECTS: userupdates.UserUpdatesProjects, |
| 300 | urls.USER_UPDATES_DEVELOPERS: userupdates.UserUpdatesDevelopers, |
| 301 | urls.USER_UPDATES_MINE: userupdates.UserUpdatesIndividual, |
| 302 | }) |
| 303 | |
Adrià Vilanova MartÃnez | de94280 | 2022-07-15 14:06:55 +0200 | [diff] [blame] | 304 | self._SetupUserHotlistServlets( |
| 305 | { |
| 306 | urls.HOTLIST_ISSUES: hotlistissues.HotlistIssues, |
| 307 | urls.HOTLIST_ISSUES_CSV: hotlistissuescsv.HotlistIssuesCsv, |
| 308 | urls.HOTLIST_PEOPLE: hotlistpeople.HotlistPeopleList, |
| 309 | urls.HOTLIST_DETAIL: hotlistdetails.HotlistDetails, |
| 310 | urls.HOTLIST_RERANK_JSON: rerankhotlist.RerankHotlistIssue, |
| 311 | }) |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 312 | |
| 313 | profile_redir = registerpages_helpers.MakeRedirectInScope( |
| 314 | urls.USER_PROFILE, 'u') |
| 315 | self._SetupUserServlets({'': profile_redir}) |
| 316 | |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 317 | def _RegisterWebComponentsHanders(self): |
| 318 | """Register page handlers that are handled by WebComponentsPage.""" |
| 319 | self._AddRoute('/', webcomponentspage.ProjectListPage, 'GET') |
| 320 | self._AddRoute( |
| 321 | '/hotlists<unused:.*>', webcomponentspage.WebComponentsPage, 'GET') |
| 322 | self._AddRoute('/users<unused:.*>', webcomponentspage.WebComponentsPage, |
| 323 | 'GET') |
| 324 | |
| 325 | def _RegisterRedirects(self): |
| 326 | """Register redirects among pages inside monorail.""" |
| 327 | redirect = registerpages_helpers.MakeRedirect('/') |
| 328 | self._SetupServlets( |
| 329 | { |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 330 | '/p': redirect, |
| 331 | '/p/': redirect, |
| 332 | '/u': redirect, |
| 333 | '/u/': redirect, |
| 334 | '/': redirect, |
| 335 | }) |
| 336 | |
| 337 | redirect = registerpages_helpers.MakeRedirectInScope( |
| 338 | urls.PEOPLE_LIST, 'p') |
| 339 | self._SetupProjectServlets({ |
| 340 | '/people': redirect, |
| 341 | '/people/': redirect, |
| 342 | }) |
| 343 | |
Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 344 | def _RegisterErrorPages(self): |
| 345 | """Register handlers for errors.""" |
| 346 | self._AddRoute( |
| 347 | '/p/<project_name:%s>/<unrecognized:.+>' % self._PROJECT_NAME_REGEX, |
| 348 | custom_404.ErrorPage, 'GET') |