1 | from galaxy.web.base.controller import * |
---|
2 | from galaxy.webapps.community import model |
---|
3 | from galaxy.model.orm import * |
---|
4 | from galaxy.web.framework.helpers import time_ago, iff, grids |
---|
5 | from common import ToolListGrid, CategoryListGrid, get_category, get_event, get_tool, get_versions |
---|
6 | import logging |
---|
7 | log = logging.getLogger( __name__ ) |
---|
8 | |
---|
9 | class UserListGrid( grids.Grid ): |
---|
10 | # TODO: move this to an admin_common controller since it is virtually the same |
---|
11 | # in the galaxy webapp. NOTE the additional ToolsColumn in this grid though... |
---|
12 | class UserLoginColumn( grids.TextColumn ): |
---|
13 | def get_value( self, trans, grid, user ): |
---|
14 | return user.email |
---|
15 | class UserNameColumn( grids.TextColumn ): |
---|
16 | def get_value( self, trans, grid, user ): |
---|
17 | if user.username: |
---|
18 | return user.username |
---|
19 | return 'not set' |
---|
20 | class GroupsColumn( grids.GridColumn ): |
---|
21 | def get_value( self, trans, grid, user ): |
---|
22 | if user.groups: |
---|
23 | return len( user.groups ) |
---|
24 | return 0 |
---|
25 | class RolesColumn( grids.GridColumn ): |
---|
26 | def get_value( self, trans, grid, user ): |
---|
27 | if user.roles: |
---|
28 | return len( user.roles ) |
---|
29 | return 0 |
---|
30 | class ExternalColumn( grids.GridColumn ): |
---|
31 | def get_value( self, trans, grid, user ): |
---|
32 | if user.external: |
---|
33 | return 'yes' |
---|
34 | return 'no' |
---|
35 | class LastLoginColumn( grids.GridColumn ): |
---|
36 | def get_value( self, trans, grid, user ): |
---|
37 | if user.galaxy_sessions: |
---|
38 | return self.format( user.galaxy_sessions[ 0 ].update_time ) |
---|
39 | return 'never' |
---|
40 | class StatusColumn( grids.GridColumn ): |
---|
41 | def get_value( self, trans, grid, user ): |
---|
42 | if user.purged: |
---|
43 | return "purged" |
---|
44 | elif user.deleted: |
---|
45 | return "deleted" |
---|
46 | return "" |
---|
47 | class ToolsColumn( grids.TextColumn ): |
---|
48 | def get_value( self, trans, grid, user ): |
---|
49 | return len( user.tools ) |
---|
50 | class EmailColumn( grids.GridColumn ): |
---|
51 | def filter( self, trans, user, query, column_filter ): |
---|
52 | if column_filter == 'All': |
---|
53 | return query |
---|
54 | return query.filter( and_( model.Tool.table.c.user_id == model.User.table.c.id, |
---|
55 | model.User.table.c.email == column_filter ) ) |
---|
56 | # Grid definition |
---|
57 | webapp = "community" |
---|
58 | title = "Users" |
---|
59 | model_class = model.User |
---|
60 | template='/admin/user/grid.mako' |
---|
61 | default_sort_key = "email" |
---|
62 | columns = [ |
---|
63 | UserLoginColumn( "Email", |
---|
64 | key="email", |
---|
65 | link=( lambda item: dict( operation="information", id=item.id, webapp="community" ) ), |
---|
66 | attach_popup=True, |
---|
67 | filterable="advanced" ), |
---|
68 | UserNameColumn( "User Name", |
---|
69 | key="username", |
---|
70 | attach_popup=False, |
---|
71 | filterable="advanced" ), |
---|
72 | GroupsColumn( "Groups", attach_popup=False ), |
---|
73 | RolesColumn( "Roles", attach_popup=False ), |
---|
74 | ExternalColumn( "External", attach_popup=False ), |
---|
75 | LastLoginColumn( "Last Login", format=time_ago ), |
---|
76 | StatusColumn( "Status", attach_popup=False ), |
---|
77 | ToolsColumn( "Uploaded Tools", |
---|
78 | link=( lambda item: dict( operation="tools_by_user", id=item.id, webapp="community" ) ), |
---|
79 | attach_popup=False, |
---|
80 | filterable="advanced" ), |
---|
81 | # Columns that are valid for filtering but are not visible. |
---|
82 | EmailColumn( "Email", |
---|
83 | key="email", |
---|
84 | visible=False ) |
---|
85 | ] |
---|
86 | columns.append( grids.MulticolFilterColumn( "Search", |
---|
87 | cols_to_filter=[ columns[0], columns[1] ], |
---|
88 | key="free-text-search", |
---|
89 | visible=False, |
---|
90 | filterable="standard" ) ) |
---|
91 | global_actions = [ |
---|
92 | grids.GridAction( "Create new user", |
---|
93 | dict( controller='admin', action='users', operation='create', webapp="community" ) ) |
---|
94 | ] |
---|
95 | operations = [ |
---|
96 | grids.GridOperation( "Manage Roles and Groups", |
---|
97 | condition=( lambda item: not item.deleted ), |
---|
98 | allow_multiple=False, |
---|
99 | url_args=dict( webapp="community", action="manage_roles_and_groups_for_user" ) ), |
---|
100 | grids.GridOperation( "Reset Password", |
---|
101 | condition=( lambda item: not item.deleted ), |
---|
102 | allow_multiple=True, |
---|
103 | allow_popup=False, |
---|
104 | url_args=dict( webapp="community", action="reset_user_password" ) ) |
---|
105 | ] |
---|
106 | standard_filters = [ |
---|
107 | grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), |
---|
108 | grids.GridColumnFilter( "Deleted", args=dict( deleted=True, purged=False ) ), |
---|
109 | grids.GridColumnFilter( "Purged", args=dict( purged=True ) ), |
---|
110 | grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) |
---|
111 | ] |
---|
112 | num_rows_per_page = 50 |
---|
113 | preserve_state = False |
---|
114 | use_paging = True |
---|
115 | def get_current_item( self, trans, **kwargs ): |
---|
116 | return trans.user |
---|
117 | |
---|
118 | class RoleListGrid( grids.Grid ): |
---|
119 | # TODO: move this to an admin_common controller since it is virtually the same |
---|
120 | # in the galaxy webapp. |
---|
121 | class NameColumn( grids.TextColumn ): |
---|
122 | def get_value( self, trans, grid, role ): |
---|
123 | return role.name |
---|
124 | class DescriptionColumn( grids.TextColumn ): |
---|
125 | def get_value( self, trans, grid, role ): |
---|
126 | if role.description: |
---|
127 | return role.description |
---|
128 | return '' |
---|
129 | class TypeColumn( grids.TextColumn ): |
---|
130 | def get_value( self, trans, grid, role ): |
---|
131 | return role.type |
---|
132 | class StatusColumn( grids.GridColumn ): |
---|
133 | def get_value( self, trans, grid, role ): |
---|
134 | if role.deleted: |
---|
135 | return "deleted" |
---|
136 | return "" |
---|
137 | class GroupsColumn( grids.GridColumn ): |
---|
138 | def get_value( self, trans, grid, role ): |
---|
139 | if role.groups: |
---|
140 | return len( role.groups ) |
---|
141 | return 0 |
---|
142 | class UsersColumn( grids.GridColumn ): |
---|
143 | def get_value( self, trans, grid, role ): |
---|
144 | if role.users: |
---|
145 | return len( role.users ) |
---|
146 | return 0 |
---|
147 | |
---|
148 | # Grid definition |
---|
149 | webapp = "community" |
---|
150 | title = "Roles" |
---|
151 | model_class = model.Role |
---|
152 | template='/admin/dataset_security/role/grid.mako' |
---|
153 | default_sort_key = "name" |
---|
154 | columns = [ |
---|
155 | NameColumn( "Name", |
---|
156 | key="name", |
---|
157 | link=( lambda item: dict( operation="Manage users and groups", id=item.id, webapp="community" ) ), |
---|
158 | attach_popup=True, |
---|
159 | filterable="advanced" ), |
---|
160 | DescriptionColumn( "Description", |
---|
161 | key='description', |
---|
162 | attach_popup=False, |
---|
163 | filterable="advanced" ), |
---|
164 | TypeColumn( "Type", |
---|
165 | key='type', |
---|
166 | attach_popup=False, |
---|
167 | filterable="advanced" ), |
---|
168 | GroupsColumn( "Groups", attach_popup=False ), |
---|
169 | UsersColumn( "Users", attach_popup=False ), |
---|
170 | StatusColumn( "Status", attach_popup=False ), |
---|
171 | # Columns that are valid for filtering but are not visible. |
---|
172 | grids.DeletedColumn( "Deleted", |
---|
173 | key="deleted", |
---|
174 | visible=False, |
---|
175 | filterable="advanced" ) |
---|
176 | ] |
---|
177 | columns.append( grids.MulticolFilterColumn( "Search", |
---|
178 | cols_to_filter=[ columns[0], columns[1], columns[2] ], |
---|
179 | key="free-text-search", |
---|
180 | visible=False, |
---|
181 | filterable="standard" ) ) |
---|
182 | global_actions = [ |
---|
183 | grids.GridAction( "Add new role", |
---|
184 | dict( controller='admin', action='roles', operation='create', webapp="community" ) ) |
---|
185 | ] |
---|
186 | operations = [ grids.GridOperation( "Rename", |
---|
187 | condition=( lambda item: not item.deleted ), |
---|
188 | allow_multiple=False, |
---|
189 | url_args=dict( webapp="community", action="rename_role" ) ), |
---|
190 | grids.GridOperation( "Delete", |
---|
191 | condition=( lambda item: not item.deleted ), |
---|
192 | allow_multiple=True, |
---|
193 | url_args=dict( webapp="community", action="mark_role_deleted" ) ), |
---|
194 | grids.GridOperation( "Undelete", |
---|
195 | condition=( lambda item: item.deleted ), |
---|
196 | allow_multiple=True, |
---|
197 | url_args=dict( webapp="community", action="undelete_role" ) ), |
---|
198 | grids.GridOperation( "Purge", |
---|
199 | condition=( lambda item: item.deleted ), |
---|
200 | allow_multiple=True, |
---|
201 | url_args=dict( webapp="community", action="purge_role" ) ) ] |
---|
202 | standard_filters = [ |
---|
203 | grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), |
---|
204 | grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), |
---|
205 | grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) |
---|
206 | ] |
---|
207 | num_rows_per_page = 50 |
---|
208 | preserve_state = False |
---|
209 | use_paging = True |
---|
210 | def apply_query_filter( self, trans, query, **kwd ): |
---|
211 | return query.filter( model.Role.type != model.Role.types.PRIVATE ) |
---|
212 | |
---|
213 | class GroupListGrid( grids.Grid ): |
---|
214 | # TODO: move this to an admin_common controller since it is virtually the same |
---|
215 | # in the galaxy webapp. |
---|
216 | class NameColumn( grids.TextColumn ): |
---|
217 | def get_value( self, trans, grid, group ): |
---|
218 | return group.name |
---|
219 | class StatusColumn( grids.GridColumn ): |
---|
220 | def get_value( self, trans, grid, group ): |
---|
221 | if group.deleted: |
---|
222 | return "deleted" |
---|
223 | return "" |
---|
224 | class RolesColumn( grids.GridColumn ): |
---|
225 | def get_value( self, trans, grid, group ): |
---|
226 | if group.roles: |
---|
227 | return len( group.roles ) |
---|
228 | return 0 |
---|
229 | class UsersColumn( grids.GridColumn ): |
---|
230 | def get_value( self, trans, grid, group ): |
---|
231 | if group.members: |
---|
232 | return len( group.members ) |
---|
233 | return 0 |
---|
234 | |
---|
235 | # Grid definition |
---|
236 | webapp = "community" |
---|
237 | title = "Groups" |
---|
238 | model_class = model.Group |
---|
239 | template='/admin/dataset_security/group/grid.mako' |
---|
240 | default_sort_key = "name" |
---|
241 | columns = [ |
---|
242 | NameColumn( "Name", |
---|
243 | #key="name", |
---|
244 | link=( lambda item: dict( operation="Manage users and roles", id=item.id, webapp="community" ) ), |
---|
245 | attach_popup=True |
---|
246 | #filterable="advanced" |
---|
247 | ), |
---|
248 | UsersColumn( "Users", attach_popup=False ), |
---|
249 | RolesColumn( "Roles", attach_popup=False ), |
---|
250 | StatusColumn( "Status", attach_popup=False ), |
---|
251 | # Columns that are valid for filtering but are not visible. |
---|
252 | grids.DeletedColumn( "Deleted", |
---|
253 | key="deleted", |
---|
254 | visible=False, |
---|
255 | filterable="advanced" ) |
---|
256 | ] |
---|
257 | columns.append( grids.MulticolFilterColumn( "Search", |
---|
258 | cols_to_filter=[ columns[0], columns[1], columns[2] ], |
---|
259 | key="free-text-search", |
---|
260 | visible=False, |
---|
261 | filterable="standard" ) ) |
---|
262 | global_actions = [ |
---|
263 | grids.GridAction( "Add new group", |
---|
264 | dict( controller='admin', action='groups', operation='create', webapp="community" ) ) |
---|
265 | ] |
---|
266 | operations = [ grids.GridOperation( "Rename", |
---|
267 | condition=( lambda item: not item.deleted ), |
---|
268 | allow_multiple=False, |
---|
269 | url_args=dict( webapp="community", action="rename_group" ) ), |
---|
270 | grids.GridOperation( "Delete", |
---|
271 | condition=( lambda item: not item.deleted ), |
---|
272 | allow_multiple=True, |
---|
273 | url_args=dict( webapp="community", action="mark_group_deleted" ) ), |
---|
274 | grids.GridOperation( "Undelete", |
---|
275 | condition=( lambda item: item.deleted ), |
---|
276 | allow_multiple=True, |
---|
277 | url_args=dict( webapp="community", action="undelete_group" ) ), |
---|
278 | grids.GridOperation( "Purge", |
---|
279 | condition=( lambda item: item.deleted ), |
---|
280 | allow_multiple=True, |
---|
281 | url_args=dict( webapp="community", action="purge_group" ) ) ] |
---|
282 | standard_filters = [ |
---|
283 | grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), |
---|
284 | grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), |
---|
285 | grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) |
---|
286 | ] |
---|
287 | num_rows_per_page = 50 |
---|
288 | preserve_state = False |
---|
289 | use_paging = True |
---|
290 | |
---|
291 | class AdminToolListGrid( ToolListGrid ): |
---|
292 | class StateColumn( grids.TextColumn ): |
---|
293 | def get_value( self, trans, grid, tool ): |
---|
294 | state = tool.state |
---|
295 | if state == 'approved': |
---|
296 | state_color = 'ok' |
---|
297 | elif state == 'rejected': |
---|
298 | state_color = 'error' |
---|
299 | elif state == 'archived': |
---|
300 | state_color = 'upload' |
---|
301 | else: |
---|
302 | state_color = state |
---|
303 | return '<div class="count-box state-color-%s">%s</div>' % ( state_color, state ) |
---|
304 | class ToolStateColumn( grids.StateColumn ): |
---|
305 | def filter( self, trans, user, query, column_filter ): |
---|
306 | """Modify query to filter by state.""" |
---|
307 | if column_filter == "All": |
---|
308 | pass |
---|
309 | elif column_filter in [ v for k, v in self.model_class.states.items() ]: |
---|
310 | # Get all of the latest ToolEventAssociation ids |
---|
311 | tea_ids = [ tea_id_tup[0] for tea_id_tup in trans.sa_session.query( func.max( model.ToolEventAssociation.table.c.id ) ) \ |
---|
312 | .group_by( model.ToolEventAssociation.table.c.tool_id ) ] |
---|
313 | # Get all of the Event ids associated with the latest ToolEventAssociation ids |
---|
314 | event_ids = [ event_id_tup[0] for event_id_tup in trans.sa_session.query( model.ToolEventAssociation.table.c.event_id ) \ |
---|
315 | .filter( model.ToolEventAssociation.table.c.id.in_( tea_ids ) ) ] |
---|
316 | # Filter our query by state and event ids |
---|
317 | return query.filter( and_( model.Event.table.c.state == column_filter, |
---|
318 | model.Event.table.c.id.in_( event_ids ) ) ) |
---|
319 | return query |
---|
320 | |
---|
321 | columns = [ col for col in ToolListGrid.columns ] |
---|
322 | columns.append( |
---|
323 | StateColumn( "Status", |
---|
324 | model_class=model.Tool, |
---|
325 | link=( lambda item: dict( operation="tools_by_state", id=item.id, webapp="community" ) ), |
---|
326 | attach_popup=False ), |
---|
327 | ) |
---|
328 | columns.append( |
---|
329 | # Columns that are valid for filtering but are not visible. |
---|
330 | ToolStateColumn( "State", |
---|
331 | key="state", |
---|
332 | model_class=model.Tool, |
---|
333 | visible=False, |
---|
334 | filterable="advanced" ) |
---|
335 | ) |
---|
336 | operations = [ |
---|
337 | grids.GridOperation( "Edit information", |
---|
338 | condition=( lambda item: not item.deleted ), |
---|
339 | allow_multiple=False, |
---|
340 | url_args=dict( controller="common", action="edit_tool", cntrller="admin", webapp="community" ) ) |
---|
341 | ] |
---|
342 | |
---|
343 | class AdminCategoryListGrid( CategoryListGrid ): |
---|
344 | # Override standard filters |
---|
345 | standard_filters = [ |
---|
346 | grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), |
---|
347 | grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), |
---|
348 | grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) |
---|
349 | ] |
---|
350 | |
---|
351 | class ManageCategoryListGrid( CategoryListGrid ): |
---|
352 | columns = [ col for col in CategoryListGrid.columns ] |
---|
353 | # Override the NameColumn to include an Edit link |
---|
354 | columns[ 0 ] = CategoryListGrid.NameColumn( "Name", |
---|
355 | key="name", |
---|
356 | link=( lambda item: dict( operation="Edit", id=item.id, webapp="community" ) ), |
---|
357 | model_class=model.Category, |
---|
358 | attach_popup=False, |
---|
359 | filterable="advanced" ) |
---|
360 | global_actions = [ |
---|
361 | grids.GridAction( "Add new category", |
---|
362 | dict( controller='admin', action='manage_categories', operation='create', webapp="community" ) ) |
---|
363 | ] |
---|
364 | operations = [ grids.GridOperation( "Delete", |
---|
365 | condition=( lambda item: not item.deleted ), |
---|
366 | allow_multiple=True, |
---|
367 | url_args=dict( webapp="community", action="mark_category_deleted" ) ), |
---|
368 | grids.GridOperation( "Undelete", |
---|
369 | condition=( lambda item: item.deleted ), |
---|
370 | allow_multiple=True, |
---|
371 | url_args=dict( webapp="community", action="undelete_category" ) ), |
---|
372 | grids.GridOperation( "Purge", |
---|
373 | condition=( lambda item: item.deleted ), |
---|
374 | allow_multiple=True, |
---|
375 | url_args=dict( webapp="community", action="purge_category" ) ) ] |
---|
376 | |
---|
377 | class AdminController( BaseController, Admin ): |
---|
378 | |
---|
379 | user_list_grid = UserListGrid() |
---|
380 | role_list_grid = RoleListGrid() |
---|
381 | group_list_grid = GroupListGrid() |
---|
382 | manage_category_list_grid = ManageCategoryListGrid() |
---|
383 | category_list_grid = AdminCategoryListGrid() |
---|
384 | tool_list_grid = AdminToolListGrid() |
---|
385 | |
---|
386 | @web.expose |
---|
387 | @web.require_admin |
---|
388 | def browse_tools( self, trans, **kwd ): |
---|
389 | # We add params to the keyword dict in this method in order to rename the param |
---|
390 | # with an "f-" prefix, simulating filtering by clicking a search link. We have |
---|
391 | # to take this approach because the "-" character is illegal in HTTP requests. |
---|
392 | if 'operation' in kwd: |
---|
393 | operation = kwd['operation'].lower() |
---|
394 | if operation == "edit_tool": |
---|
395 | return trans.response.send_redirect( web.url_for( controller='common', |
---|
396 | action='edit_tool', |
---|
397 | cntrller='admin', |
---|
398 | **kwd ) ) |
---|
399 | elif operation == "view_tool": |
---|
400 | return trans.response.send_redirect( web.url_for( controller='common', |
---|
401 | action='view_tool', |
---|
402 | cntrller='admin', |
---|
403 | **kwd ) ) |
---|
404 | elif operation == 'tool_history': |
---|
405 | return trans.response.send_redirect( web.url_for( controller='common', |
---|
406 | cntrller='admin', |
---|
407 | action='events', |
---|
408 | **kwd ) ) |
---|
409 | elif operation == "tools_by_user": |
---|
410 | # Eliminate the current filters if any exist. |
---|
411 | for k, v in kwd.items(): |
---|
412 | if k.startswith( 'f-' ): |
---|
413 | del kwd[ k ] |
---|
414 | if 'user_id' in kwd: |
---|
415 | user = get_user( trans, kwd[ 'user_id' ] ) |
---|
416 | kwd[ 'f-email' ] = user.email |
---|
417 | del kwd[ 'user_id' ] |
---|
418 | else: |
---|
419 | # The received id is the tool id, so we need to get the id of the user |
---|
420 | # that uploaded the tool. |
---|
421 | tool_id = kwd.get( 'id', None ) |
---|
422 | tool = get_tool( trans, tool_id ) |
---|
423 | kwd[ 'f-email' ] = tool.user.email |
---|
424 | elif operation == "tools_by_state": |
---|
425 | # Eliminate the current filters if any exist. |
---|
426 | for k, v in kwd.items(): |
---|
427 | if k.startswith( 'f-' ): |
---|
428 | del kwd[ k ] |
---|
429 | if 'state' in kwd: |
---|
430 | # Called from the Admin menu |
---|
431 | kwd[ 'f-state' ] = kwd[ 'state' ] |
---|
432 | else: |
---|
433 | # Called from the ToolStateColumn link |
---|
434 | tool_id = kwd.get( 'id', None ) |
---|
435 | tool = get_tool( trans, tool_id ) |
---|
436 | kwd[ 'f-state' ] = tool.state |
---|
437 | elif operation == "tools_by_category": |
---|
438 | # Eliminate the current filters if any exist. |
---|
439 | for k, v in kwd.items(): |
---|
440 | if k.startswith( 'f-' ): |
---|
441 | del kwd[ k ] |
---|
442 | category_id = kwd.get( 'id', None ) |
---|
443 | category = get_category( trans, category_id ) |
---|
444 | kwd[ 'f-Category.name' ] = category.name |
---|
445 | # Render the list view |
---|
446 | return self.tool_list_grid( trans, **kwd ) |
---|
447 | @web.expose |
---|
448 | @web.require_admin |
---|
449 | def browse_categories( self, trans, **kwd ): |
---|
450 | if 'operation' in kwd: |
---|
451 | operation = kwd['operation'].lower() |
---|
452 | if operation in [ "tools_by_category", "tools_by_state", "tools_by_user" ]: |
---|
453 | # Eliminate the current filters if any exist. |
---|
454 | for k, v in kwd.items(): |
---|
455 | if k.startswith( 'f-' ): |
---|
456 | del kwd[ k ] |
---|
457 | return trans.response.send_redirect( web.url_for( controller='admin', |
---|
458 | action='browse_tools', |
---|
459 | **kwd ) ) |
---|
460 | # Render the list view |
---|
461 | return self.category_list_grid( trans, **kwd ) |
---|
462 | @web.expose |
---|
463 | @web.require_admin |
---|
464 | def manage_categories( self, trans, **kwd ): |
---|
465 | if 'operation' in kwd: |
---|
466 | operation = kwd['operation'].lower() |
---|
467 | if operation == "create": |
---|
468 | return self.create_category( trans, **kwd ) |
---|
469 | elif operation == "delete": |
---|
470 | return self.mark_category_deleted( trans, **kwd ) |
---|
471 | elif operation == "undelete": |
---|
472 | return self.undelete_category( trans, **kwd ) |
---|
473 | elif operation == "purge": |
---|
474 | return self.purge_category( trans, **kwd ) |
---|
475 | elif operation == "edit": |
---|
476 | return self.edit_category( trans, **kwd ) |
---|
477 | # Render the list view |
---|
478 | return self.manage_category_list_grid( trans, **kwd ) |
---|
479 | @web.expose |
---|
480 | @web.require_admin |
---|
481 | def create_category( self, trans, **kwd ): |
---|
482 | params = util.Params( kwd ) |
---|
483 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
484 | status = params.get( 'status', 'done' ) |
---|
485 | if params.get( 'create_category_button', False ): |
---|
486 | name = util.restore_text( params.name ) |
---|
487 | description = util.restore_text( params.description ) |
---|
488 | error = False |
---|
489 | if not name or not description: |
---|
490 | message = 'Enter a valid name and a description' |
---|
491 | error = True |
---|
492 | elif trans.sa_session.query( trans.app.model.Category ) \ |
---|
493 | .filter( trans.app.model.Category.table.c.name==name ) \ |
---|
494 | .first(): |
---|
495 | message = 'A category with that name already exists' |
---|
496 | error = True |
---|
497 | if error: |
---|
498 | return trans.fill_template( '/webapps/community/category/create_category.mako', |
---|
499 | name=name, |
---|
500 | description=description, |
---|
501 | message=message, |
---|
502 | status='error' ) |
---|
503 | else: |
---|
504 | # Create the category |
---|
505 | category = trans.app.model.Category( name=name, description=description ) |
---|
506 | trans.sa_session.add( category ) |
---|
507 | message = "Category '%s' has been created" % category.name |
---|
508 | trans.sa_session.flush() |
---|
509 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
510 | action='manage_categories', |
---|
511 | message=util.sanitize_text( message ), |
---|
512 | status='done' ) ) |
---|
513 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
514 | action='create_category', |
---|
515 | message=util.sanitize_text( message ), |
---|
516 | status='error' ) ) |
---|
517 | else: |
---|
518 | name = '' |
---|
519 | description = '' |
---|
520 | return trans.fill_template( '/webapps/community/category/create_category.mako', |
---|
521 | name=name, |
---|
522 | description=description, |
---|
523 | message=message, |
---|
524 | status=status ) |
---|
525 | @web.expose |
---|
526 | @web.require_admin |
---|
527 | def set_tool_state( self, trans, state, **kwd ): |
---|
528 | params = util.Params( kwd ) |
---|
529 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
530 | status = params.get( 'status', 'done' ) |
---|
531 | comments = util.restore_text( params.get( 'comments', '' ) ) |
---|
532 | id = params.get( 'id', None ) |
---|
533 | if not id: |
---|
534 | message = "No tool id received for setting status" |
---|
535 | status = 'error' |
---|
536 | else: |
---|
537 | tool = get_tool( trans, id ) |
---|
538 | if state == trans.app.model.Tool.states.APPROVED: |
---|
539 | # If we're approving a tool, all previously approved versions must be set to archived |
---|
540 | for version in get_versions( tool ): |
---|
541 | # TODO: get latest approved version instead of all versions |
---|
542 | if version != tool and version.is_approved: |
---|
543 | # Create an event with state ARCHIVED for the previously approved version of this tool |
---|
544 | self.__create_tool_event( trans, |
---|
545 | version, |
---|
546 | trans.app.model.Tool.states.ARCHIVED ) |
---|
547 | # Create an event with state APPROVED for this tool |
---|
548 | self.__create_tool_event( trans, tool, state, comments ) |
---|
549 | elif state == trans.app.model.Tool.states.REJECTED: |
---|
550 | # If we're rejecting a tool, comments about why are necessary. |
---|
551 | return trans.fill_template( '/webapps/community/admin/reject_tool.mako', |
---|
552 | tool=tool, |
---|
553 | cntrller='admin' ) |
---|
554 | message = "State of tool '%s' is now %s" % ( tool.name, state ) |
---|
555 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
556 | action='browse_tools', |
---|
557 | message=message, |
---|
558 | status=status ) ) |
---|
559 | @web.expose |
---|
560 | @web.require_admin |
---|
561 | def reject_tool( self, trans, **kwd ): |
---|
562 | params = util.Params( kwd ) |
---|
563 | if params.get( 'cancel_reject_button', False ): |
---|
564 | # Fix up the keyword dict to include params to view the current tool |
---|
565 | # since that is the page from which we originated. |
---|
566 | del kwd[ 'cancel_reject_button' ] |
---|
567 | del kwd[ 'comments' ] |
---|
568 | kwd[ 'webapp' ] = 'community' |
---|
569 | kwd[ 'operation' ] = 'view_tool' |
---|
570 | message = 'Tool rejection cancelled' |
---|
571 | status = 'done' |
---|
572 | return trans.response.send_redirect( web.url_for( controller='admin', |
---|
573 | action='browse_tools', |
---|
574 | message=message, |
---|
575 | status=status, |
---|
576 | **kwd ) ) |
---|
577 | id = params.get( 'id', None ) |
---|
578 | if not id: |
---|
579 | return trans.response.send_redirect( web.url_for( controller=cntrller, |
---|
580 | action='browse_tools', |
---|
581 | message='No tool id received for rejecting', |
---|
582 | status='error' ) ) |
---|
583 | tool = get_tool( trans, id ) |
---|
584 | if not trans.app.security_agent.can_approve_or_reject( trans.user, trans.user_is_admin(), 'admin', tool ): |
---|
585 | return trans.response.send_redirect( web.url_for( controller='admin', |
---|
586 | action='browse_tools', |
---|
587 | message='You are not allowed to reject this tool', |
---|
588 | status='error' ) ) |
---|
589 | # Comments are required when rejecting a tool. |
---|
590 | comments = util.restore_text( params.get( 'comments', '' ) ) |
---|
591 | if not comments: |
---|
592 | message = 'The reason for rejection is required when rejecting a tool.' |
---|
593 | return trans.fill_template( '/webapps/community/admin/reject_tool.mako', |
---|
594 | tool=tool, |
---|
595 | cntrller='admin', |
---|
596 | message=message, |
---|
597 | status='error' ) |
---|
598 | # Create an event with state REJECTED for this tool |
---|
599 | self.__create_tool_event( trans, tool, trans.app.model.Tool.states.REJECTED, comments ) |
---|
600 | message = 'The tool "%s" has been rejected.' % tool.name |
---|
601 | return trans.response.send_redirect( web.url_for( controller='admin', |
---|
602 | action='browse_tools', |
---|
603 | operation='tools_by_state', |
---|
604 | state='rejected', |
---|
605 | message=message, |
---|
606 | status='done' ) ) |
---|
607 | def __create_tool_event( self, trans, tool, state, comments='' ): |
---|
608 | event = trans.model.Event( state, comments ) |
---|
609 | # Flush so we can get an id |
---|
610 | trans.sa_session.add( event ) |
---|
611 | trans.sa_session.flush() |
---|
612 | tea = trans.model.ToolEventAssociation( tool, event ) |
---|
613 | trans.sa_session.add( tea ) |
---|
614 | trans.sa_session.flush() |
---|
615 | @web.expose |
---|
616 | @web.require_admin |
---|
617 | def purge_tool( self, trans, **kwd ): |
---|
618 | # This method completely removes a tool record and all associated foreign key rows |
---|
619 | # from the database, so it must be used carefully. |
---|
620 | # This method should only be called for a tool that has previously been deleted. |
---|
621 | # Purging a deleted tool deletes all of the following from the database: |
---|
622 | # - ToolCategoryAssociations |
---|
623 | # - ToolEventAssociations and associated Events |
---|
624 | # TODO: when we add tagging for tools, we'll have to purge them as well |
---|
625 | params = util.Params( kwd ) |
---|
626 | id = kwd.get( 'id', None ) |
---|
627 | if not id: |
---|
628 | message = "No tool ids received for purging" |
---|
629 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
630 | action='browse_tools', |
---|
631 | message=util.sanitize_text( message ), |
---|
632 | status='error' ) ) |
---|
633 | ids = util.listify( id ) |
---|
634 | message = "Purged %d tools: " % len( ids ) |
---|
635 | for tool_id in ids: |
---|
636 | tool = get_tool( trans, tool_id ) |
---|
637 | message += " %s " % tool.name |
---|
638 | if not tool.deleted: |
---|
639 | message = "Tool '%s' has not been deleted, so it cannot be purged." % tool.name |
---|
640 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
641 | action='browse_tools', |
---|
642 | message=util.sanitize_text( message ), |
---|
643 | status='error' ) ) |
---|
644 | # Delete ToolCategoryAssociations |
---|
645 | for tca in tool.categories: |
---|
646 | trans.sa_session.delete( tca ) |
---|
647 | # Delete ToolEventAssociations and associated events |
---|
648 | for tea in tool.events: |
---|
649 | event = tea.event |
---|
650 | trans.sa_session.delete( event ) |
---|
651 | trans.sa_session.delete( tea ) |
---|
652 | # Delete the tool |
---|
653 | trans.sa_session.delete( tool ) |
---|
654 | trans.sa_session.flush() |
---|
655 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
656 | action='browse_tools', |
---|
657 | message=util.sanitize_text( message ), |
---|
658 | status='done' ) ) |
---|
659 | @web.expose |
---|
660 | @web.require_admin |
---|
661 | def edit_category( self, trans, **kwd ): |
---|
662 | params = util.Params( kwd ) |
---|
663 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
664 | status = params.get( 'status', 'done' ) |
---|
665 | id = params.get( 'id', None ) |
---|
666 | if not id: |
---|
667 | message = "No category ids received for editing" |
---|
668 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
669 | action='manage_categories', |
---|
670 | message=message, |
---|
671 | status='error' ) ) |
---|
672 | category = get_category( trans, id ) |
---|
673 | if params.get( 'edit_category_button', False ): |
---|
674 | new_name = util.restore_text( params.get( 'name', '' ) ).strip() |
---|
675 | new_description = util.restore_text( params.get( 'description', '' ) ).strip() |
---|
676 | if category.name != new_name or category.description != new_description: |
---|
677 | if not new_name: |
---|
678 | message = 'Enter a valid name' |
---|
679 | status = 'error' |
---|
680 | elif category.name != new_name and \ |
---|
681 | trans.sa_session.query( trans.app.model.Category ).filter( trans.app.model.Category.table.c.name==new_name ).first(): |
---|
682 | message = 'A category with that name already exists' |
---|
683 | status = 'error' |
---|
684 | else: |
---|
685 | category.name = new_name |
---|
686 | category.description = new_description |
---|
687 | trans.sa_session.add( category ) |
---|
688 | trans.sa_session.flush() |
---|
689 | message = "The information has been saved for category '%s'" % ( category.name ) |
---|
690 | return trans.response.send_redirect( web.url_for( controller='admin', |
---|
691 | action='manage_categories', |
---|
692 | message=util.sanitize_text( message ), |
---|
693 | status='done' ) ) |
---|
694 | return trans.fill_template( '/webapps/community/category/edit_category.mako', |
---|
695 | category=category, |
---|
696 | message=message, |
---|
697 | status=status ) |
---|
698 | @web.expose |
---|
699 | @web.require_admin |
---|
700 | def mark_category_deleted( self, trans, **kwd ): |
---|
701 | params = util.Params( kwd ) |
---|
702 | id = kwd.get( 'id', None ) |
---|
703 | if not id: |
---|
704 | message = "No category ids received for deleting" |
---|
705 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
706 | action='manage_categories', |
---|
707 | message=message, |
---|
708 | status='error' ) ) |
---|
709 | ids = util.listify( id ) |
---|
710 | message = "Deleted %d categories: " % len( ids ) |
---|
711 | for category_id in ids: |
---|
712 | category = get_category( trans, category_id ) |
---|
713 | category.deleted = True |
---|
714 | trans.sa_session.add( category ) |
---|
715 | trans.sa_session.flush() |
---|
716 | message += " %s " % category.name |
---|
717 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
718 | action='manage_categories', |
---|
719 | message=util.sanitize_text( message ), |
---|
720 | status='done' ) ) |
---|
721 | @web.expose |
---|
722 | @web.require_admin |
---|
723 | def undelete_category( self, trans, **kwd ): |
---|
724 | params = util.Params( kwd ) |
---|
725 | id = kwd.get( 'id', None ) |
---|
726 | if not id: |
---|
727 | message = "No category ids received for undeleting" |
---|
728 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
729 | action='manage_categories', |
---|
730 | message=message, |
---|
731 | status='error' ) ) |
---|
732 | ids = util.listify( id ) |
---|
733 | count = 0 |
---|
734 | undeleted_categories = "" |
---|
735 | for category_id in ids: |
---|
736 | category = get_category( trans, category_id ) |
---|
737 | if not category.deleted: |
---|
738 | message = "Category '%s' has not been deleted, so it cannot be undeleted." % category.name |
---|
739 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
740 | action='manage_categories', |
---|
741 | message=util.sanitize_text( message ), |
---|
742 | status='error' ) ) |
---|
743 | category.deleted = False |
---|
744 | trans.sa_session.add( category ) |
---|
745 | trans.sa_session.flush() |
---|
746 | count += 1 |
---|
747 | undeleted_categories += " %s" % category.name |
---|
748 | message = "Undeleted %d categories: %s" % ( count, undeleted_categories ) |
---|
749 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
750 | action='manage_categories', |
---|
751 | message=util.sanitize_text( message ), |
---|
752 | status='done' ) ) |
---|
753 | @web.expose |
---|
754 | @web.require_admin |
---|
755 | def purge_category( self, trans, **kwd ): |
---|
756 | # This method should only be called for a Category that has previously been deleted. |
---|
757 | # Purging a deleted Category deletes all of the following from the database: |
---|
758 | # - ToolCategoryAssociations where category_id == Category.id |
---|
759 | params = util.Params( kwd ) |
---|
760 | id = kwd.get( 'id', None ) |
---|
761 | if not id: |
---|
762 | message = "No category ids received for purging" |
---|
763 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
764 | action='manage_categories', |
---|
765 | message=util.sanitize_text( message ), |
---|
766 | status='error' ) ) |
---|
767 | ids = util.listify( id ) |
---|
768 | message = "Purged %d categories: " % len( ids ) |
---|
769 | for category_id in ids: |
---|
770 | category = get_category( trans, category_id ) |
---|
771 | if not category.deleted: |
---|
772 | message = "Category '%s' has not been deleted, so it cannot be purged." % category.name |
---|
773 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
774 | action='manage_categories', |
---|
775 | message=util.sanitize_text( message ), |
---|
776 | status='error' ) ) |
---|
777 | # Delete ToolCategoryAssociations |
---|
778 | for tca in category.tools: |
---|
779 | trans.sa_session.delete( tca ) |
---|
780 | trans.sa_session.flush() |
---|
781 | message += " %s " % category.name |
---|
782 | trans.response.send_redirect( web.url_for( controller='admin', |
---|
783 | action='manage_categories', |
---|
784 | message=util.sanitize_text( message ), |
---|
785 | status='done' ) ) |
---|