OLD | NEW |
(Empty) | |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is govered 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 |
| 8 import logging |
| 9 |
| 10 import webapp2 |
| 11 |
| 12 import settings |
| 13 |
| 14 from features import autolink |
| 15 from features import cues |
| 16 from features import filterrules |
| 17 from features import inboundemail |
| 18 from features import notify |
| 19 from features import savedqueries |
| 20 from features import spammodel |
| 21 from features import stars |
| 22 |
| 23 from framework import artifactcollision |
| 24 from framework import banned |
| 25 from framework import csp_report |
| 26 from framework import excessiveactivity |
| 27 from framework import reap |
| 28 from framework import registerpages_helpers |
| 29 from framework import tokenrefresh |
| 30 from framework import urls |
| 31 |
| 32 from project import peopledetail |
| 33 from project import peoplelist |
| 34 from project import projectadmin |
| 35 from project import projectadminadvanced |
| 36 from project import projectexport |
| 37 from project import projectsummary |
| 38 from project import projectupdates |
| 39 from project import wikiredirect |
| 40 |
| 41 from search import backendnonviewable |
| 42 from search import backendsearch |
| 43 |
| 44 from services import cachemanager_svc |
| 45 |
| 46 from sitewide import custom_404 |
| 47 from sitewide import groupadmin |
| 48 from sitewide import groupcreate |
| 49 from sitewide import groupdetail |
| 50 from sitewide import grouplist |
| 51 from sitewide import hostinghome |
| 52 from sitewide import moved |
| 53 from sitewide import projectcreate |
| 54 from sitewide import userprofile |
| 55 from sitewide import userprojects |
| 56 from sitewide import usersettings |
| 57 from sitewide import userupdates |
| 58 |
| 59 from tracker import componentcreate |
| 60 from tracker import componentdetail |
| 61 from tracker import fieldcreate |
| 62 from tracker import fielddetail |
| 63 from tracker import issueadmin |
| 64 from tracker import issueadvsearch |
| 65 from tracker import issueattachment |
| 66 from tracker import issueattachmenttext |
| 67 from tracker import issuebulkedit |
| 68 from tracker import issuedetail |
| 69 from tracker import issueentry |
| 70 from tracker import issuelist |
| 71 from tracker import issuelistcsv |
| 72 from tracker import issueoptions |
| 73 from tracker import issueoriginal |
| 74 from tracker import issueexport |
| 75 from tracker import issueimport |
| 76 from tracker import issuereindex |
| 77 from tracker import issuetips |
| 78 from tracker import spam |
| 79 |
| 80 |
| 81 class ServletRegistry(object): |
| 82 |
| 83 _PROJECT_NAME_REGEX = r'[a-z0-9][-a-z0-9]*[a-z0-9]' |
| 84 _USERNAME_REGEX = r'[-+\w=.%]+(@([a-z0-9]+\.)*[a-z0-9]+)?' |
| 85 |
| 86 def __init__(self): |
| 87 self.routes = [] |
| 88 |
| 89 def _AddRoute(self, path_regex, servlet_class, method, does_write=False): |
| 90 """Add a GET or POST handler to our webapp2 route list. |
| 91 |
| 92 Args: |
| 93 path_regex: string with webapp2 URL template regex. |
| 94 servlet_class: a subclass of class Servlet. |
| 95 method: string 'GET' or 'POST'. |
| 96 does_write: True if the servlet could write to the database, we skip |
| 97 registering such servlets when the site is in read_only mode. GET |
| 98 handlers never write. Most, but not all, POST handlers do write. |
| 99 """ |
| 100 if settings.read_only and does_write: |
| 101 logging.info('Not registring %r because site is read-only', path_regex) |
| 102 # TODO(jrobbins): register a helpful error page instead. |
| 103 else: |
| 104 self.routes.append( |
| 105 webapp2.Route(path_regex, handler=servlet_class, methods=[method])) |
| 106 |
| 107 def _SetupServlets(self, spec_dict, base='', post_does_write=True): |
| 108 """Register each of the given servlets.""" |
| 109 for get_uri, servlet_class in spec_dict.items(): |
| 110 self._AddRoute(base + get_uri, servlet_class, 'GET') |
| 111 post_uri = get_uri + ('edit.do' if get_uri.endswith('/') else '.do') |
| 112 self._AddRoute(base + post_uri, servlet_class, 'POST', |
| 113 does_write=post_does_write) |
| 114 |
| 115 def _SetupProjectServlets(self, spec_dict, post_does_write=True): |
| 116 """Register each of the given servlets in the project URI space.""" |
| 117 self._SetupServlets( |
| 118 spec_dict, base='/p/<project_name:%s>' % self._PROJECT_NAME_REGEX, |
| 119 post_does_write=post_does_write) |
| 120 |
| 121 def _SetupUserServlets(self, spec_dict, post_does_write=True): |
| 122 """Register each of the given servlets in the user URI space.""" |
| 123 self._SetupServlets( |
| 124 spec_dict, base='/u/<viewed_username:%s>' % self._USERNAME_REGEX, |
| 125 post_does_write=post_does_write) |
| 126 |
| 127 def _SetupGroupServlets(self, spec_dict, post_does_write=True): |
| 128 """Register each of the given servlets in the user group URI space.""" |
| 129 self._SetupServlets( |
| 130 spec_dict, base='/g/<viewed_username:%s>' % self._USERNAME_REGEX, |
| 131 post_does_write=post_does_write) |
| 132 |
| 133 def Register(self, services): |
| 134 """Register all the monorail request handlers.""" |
| 135 self._RegisterFrameworkHandlers() |
| 136 self._RegisterSitewideHandlers() |
| 137 self._RegisterProjectHandlers() |
| 138 self._RegisterIssueHandlers() |
| 139 self._RegisterRedirects() |
| 140 self._RegisterInboundMail() |
| 141 autolink.RegisterAutolink(services) |
| 142 # Error pages should be the last to register. |
| 143 self._RegisterErrorPages() |
| 144 logging.info('Finished registering monorail handlers.') |
| 145 return self.routes |
| 146 |
| 147 def _RegisterProjectHandlers(self): |
| 148 """Register page and form handlers that operate within a project.""" |
| 149 self._SetupProjectServlets({ |
| 150 urls.ADMIN_INTRO: projectsummary.ProjectSummary, |
| 151 urls.PEOPLE_LIST: peoplelist.PeopleList, |
| 152 urls.PEOPLE_DETAIL: peopledetail.PeopleDetail, |
| 153 urls.PEOPLE_DETAIL_PREFS_JSON: peopledetail.PagePrefs, |
| 154 urls.UPDATES_LIST: projectupdates.ProjectUpdates, |
| 155 urls.ADMIN_META: projectadmin.ProjectAdmin, |
| 156 urls.ADMIN_ADVANCED: projectadminadvanced.ProjectAdminAdvanced, |
| 157 urls.ADMIN_EXPORT: projectexport.ProjectExport, |
| 158 urls.ADMIN_EXPORT_JSON: projectexport.ProjectExportJSON, |
| 159 }) |
| 160 |
| 161 def _RegisterIssueHandlers(self): |
| 162 """Register page and form handlers for the issue tracker.""" |
| 163 self._SetupServlets({ |
| 164 # Note: there is both a site-wide and per-project issue list. |
| 165 urls.ISSUE_LIST: issuelist.IssueList, |
| 166 |
| 167 # Note: the following are at URLs that are not externaly accessible. |
| 168 urls.BACKEND_SEARCH: backendsearch.BackendSearch, |
| 169 urls.BACKEND_NONVIEWABLE: backendnonviewable.BackendNonviewable, |
| 170 urls.RECOMPUTE_DERIVED_FIELDS_TASK: |
| 171 filterrules.RecomputeDerivedFieldsTask, |
| 172 urls.REINDEX_QUEUE_CRON: filterrules.ReindexQueueCron, |
| 173 urls.NOTIFY_ISSUE_CHANGE_TASK: notify.NotifyIssueChangeTask, |
| 174 urls.NOTIFY_BLOCKING_CHANGE_TASK: notify.NotifyBlockingChangeTask, |
| 175 urls.NOTIFY_BULK_CHANGE_TASK: notify.NotifyBulkChangeTask, |
| 176 urls.OUTBOUND_EMAIL_TASK: notify.OutboundEmailTask, |
| 177 urls.SPAM_DATA_EXPORT_TASK: spammodel.TrainingDataExportTask, |
| 178 }) |
| 179 |
| 180 self._SetupProjectServlets({ |
| 181 urls.ISSUE_LIST: issuelist.IssueList, |
| 182 urls.ISSUE_LIST_CSV: issuelistcsv.IssueListCsv, |
| 183 urls.ISSUE_REINDEX: issuereindex.IssueReindex, |
| 184 urls.ISSUE_DETAIL: issuedetail.IssueDetail, |
| 185 urls.ISSUE_COMMENT_DELETION_JSON: issuedetail.IssueCommentDeletion, |
| 186 urls.ISSUE_ATTACHMENT_DELETION_JSON: |
| 187 issueattachment.IssueAttachmentDeletion, |
| 188 urls.ISSUE_FLAGSPAM_JSON: spam.FlagSpamForm, |
| 189 urls.ISSUE_SETSTAR_JSON: issuedetail.SetStarForm, |
| 190 urls.ISSUE_DELETE_JSON: issuedetail.IssueDeleteForm, |
| 191 urls.ISSUE_ENTRY: issueentry.IssueEntry, |
| 192 urls.ISSUE_OPTIONS_JSON: issueoptions.IssueOptionsJSON, |
| 193 urls.ISSUE_TIPS: issuetips.IssueSearchTips, |
| 194 urls.ISSUE_ATTACHMENT: issueattachment.AttachmentPage, |
| 195 urls.ISSUE_ATTACHMENT_TEXT: issueattachmenttext.AttachmentText, |
| 196 urls.ISSUE_BULK_EDIT: issuebulkedit.IssueBulkEdit, |
| 197 urls.COMPONENT_CHECKNAME_JSON: componentcreate.CheckComponentNameJSON, |
| 198 urls.COMPONENT_CREATE: componentcreate.ComponentCreate, |
| 199 urls.COMPONENT_DETAIL: componentdetail.ComponentDetail, |
| 200 urls.FIELD_CHECKNAME_JSON: fieldcreate.CheckFieldNameJSON, |
| 201 urls.FIELD_CREATE: fieldcreate.FieldCreate, |
| 202 urls.FIELD_DETAIL: fielddetail.FieldDetail, |
| 203 urls.WIKI_LIST: wikiredirect.WikiRedirect, |
| 204 urls.WIKI_PAGE: wikiredirect.WikiRedirect, |
| 205 urls.ADMIN_STATUSES: issueadmin.AdminStatuses, |
| 206 urls.ADMIN_LABELS: issueadmin.AdminLabels, |
| 207 urls.ADMIN_RULES: issueadmin.AdminRules, |
| 208 urls.ADMIN_TEMPLATES: issueadmin.AdminTemplates, |
| 209 urls.ADMIN_COMPONENTS: issueadmin.AdminComponents, |
| 210 urls.ADMIN_VIEWS: issueadmin.AdminViews, |
| 211 urls.ISSUE_ORIGINAL: issueoriginal.IssueOriginal, |
| 212 urls.ISSUE_EXPORT: issueexport.IssueExport, |
| 213 urls.ISSUE_EXPORT_JSON: issueexport.IssueExportJSON, |
| 214 urls.ISSUE_IMPORT: issueimport.IssueImport, |
| 215 urls.SPAM_MODERATION_QUEUE: spam.ModerationQueue, |
| 216 }) |
| 217 |
| 218 self._SetupUserServlets({ |
| 219 urls.SAVED_QUERIES: savedqueries.SavedQueries, |
| 220 }) |
| 221 |
| 222 # These servlets accept POST, but never write to the database, so they can |
| 223 # still be used when the site is read-only. |
| 224 self._SetupProjectServlets({ |
| 225 urls.ISSUE_ADVSEARCH: issueadvsearch.IssueAdvancedSearch, |
| 226 }, post_does_write=False) |
| 227 |
| 228 list_redir = registerpages_helpers.MakeRedirectInScope( |
| 229 urls.ISSUE_LIST, 'p') |
| 230 self._SetupProjectServlets({ |
| 231 '': list_redir, |
| 232 '/': list_redir, |
| 233 '/issues': list_redir, |
| 234 '/issues/': list_redir, |
| 235 }) |
| 236 |
| 237 list_redir = registerpages_helpers.MakeRedirect(urls.ISSUE_LIST) |
| 238 self._SetupServlets({ |
| 239 '/issues': list_redir, |
| 240 '/issues/': list_redir, |
| 241 }) |
| 242 |
| 243 def _RegisterFrameworkHandlers(self): |
| 244 """Register page and form handlers for framework functionality.""" |
| 245 self._SetupServlets({ |
| 246 urls.CSP_REPORT: csp_report.CSPReportPage, |
| 247 urls.TOKEN_REFRESH: tokenrefresh.TokenRefresh, |
| 248 |
| 249 # These are only shown to users iff specific conditions are met. |
| 250 urls.NONPROJECT_COLLISION: artifactcollision.ArtifactCollision, |
| 251 urls.EXCESSIVE_ACTIVITY: excessiveactivity.ExcessiveActivity, |
| 252 urls.BANNED: banned.Banned, |
| 253 urls.PROJECT_MOVED: moved.ProjectMoved, |
| 254 |
| 255 # These are not externally accessible |
| 256 urls.RAMCACHE_CONSOLIDATE_CRON: cachemanager_svc.RamCacheConsolidate, |
| 257 urls.REAP_CRON: reap.Reap, |
| 258 urls.SPAM_DATA_EXPORT_CRON: spammodel.TrainingDataExport, |
| 259 }) |
| 260 |
| 261 self._SetupProjectServlets({ |
| 262 # Collisions can happen on artifacts within a project or outside. |
| 263 urls.ARTIFACT_COLLISION: artifactcollision.ArtifactCollision, |
| 264 }) |
| 265 |
| 266 def _RegisterSitewideHandlers(self): |
| 267 """Register page and form handlers that aren't associated with projects.""" |
| 268 self._SetupServlets({ |
| 269 urls.PROJECT_CREATE: projectcreate.ProjectCreate, |
| 270 urls.CHECK_PROJECT_NAME_JSON: projectcreate.CheckProjectNameJSON, |
| 271 # The user settings page is a site-wide servlet, not under /u/. |
| 272 urls.USER_SETTINGS: usersettings.UserSettings, |
| 273 urls.USER_PROJECTS_JSON: userprojects.ProjectsJsonFeed, |
| 274 urls.HOSTING_HOME: hostinghome.HostingHome, |
| 275 urls.STARS_JSON: stars.SetStarsFeed, |
| 276 urls.CUES_JSON: cues.SetCuesFeed, |
| 277 urls.GROUP_CREATE: groupcreate.GroupCreate, |
| 278 urls.GROUP_LIST: grouplist.GroupList, |
| 279 urls.GROUP_DELETE: grouplist.GroupList, |
| 280 }) |
| 281 |
| 282 self._SetupUserServlets({ |
| 283 urls.USER_PROFILE: userprofile.UserProfile, |
| 284 urls.USER_UPDATES_PROJECTS: userupdates.UserUpdatesProjects, |
| 285 urls.USER_UPDATES_DEVELOPERS: userupdates.UserUpdatesDevelopers, |
| 286 urls.USER_UPDATES_MINE: userupdates.UserUpdatesIndividual, |
| 287 }) |
| 288 |
| 289 profile_redir = registerpages_helpers.MakeRedirectInScope( |
| 290 urls.USER_PROFILE, 'u') |
| 291 self._SetupUserServlets({'': profile_redir}) |
| 292 |
| 293 self._SetupGroupServlets({ |
| 294 urls.GROUP_DETAIL: groupdetail.GroupDetail, |
| 295 urls.GROUP_ADMIN: groupadmin.GroupAdmin, |
| 296 }) |
| 297 |
| 298 def _RegisterRedirects(self): |
| 299 """Register redirects among pages inside monorail.""" |
| 300 redirect = registerpages_helpers.MakeRedirect('/hosting/') |
| 301 self._SetupServlets({ |
| 302 '/hosting': redirect, |
| 303 '/p': redirect, |
| 304 '/p/': redirect, |
| 305 '/u': redirect, |
| 306 '/u/': redirect, |
| 307 '/': redirect, |
| 308 }) |
| 309 |
| 310 redirect = registerpages_helpers.MakeRedirectInScope( |
| 311 urls.PEOPLE_LIST, 'p') |
| 312 self._SetupProjectServlets({ |
| 313 '/people': redirect, |
| 314 '/people/': redirect, |
| 315 }) |
| 316 |
| 317 redirect = registerpages_helpers.MakeRedirect(urls.GROUP_LIST) |
| 318 self._SetupServlets({'/g': redirect}) |
| 319 |
| 320 group_redir = registerpages_helpers.MakeRedirectInScope( |
| 321 urls.USER_PROFILE, 'g') |
| 322 self._SetupGroupServlets({'': group_redir}) |
| 323 |
| 324 def _RegisterInboundMail(self): |
| 325 """Register a handler for inbound email.""" |
| 326 self.routes.append(webapp2.Route( |
| 327 '/_ah/mail/<project_addr:.+>', |
| 328 handler=inboundemail.InboundEmail, |
| 329 methods=['POST', 'GET'])) |
| 330 |
| 331 def _RegisterErrorPages(self): |
| 332 """Register handlers for errors.""" |
| 333 self._AddRoute( |
| 334 '/p/<project_name:%s>/<unrecognized:.+>' % self._PROJECT_NAME_REGEX, |
| 335 custom_404.ErrorPage, 'GET') |
| 336 |
OLD | NEW |