Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(155)

Side by Side Diff: appengine/monorail/tracker/componentdetail.py

Issue 1868553004: Open Source Monorail (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Rebase Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 """A servlet for project and component owners to view and edit components."""
7
8 import logging
9 import time
10
11 from third_party import ezt
12
13 from features import filterrules_helpers
14 from framework import framework_helpers
15 from framework import framework_views
16 from framework import permissions
17 from framework import servlet
18 from framework import timestr
19 from framework import urls
20 from tracker import component_helpers
21 from tracker import tracker_bizobj
22 from tracker import tracker_constants
23 from tracker import tracker_views
24
25
26 class ComponentDetail(servlet.Servlet):
27 """Servlets allowing project owners to view and edit a component."""
28
29 _MAIN_TAB_MODE = servlet.Servlet.MAIN_TAB_PROCESS
30 _PAGE_TEMPLATE = 'tracker/component-detail-page.ezt'
31
32 def _GetComponentDef(self, mr):
33 """Get the config and component definition to be viewed or edited."""
34 if not mr.component_path:
35 self.abort(404, 'component not specified')
36 config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
37 component_def = tracker_bizobj.FindComponentDef(mr.component_path, config)
38 if not component_def:
39 self.abort(404, 'component not found')
40 return config, component_def
41
42 def AssertBasePermission(self, mr):
43 """Check whether the user has any permission to visit this page.
44
45 Args:
46 mr: commonly used info parsed from the request.
47 """
48 super(ComponentDetail, self).AssertBasePermission(mr)
49 _config, component_def = self._GetComponentDef(mr)
50
51 # TODO(jrobbins): optional restrictions on viewing fields by component.
52
53 allow_view = permissions.CanViewComponentDef(
54 mr.auth.effective_ids, mr.perms, mr.project, component_def)
55 if not allow_view:
56 raise permissions.PermissionException(
57 'User is not allowed to view this component')
58
59 def GatherPageData(self, mr):
60 """Build up a dictionary of data values to use when rendering the page.
61
62 Args:
63 mr: commonly used info parsed from the request.
64
65 Returns:
66 Dict of values used by EZT for rendering the page.
67 """
68 config, component_def = self._GetComponentDef(mr)
69 users_by_id = framework_views.MakeAllUserViews(
70 mr.cnxn, self.services.user,
71 component_def.admin_ids, component_def.cc_ids)
72 component_def_view = tracker_views.ComponentDefView(
73 component_def, users_by_id)
74 initial_admins = [users_by_id[uid].email for uid in component_def.admin_ids]
75 initial_cc = [users_by_id[uid].email for uid in component_def.cc_ids]
76
77 creator, created = self._GetUserViewAndFormattedTime(
78 mr, component_def.creator_id, component_def.created)
79 modifier, modified = self._GetUserViewAndFormattedTime(
80 mr, component_def.modifier_id, component_def.modified)
81
82 allow_edit = permissions.CanEditComponentDef(
83 mr.auth.effective_ids, mr.perms, mr.project, component_def, config)
84
85 subcomponents = tracker_bizobj.FindDescendantComponents(
86 config, component_def)
87 templates = self.services.config.TemplatesWithComponent(
88 mr.cnxn, component_def.component_id, config)
89 allow_delete = allow_edit and not subcomponents and not templates
90
91 return {
92 'admin_tab_mode': servlet.Servlet.PROCESS_TAB_COMPONENTS,
93 'component_def': component_def_view,
94 'initial_leaf_name': component_def_view.leaf_name,
95 'initial_docstring': component_def.docstring,
96 'initial_deprecated': ezt.boolean(component_def.deprecated),
97 'initial_admins': initial_admins,
98 'initial_cc': initial_cc,
99 'allow_edit': ezt.boolean(allow_edit),
100 'allow_delete': ezt.boolean(allow_delete),
101 'subcomponents': subcomponents,
102 'templates': templates,
103 'creator': creator,
104 'created': created,
105 'modifier': modifier,
106 'modified': modified,
107 }
108
109 def ProcessFormData(self, mr, post_data):
110 """Validate and store the contents of the issues tracker admin page.
111
112 Args:
113 mr: commonly used info parsed from the request.
114 post_data: HTML form data from the request.
115
116 Returns:
117 String URL to redirect the user to, or None if response was already sent.
118 """
119 config, component_def = self._GetComponentDef(mr)
120 allow_edit = permissions.CanEditComponentDef(
121 mr.auth.effective_ids, mr.perms, mr.project, component_def, config)
122 if not allow_edit:
123 raise permissions.PermissionException(
124 'User is not allowed to edit or delete this component')
125
126 if 'deletecomponent' in post_data:
127 allow_delete = not tracker_bizobj.FindDescendantComponents(
128 config, component_def)
129 if not allow_delete:
130 raise permissions.PermissionException(
131 'User tried to delete component that had subcomponents')
132 return self._ProcessDeleteComponent(mr, component_def)
133
134 else:
135 return self._ProcessEditComponent(mr, post_data, config, component_def)
136
137
138 def _ProcessDeleteComponent(self, mr, component_def):
139 """The user wants to delete the specified custom field definition."""
140 self.services.issue.DeleteComponentReferences(
141 mr.cnxn, component_def.component_id)
142 self.services.config.DeleteComponentDef(
143 mr.cnxn, mr.project_id, component_def.component_id)
144 return framework_helpers.FormatAbsoluteURL(
145 mr, urls.ADMIN_COMPONENTS, deleted=1, ts=int(time.time()))
146
147 def _GetUserViewAndFormattedTime(self, mr, user_id, timestamp):
148 formatted_time = (timestr.FormatAbsoluteDate(timestamp)
149 if timestamp else None)
150 user = self.services.user.GetUser(mr.cnxn, user_id) if user_id else None
151 user_view = None
152 if user:
153 user_view = framework_views.UserView(
154 user_id, user.email, user.obscure_email)
155 viewing_self = mr.auth.user_id == user_id
156 # Do not obscure email if current user is a site admin. Do not obscure
157 # email if current user is the same as the creator. For all other
158 # cases do whatever obscure_email setting for the user is.
159 email_obscured = (not(mr.auth.user_pb.is_site_admin or viewing_self)
160 and user_view.obscure_email)
161 if not email_obscured:
162 user_view.RevealEmail()
163
164 return user_view, formatted_time
165
166 def _ProcessEditComponent(self, mr, post_data, config, component_def):
167 """The user wants to edit this component definition."""
168 parsed = component_helpers.ParseComponentRequest(
169 mr, post_data, self.services.user)
170
171 if not tracker_constants.COMPONENT_NAME_RE.match(parsed.leaf_name):
172 mr.errors.leaf_name = 'Invalid component name'
173
174 original_path = component_def.path
175 if mr.component_path and '>' in mr.component_path:
176 parent_path = mr.component_path[:mr.component_path.rindex('>')]
177 new_path = '%s>%s' % (parent_path, parsed.leaf_name)
178 else:
179 new_path = parsed.leaf_name
180
181 conflict = tracker_bizobj.FindComponentDef(new_path, config)
182 if conflict and conflict.component_id != component_def.component_id:
183 mr.errors.leaf_name = 'That name is already in use.'
184
185 creator, created = self._GetUserViewAndFormattedTime(
186 mr, component_def.creator_id, component_def.created)
187 modifier, modified = self._GetUserViewAndFormattedTime(
188 mr, component_def.modifier_id, component_def.modified)
189
190 if mr.errors.AnyErrors():
191 self.PleaseCorrect(
192 mr, initial_leaf_name=parsed.leaf_name,
193 initial_docstring=parsed.docstring,
194 initial_deprecated=ezt.boolean(parsed.deprecated),
195 initial_admins=parsed.admin_usernames,
196 initial_cc=parsed.cc_usernames,
197 created=created,
198 creator=creator,
199 modified=modified,
200 modifier=modifier,
201 )
202 return None
203
204 new_modified = int(time.time())
205 new_modifier_id = self.services.user.LookupUserID(
206 mr.cnxn, mr.auth.email, autocreate=False)
207 self.services.config.UpdateComponentDef(
208 mr.cnxn, mr.project_id, component_def.component_id,
209 path=new_path, docstring=parsed.docstring, deprecated=parsed.deprecated,
210 admin_ids=parsed.admin_ids, cc_ids=parsed.cc_ids, modified=new_modified,
211 modifier_id=new_modifier_id)
212
213 update_rule = False
214 if new_path != original_path:
215 update_rule = True
216 # If the name changed then update all of its subcomponents as well.
217 subcomponent_ids = tracker_bizobj.FindMatchingComponentIDs(
218 original_path, config, exact=False)
219 for subcomponent_id in subcomponent_ids:
220 if subcomponent_id == component_def.component_id:
221 continue
222 subcomponent_def = tracker_bizobj.FindComponentDefByID(
223 subcomponent_id, config)
224 subcomponent_new_path = subcomponent_def.path.replace(
225 original_path, new_path, 1)
226 self.services.config.UpdateComponentDef(
227 mr.cnxn, mr.project_id, subcomponent_def.component_id,
228 path=subcomponent_new_path)
229
230 if set(parsed.cc_ids) != set(component_def.cc_ids):
231 update_rule = True
232 if update_rule:
233 filterrules_helpers.RecomputeAllDerivedFields(
234 mr.cnxn, self.services, mr.project, config)
235
236 return framework_helpers.FormatAbsoluteURL(
237 mr, urls.COMPONENT_DETAIL,
238 component=new_path, saved=1, ts=int(time.time()))
OLDNEW
« no previous file with comments | « appengine/monorail/tracker/componentcreate.py ('k') | appengine/monorail/tracker/field_helpers.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698