root/galaxy-central/lib/galaxy/webapps/community/controllers/admin.py

リビジョン 2, 40.7 KB (コミッタ: hatakeyama, 14 年 前)

import galaxy-central

行番号 
1from galaxy.web.base.controller import *
2from galaxy.webapps.community import model
3from galaxy.model.orm import *
4from galaxy.web.framework.helpers import time_ago, iff, grids
5from common import ToolListGrid, CategoryListGrid, get_category, get_event, get_tool, get_versions
6import logging
7log = logging.getLogger( __name__ )
8
9class 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
118class 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
213class 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
291class 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
343class 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
351class 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
377class 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' ) )
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。