1 | import os, os.path, shutil, urllib, StringIO, re, gzip, tempfile, shutil, zipfile, copy, glob, string |
---|
2 | from galaxy.web.base.controller import * |
---|
3 | from galaxy import util, jobs |
---|
4 | from galaxy.datatypes import sniff |
---|
5 | from galaxy.security import RBACAgent |
---|
6 | from galaxy.util.json import to_json_string |
---|
7 | from galaxy.tools.actions import upload_common |
---|
8 | from galaxy.model.orm import * |
---|
9 | from galaxy.util.streamball import StreamBall |
---|
10 | from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, TextArea, TextField, WorkflowField |
---|
11 | import logging, tempfile, zipfile, tarfile, os, sys |
---|
12 | |
---|
13 | if sys.version_info[:2] < ( 2, 6 ): |
---|
14 | zipfile.BadZipFile = zipfile.error |
---|
15 | if sys.version_info[:2] < ( 2, 5 ): |
---|
16 | zipfile.LargeZipFile = zipfile.error |
---|
17 | |
---|
18 | log = logging.getLogger( __name__ ) |
---|
19 | |
---|
20 | # Test for available compression types |
---|
21 | tmpd = tempfile.mkdtemp() |
---|
22 | comptypes = [] |
---|
23 | for comptype in ( 'gz', 'bz2' ): |
---|
24 | tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype ) |
---|
25 | try: |
---|
26 | archive = tarfile.open( tmpf, 'w:' + comptype ) |
---|
27 | archive.close() |
---|
28 | comptypes.append( comptype ) |
---|
29 | except tarfile.CompressionError: |
---|
30 | log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype ) |
---|
31 | try: |
---|
32 | os.unlink( tmpf ) |
---|
33 | except OSError: |
---|
34 | pass |
---|
35 | ziptype = '32' |
---|
36 | tmpf = os.path.join( tmpd, 'compression_test.zip' ) |
---|
37 | try: |
---|
38 | archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True ) |
---|
39 | archive.close() |
---|
40 | comptypes.append( 'zip' ) |
---|
41 | ziptype = '64' |
---|
42 | except RuntimeError: |
---|
43 | log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." ) |
---|
44 | except (TypeError, zipfile.LargeZipFile): |
---|
45 | # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped |
---|
46 | log.warning( 'Max zip file size is 2GB, ZIP64 not supported' ) |
---|
47 | comptypes.append( 'zip' ) |
---|
48 | try: |
---|
49 | os.unlink( tmpf ) |
---|
50 | except OSError: |
---|
51 | pass |
---|
52 | os.rmdir( tmpd ) |
---|
53 | |
---|
54 | class LibraryCommon( BaseController, UsesFormDefinitionWidgets ): |
---|
55 | @web.json |
---|
56 | def library_item_updates( self, trans, ids=None, states=None ): |
---|
57 | # Avoid caching |
---|
58 | trans.response.headers['Pragma'] = 'no-cache' |
---|
59 | trans.response.headers['Expires'] = '0' |
---|
60 | # Create new HTML for any that have changed |
---|
61 | rval = {} |
---|
62 | if ids is not None and states is not None: |
---|
63 | ids = map( int, ids.split( "," ) ) |
---|
64 | states = states.split( "," ) |
---|
65 | for id, state in zip( ids, states ): |
---|
66 | data = trans.sa_session.query( self.app.model.LibraryDatasetDatasetAssociation ).get( id ) |
---|
67 | if data.state != state: |
---|
68 | job_ldda = data |
---|
69 | while job_ldda.copied_from_library_dataset_dataset_association: |
---|
70 | job_ldda = job_ldda.copied_from_library_dataset_dataset_association |
---|
71 | force_history_refresh = False |
---|
72 | rval[id] = { |
---|
73 | "state": data.state, |
---|
74 | "html": unicode( trans.fill_template( "library/common/library_item_info.mako", ldda=data ), 'utf-8' ) |
---|
75 | #"force_history_refresh": force_history_refresh |
---|
76 | } |
---|
77 | return rval |
---|
78 | @web.expose |
---|
79 | def browse_library( self, trans, cntrller, **kwd ): |
---|
80 | params = util.Params( kwd ) |
---|
81 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
82 | status = params.get( 'status', 'done' ) |
---|
83 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
84 | library_id = params.get( 'id', None ) |
---|
85 | if not library_id: |
---|
86 | # To handle bots |
---|
87 | message = "You must specify a library id." |
---|
88 | status = 'error' |
---|
89 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
90 | current_user_roles = trans.get_current_user_roles() |
---|
91 | try: |
---|
92 | library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) |
---|
93 | except: |
---|
94 | # Protect against attempts to phish for valid keys that return libraries |
---|
95 | library = None |
---|
96 | # Most security for browsing libraries is handled in the template, but do a basic check here. |
---|
97 | if not library or not ( is_admin or trans.app.security_agent.can_access_library( current_user_roles, library ) ): |
---|
98 | message = "Invalid library id ( %s ) specified." % str( library_id ) |
---|
99 | status = 'error' |
---|
100 | else: |
---|
101 | # If use_panels is True, the library is being accessed via an external link |
---|
102 | # which did not originate from within the Galaxy instance, and the library will |
---|
103 | # be displayed correctly with the mast head. |
---|
104 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
105 | created_ldda_ids = params.get( 'created_ldda_ids', '' ) |
---|
106 | hidden_folder_ids = util.listify( params.get( 'hidden_folder_ids', '' ) ) |
---|
107 | if created_ldda_ids and not message: |
---|
108 | message = "%d datasets are uploading in the background to the library '%s' (each is selected). " % \ |
---|
109 | ( len( created_ldda_ids.split( ',' ) ), library.name ) |
---|
110 | message += "Don't navigate away from Galaxy or use the browser's \"stop\" or \"reload\" buttons (on this tab) until the " |
---|
111 | message += "message \"This job is running\" is cleared from the \"Information\" column below for each selected dataset." |
---|
112 | status = "info" |
---|
113 | comptypes_t = comptypes |
---|
114 | if trans.app.config.nginx_x_archive_files_base: |
---|
115 | comptypes_t = ['ngxzip'] |
---|
116 | for comptype in trans.app.config.disable_library_comptypes: |
---|
117 | # TODO: do this once, not every time (we're gonna raise an |
---|
118 | # exception every time after the first time) |
---|
119 | try: |
---|
120 | comptypes_t.remove( comptype ) |
---|
121 | except: |
---|
122 | pass |
---|
123 | return trans.fill_template( '/library/common/browse_library.mako', |
---|
124 | cntrller=cntrller, |
---|
125 | use_panels=use_panels, |
---|
126 | library=library, |
---|
127 | created_ldda_ids=created_ldda_ids, |
---|
128 | hidden_folder_ids=hidden_folder_ids, |
---|
129 | show_deleted=show_deleted, |
---|
130 | comptypes=comptypes_t, |
---|
131 | current_user_roles=current_user_roles, |
---|
132 | message=message, |
---|
133 | status=status ) |
---|
134 | return trans.response.send_redirect( web.url_for( use_panels=use_panels, |
---|
135 | controller=cntrller, |
---|
136 | action='browse_libraries', |
---|
137 | default_action=params.get( 'default_action', None ), |
---|
138 | message=util.sanitize_text( message ), |
---|
139 | status=status ) ) |
---|
140 | @web.expose |
---|
141 | def library_info( self, trans, cntrller, **kwd ): |
---|
142 | params = util.Params( kwd ) |
---|
143 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
144 | status = params.get( 'status', 'done' ) |
---|
145 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
146 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
147 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
148 | current_user_roles = trans.get_current_user_roles() |
---|
149 | library_id = params.get( 'id', None ) |
---|
150 | try: |
---|
151 | library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) |
---|
152 | except: |
---|
153 | library = None |
---|
154 | self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted ) |
---|
155 | if params.get( 'library_info_button', False ): |
---|
156 | self._check_modify( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted ) |
---|
157 | old_name = library.name |
---|
158 | new_name = util.restore_text( params.get( 'name', 'No name' ) ) |
---|
159 | if not new_name: |
---|
160 | message = 'Enter a valid name' |
---|
161 | status='error' |
---|
162 | else: |
---|
163 | new_description = util.restore_text( params.get( 'description', '' ) ) |
---|
164 | new_synopsis = util.restore_text( params.get( 'synopsis', '' ) ) |
---|
165 | if new_synopsis in [ None, 'None' ]: |
---|
166 | new_synopsis = '' |
---|
167 | library.name = new_name |
---|
168 | library.description = new_description |
---|
169 | library.synopsis = new_synopsis |
---|
170 | # Rename the root_folder |
---|
171 | library.root_folder.name = new_name |
---|
172 | library.root_folder.description = new_description |
---|
173 | trans.sa_session.add_all( ( library, library.root_folder ) ) |
---|
174 | trans.sa_session.flush() |
---|
175 | message = "Information updated for library '%s'." % library.name |
---|
176 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
177 | action='library_info', |
---|
178 | cntrller=cntrller, |
---|
179 | use_panels=use_panels, |
---|
180 | id=trans.security.encode_id( library.id ), |
---|
181 | show_deleted=show_deleted, |
---|
182 | message=util.sanitize_text( message ), |
---|
183 | status='done' ) ) |
---|
184 | # See if we have any associated templates |
---|
185 | info_association, inherited = library.get_info_association() |
---|
186 | widgets = library.get_template_widgets( trans ) |
---|
187 | widget_fields_have_contents = self.widget_fields_have_contents( widgets ) |
---|
188 | return trans.fill_template( '/library/common/library_info.mako', |
---|
189 | cntrller=cntrller, |
---|
190 | use_panels=use_panels, |
---|
191 | library=library, |
---|
192 | widgets=widgets, |
---|
193 | widget_fields_have_contents=widget_fields_have_contents, |
---|
194 | current_user_roles=current_user_roles, |
---|
195 | show_deleted=show_deleted, |
---|
196 | info_association=info_association, |
---|
197 | inherited=inherited, |
---|
198 | message=message, |
---|
199 | status=status ) |
---|
200 | @web.expose |
---|
201 | def library_permissions( self, trans, cntrller, **kwd ): |
---|
202 | params = util.Params( kwd ) |
---|
203 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
204 | status = params.get( 'status', 'done' ) |
---|
205 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
206 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
207 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
208 | current_user_roles = trans.get_current_user_roles() |
---|
209 | library_id = params.get( 'id', None ) |
---|
210 | try: |
---|
211 | library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) |
---|
212 | except: |
---|
213 | library = None |
---|
214 | self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted ) |
---|
215 | self._check_manage( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted ) |
---|
216 | if params.get( 'update_roles_button', False ): |
---|
217 | # The user clicked the Save button on the 'Associate With Roles' form |
---|
218 | permissions = {} |
---|
219 | for k, v in trans.app.model.Library.permitted_actions.items(): |
---|
220 | in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ] |
---|
221 | permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles |
---|
222 | trans.app.security_agent.set_all_library_permissions( library, permissions ) |
---|
223 | trans.sa_session.refresh( library ) |
---|
224 | # Copy the permissions to the root folder |
---|
225 | trans.app.security_agent.copy_library_permissions( library, library.root_folder ) |
---|
226 | message = "Permissions updated for library '%s'." % library.name |
---|
227 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
228 | action='library_permissions', |
---|
229 | cntrller=cntrller, |
---|
230 | use_panels=use_panels, |
---|
231 | id=trans.security.encode_id( library.id ), |
---|
232 | show_deleted=show_deleted, |
---|
233 | message=util.sanitize_text( message ), |
---|
234 | status='done' ) ) |
---|
235 | roles = trans.app.security_agent.get_legitimate_roles( trans, library, cntrller ) |
---|
236 | return trans.fill_template( '/library/common/library_permissions.mako', |
---|
237 | cntrller=cntrller, |
---|
238 | use_panels=use_panels, |
---|
239 | library=library, |
---|
240 | current_user_roles=current_user_roles, |
---|
241 | roles=roles, |
---|
242 | show_deleted=show_deleted, |
---|
243 | message=message, |
---|
244 | status=status ) |
---|
245 | @web.expose |
---|
246 | def create_folder( self, trans, cntrller, parent_id, library_id, **kwd ): |
---|
247 | params = util.Params( kwd ) |
---|
248 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
249 | status = params.get( 'status', 'done' ) |
---|
250 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
251 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
252 | is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' ) |
---|
253 | current_user_roles = trans.get_current_user_roles() |
---|
254 | try: |
---|
255 | parent_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( parent_id ) ) |
---|
256 | except: |
---|
257 | parent_folder = None |
---|
258 | # Check the library which actually contains the user-supplied parent folder, not the user-supplied |
---|
259 | # library, which could be anything. |
---|
260 | if parent_folder: |
---|
261 | parent_library = parent_folder.parent_library |
---|
262 | self._check_access( trans, cntrller, is_admin, parent_folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
263 | self._check_add( trans, cntrller, is_admin, parent_folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
264 | if params.get( 'new_folder_button', False ) or cntrller == 'api': |
---|
265 | new_folder = trans.app.model.LibraryFolder( name=util.restore_text( params.name ), |
---|
266 | description=util.restore_text( params.description ) ) |
---|
267 | # We are associating the last used genome build with folders, so we will always |
---|
268 | # initialize a new folder with the first dbkey in util.dbnames which is currently |
---|
269 | # ? unspecified (?) |
---|
270 | new_folder.genome_build = util.dbnames.default_value |
---|
271 | parent_folder.add_folder( new_folder ) |
---|
272 | trans.sa_session.add( new_folder ) |
---|
273 | trans.sa_session.flush() |
---|
274 | # New folders default to having the same permissions as their parent folder |
---|
275 | trans.app.security_agent.copy_library_permissions( parent_folder, new_folder ) |
---|
276 | # If we're creating in the API, we're done |
---|
277 | if cntrller == 'api': |
---|
278 | return 200, dict( created=new_folder ) |
---|
279 | # If we have an inheritable template, redirect to the folder_info page so information |
---|
280 | # can be filled in immediately. |
---|
281 | widgets = [] |
---|
282 | info_association, inherited = new_folder.get_info_association() |
---|
283 | if info_association and ( not( inherited ) or info_association.inheritable ): |
---|
284 | widgets = new_folder.get_template_widgets( trans ) |
---|
285 | if info_association: |
---|
286 | message = "The new folder named '%s' has been added to the data library. " % new_folder.name |
---|
287 | message += "Additional information about this folder may be added using the inherited template." |
---|
288 | return trans.fill_template( '/library/common/folder_info.mako', |
---|
289 | cntrller=cntrller, |
---|
290 | use_panels=use_panels, |
---|
291 | folder=new_folder, |
---|
292 | library_id=library_id, |
---|
293 | widgets=widgets, |
---|
294 | current_user_roles=current_user_roles, |
---|
295 | show_deleted=show_deleted, |
---|
296 | info_association=info_association, |
---|
297 | inherited=inherited, |
---|
298 | message=message, |
---|
299 | status='done' ) |
---|
300 | # If not inheritable info_association, redirect to the library. |
---|
301 | message = "The new folder named '%s' has been added to the data library." % new_folder.name |
---|
302 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
303 | action='browse_library', |
---|
304 | cntrller=cntrller, |
---|
305 | use_panels=use_panels, |
---|
306 | id=library_id, |
---|
307 | show_deleted=show_deleted, |
---|
308 | message=util.sanitize_text( message ), |
---|
309 | status='done' ) ) |
---|
310 | # We do not render any template widgets on creation pages since saving the info_association |
---|
311 | # cannot occur before the associated item is saved. |
---|
312 | return trans.fill_template( '/library/common/new_folder.mako', |
---|
313 | cntrller=cntrller, |
---|
314 | use_panels=use_panels, |
---|
315 | library_id=library_id, |
---|
316 | folder=parent_folder, |
---|
317 | show_deleted=show_deleted, |
---|
318 | message=message, |
---|
319 | status=status ) |
---|
320 | @web.expose |
---|
321 | def folder_info( self, trans, cntrller, id, library_id, **kwd ): |
---|
322 | params = util.Params( kwd ) |
---|
323 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
324 | status = params.get( 'status', 'done' ) |
---|
325 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
326 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
327 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
328 | current_user_roles = trans.get_current_user_roles() |
---|
329 | try: |
---|
330 | folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( id ) ) |
---|
331 | except: |
---|
332 | folder = None |
---|
333 | self._check_access( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
334 | if params.get( 'rename_folder_button', False ): |
---|
335 | self._check_modify( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
336 | old_name = folder.name |
---|
337 | new_name = util.restore_text( params.name ) |
---|
338 | new_description = util.restore_text( params.description ) |
---|
339 | if not new_name: |
---|
340 | message = 'Enter a valid name' |
---|
341 | status='error' |
---|
342 | else: |
---|
343 | folder.name = new_name |
---|
344 | folder.description = new_description |
---|
345 | trans.sa_session.add( folder ) |
---|
346 | trans.sa_session.flush() |
---|
347 | message = "Information updated for folder '%s'." % folder.name |
---|
348 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
349 | action='folder_info', |
---|
350 | cntrller=cntrller, |
---|
351 | use_panels=use_panels, |
---|
352 | id=id, |
---|
353 | library_id=library_id, |
---|
354 | show_deleted=show_deleted, |
---|
355 | message=util.sanitize_text( message ), |
---|
356 | status='done' ) ) |
---|
357 | # See if we have any associated templates |
---|
358 | widgets = [] |
---|
359 | widget_fields_have_contents = False |
---|
360 | info_association, inherited = folder.get_info_association() |
---|
361 | if info_association and ( not( inherited ) or info_association.inheritable ): |
---|
362 | widgets = folder.get_template_widgets( trans ) |
---|
363 | widget_fields_have_contents = self.widget_fields_have_contents( widgets ) |
---|
364 | return trans.fill_template( '/library/common/folder_info.mako', |
---|
365 | cntrller=cntrller, |
---|
366 | use_panels=use_panels, |
---|
367 | folder=folder, |
---|
368 | library_id=library_id, |
---|
369 | widgets=widgets, |
---|
370 | widget_fields_have_contents=widget_fields_have_contents, |
---|
371 | current_user_roles=current_user_roles, |
---|
372 | show_deleted=show_deleted, |
---|
373 | info_association=info_association, |
---|
374 | inherited=inherited, |
---|
375 | message=message, |
---|
376 | status=status ) |
---|
377 | @web.expose |
---|
378 | def folder_permissions( self, trans, cntrller, id, library_id, **kwd ): |
---|
379 | params = util.Params( kwd ) |
---|
380 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
381 | status = params.get( 'status', 'done' ) |
---|
382 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
383 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
384 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
385 | current_user_roles = trans.get_current_user_roles() |
---|
386 | try: |
---|
387 | folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( id ) ) |
---|
388 | except: |
---|
389 | folder = None |
---|
390 | self._check_access( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
391 | self._check_manage( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
392 | if params.get( 'update_roles_button', False ): |
---|
393 | # The user clicked the Save button on the 'Associate With Roles' form |
---|
394 | permissions = {} |
---|
395 | for k, v in trans.app.model.Library.permitted_actions.items(): |
---|
396 | if k != 'LIBRARY_ACCESS': |
---|
397 | # LIBRARY_ACCESS is a special permission set only at the library level |
---|
398 | # and it is not inherited. |
---|
399 | in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( int( x ) ) for x in util.listify( params.get( k + '_in', [] ) ) ] |
---|
400 | permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles |
---|
401 | trans.app.security_agent.set_all_library_permissions( folder, permissions ) |
---|
402 | trans.sa_session.refresh( folder ) |
---|
403 | message = "Permissions updated for folder '%s'." % folder.name |
---|
404 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
405 | action='folder_permissions', |
---|
406 | cntrller=cntrller, |
---|
407 | use_panels=use_panels, |
---|
408 | id=trans.security.encode_id( folder.id ), |
---|
409 | library_id=library_id, |
---|
410 | show_deleted=show_deleted, |
---|
411 | message=util.sanitize_text( message ), |
---|
412 | status='done' ) ) |
---|
413 | # If the library is public all roles are legitimate, but if the library |
---|
414 | # is restricted, only those roles associated with the LIBRARY_ACCESS |
---|
415 | # permission are legitimate. |
---|
416 | roles = trans.app.security_agent.get_legitimate_roles( trans, folder.parent_library, cntrller ) |
---|
417 | return trans.fill_template( '/library/common/folder_permissions.mako', |
---|
418 | cntrller=cntrller, |
---|
419 | use_panels=use_panels, |
---|
420 | folder=folder, |
---|
421 | library_id=library_id, |
---|
422 | current_user_roles=current_user_roles, |
---|
423 | roles=roles, |
---|
424 | show_deleted=show_deleted, |
---|
425 | message=message, |
---|
426 | status=status ) |
---|
427 | @web.expose |
---|
428 | def ldda_edit_info( self, trans, cntrller, library_id, folder_id, id, **kwd ): |
---|
429 | params = util.Params( kwd ) |
---|
430 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
431 | status = params.get( 'status', 'done' ) |
---|
432 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
433 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
434 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
435 | current_user_roles = trans.get_current_user_roles() |
---|
436 | try: |
---|
437 | ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( id ) ) |
---|
438 | except: |
---|
439 | ldda = None |
---|
440 | self._check_access( trans, cntrller, is_admin, ldda, current_user_roles, use_panels, library_id, show_deleted ) |
---|
441 | self._check_modify( trans, cntrller, is_admin, ldda, current_user_roles, use_panels, library_id, show_deleted ) |
---|
442 | dbkey = params.get( 'dbkey', '?' ) |
---|
443 | if isinstance( dbkey, list ): |
---|
444 | dbkey = dbkey[0] |
---|
445 | file_formats = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems() if dtype_value.allow_datatype_change ] |
---|
446 | file_formats.sort() |
---|
447 | # See if we have any associated templates |
---|
448 | widgets = [] |
---|
449 | info_association, inherited = ldda.get_info_association() |
---|
450 | if info_association and ( not( inherited ) or info_association.inheritable ): |
---|
451 | widgets = ldda.get_template_widgets( trans ) |
---|
452 | if params.get( 'change', False ): |
---|
453 | # The user clicked the Save button on the 'Change data type' form |
---|
454 | if ldda.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( params.datatype ).allow_datatype_change: |
---|
455 | trans.app.datatypes_registry.change_datatype( ldda, params.datatype ) |
---|
456 | trans.sa_session.flush() |
---|
457 | message = "Data type changed for library dataset '%s'." % ldda.name |
---|
458 | status = 'done' |
---|
459 | else: |
---|
460 | message = "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % ( ldda.extension, params.datatype ) |
---|
461 | status = 'error' |
---|
462 | elif params.get( 'save', False ): |
---|
463 | # The user clicked the Save button on the 'Edit Attributes' form |
---|
464 | old_name = ldda.name |
---|
465 | new_name = util.restore_text( params.get( 'name', '' ) ) |
---|
466 | new_info = util.restore_text( params.get( 'info', '' ) ) |
---|
467 | new_message = util.restore_text( params.get( 'message', '' ) ) |
---|
468 | if not new_name: |
---|
469 | message = 'Enter a valid name' |
---|
470 | status = 'error' |
---|
471 | else: |
---|
472 | ldda.name = new_name |
---|
473 | ldda.info = new_info |
---|
474 | ldda.message = new_message |
---|
475 | # The following for loop will save all metadata_spec items |
---|
476 | for name, spec in ldda.datatype.metadata_spec.items(): |
---|
477 | if spec.get("readonly"): |
---|
478 | continue |
---|
479 | optional = params.get( "is_" + name, None ) |
---|
480 | if optional and optional == 'true': |
---|
481 | # optional element... == 'true' actually means it is NOT checked (and therefore ommitted) |
---|
482 | setattr( ldda.metadata, name, None ) |
---|
483 | else: |
---|
484 | setattr( ldda.metadata, name, spec.unwrap( params.get ( name, None ) ) ) |
---|
485 | ldda.metadata.dbkey = dbkey |
---|
486 | ldda.datatype.after_setting_metadata( ldda ) |
---|
487 | trans.sa_session.flush() |
---|
488 | message = "Attributes updated for library dataset '%s'." % ldda.name |
---|
489 | status = 'done' |
---|
490 | elif params.get( 'detect', False ): |
---|
491 | # The user clicked the Auto-detect button on the 'Edit Attributes' form |
---|
492 | for name, spec in ldda.datatype.metadata_spec.items(): |
---|
493 | # We need to be careful about the attributes we are resetting |
---|
494 | if name not in [ 'name', 'info', 'dbkey' ]: |
---|
495 | if spec.get( 'default' ): |
---|
496 | setattr( ldda.metadata, name, spec.unwrap( spec.get( 'default' ) ) ) |
---|
497 | ldda.datatype.set_meta( ldda ) |
---|
498 | ldda.datatype.after_setting_metadata( ldda ) |
---|
499 | trans.sa_session.flush() |
---|
500 | message = "Information updated for library dataset '%s'." % ldda.name |
---|
501 | status = 'done' |
---|
502 | if "dbkey" in ldda.datatype.metadata_spec and not ldda.metadata.dbkey: |
---|
503 | # Copy dbkey into metadata, for backwards compatability |
---|
504 | # This looks like it does nothing, but getting the dbkey |
---|
505 | # returns the metadata dbkey unless it is None, in which |
---|
506 | # case it resorts to the old dbkey. Setting the dbkey |
---|
507 | # sets it properly in the metadata |
---|
508 | ldda.metadata.dbkey = ldda.dbkey |
---|
509 | return trans.fill_template( "/library/common/ldda_edit_info.mako", |
---|
510 | cntrller=cntrller, |
---|
511 | use_panels=use_panels, |
---|
512 | ldda=ldda, |
---|
513 | library_id=library_id, |
---|
514 | file_formats=file_formats, |
---|
515 | widgets=widgets, |
---|
516 | current_user_roles=current_user_roles, |
---|
517 | show_deleted=show_deleted, |
---|
518 | info_association=info_association, |
---|
519 | inherited=inherited, |
---|
520 | message=message, |
---|
521 | status=status ) |
---|
522 | @web.expose |
---|
523 | def ldda_info( self, trans, cntrller, library_id, folder_id, id, **kwd ): |
---|
524 | params = util.Params( kwd ) |
---|
525 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
526 | status = params.get( 'status', 'done' ) |
---|
527 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
528 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
529 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
530 | current_user_roles = trans.get_current_user_roles() |
---|
531 | try: |
---|
532 | ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( id ) ) |
---|
533 | except: |
---|
534 | ldda = None |
---|
535 | self._check_access( trans, cntrller, is_admin, ldda, current_user_roles, use_panels, library_id, show_deleted ) |
---|
536 | if is_admin: |
---|
537 | # Get all associated hdas and lddas that use the same disk file. |
---|
538 | associated_hdas = trans.sa_session.query( trans.model.HistoryDatasetAssociation ) \ |
---|
539 | .filter( and_( trans.model.HistoryDatasetAssociation.deleted == False, |
---|
540 | trans.model.HistoryDatasetAssociation.dataset_id == ldda.dataset_id ) ) \ |
---|
541 | .all() |
---|
542 | associated_lddas = trans.sa_session.query( trans.model.LibraryDatasetDatasetAssociation ) \ |
---|
543 | .filter( and_( trans.model.LibraryDatasetDatasetAssociation.deleted == False, |
---|
544 | trans.model.LibraryDatasetDatasetAssociation.dataset_id == ldda.dataset_id, |
---|
545 | trans.model.LibraryDatasetDatasetAssociation.id != ldda.id ) ) \ |
---|
546 | .all() |
---|
547 | else: |
---|
548 | associated_hdas = [] |
---|
549 | associated_lddas = [] |
---|
550 | # See if we have any associated templates |
---|
551 | widgets = [] |
---|
552 | widget_fields_have_contents = False |
---|
553 | info_association, inherited = ldda.get_info_association() |
---|
554 | if info_association and ( not( inherited ) or info_association.inheritable ): |
---|
555 | widgets = ldda.get_template_widgets( trans ) |
---|
556 | widget_fields_have_contents = self.widget_fields_have_contents( widgets ) |
---|
557 | return trans.fill_template( '/library/common/ldda_info.mako', |
---|
558 | cntrller=cntrller, |
---|
559 | use_panels=use_panels, |
---|
560 | ldda=ldda, |
---|
561 | library=ldda.library_dataset.folder.parent_library, |
---|
562 | associated_hdas=associated_hdas, |
---|
563 | associated_lddas=associated_lddas, |
---|
564 | show_deleted=show_deleted, |
---|
565 | widgets=widgets, |
---|
566 | widget_fields_have_contents=widget_fields_have_contents, |
---|
567 | current_user_roles=current_user_roles, |
---|
568 | info_association=info_association, |
---|
569 | inherited=inherited, |
---|
570 | message=message, |
---|
571 | status=status ) |
---|
572 | @web.expose |
---|
573 | def ldda_permissions( self, trans, cntrller, library_id, folder_id, id, **kwd ): |
---|
574 | params = util.Params( kwd ) |
---|
575 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
576 | status = params.get( 'status', 'done' ) |
---|
577 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
578 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
579 | ids = util.listify( id ) |
---|
580 | lddas = [] |
---|
581 | libraries = [] |
---|
582 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
583 | current_user_roles = trans.get_current_user_roles() |
---|
584 | for id in ids: |
---|
585 | try: |
---|
586 | ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( id ) ) |
---|
587 | except: |
---|
588 | ldda = None |
---|
589 | if ldda: |
---|
590 | library = ldda.library_dataset.folder.parent_library |
---|
591 | self._check_access( trans, cntrller, is_admin, ldda, current_user_roles, use_panels, library_id, show_deleted ) |
---|
592 | lddas.append( ldda ) |
---|
593 | libraries.append( library ) |
---|
594 | library = libraries[0] |
---|
595 | if filter( lambda x: x != library, libraries ): |
---|
596 | message = "Library datasets specified span multiple libraries." |
---|
597 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
598 | action='browse_library', |
---|
599 | id=library_id, |
---|
600 | cntrller=cntrller, |
---|
601 | use_panels=use_panels, |
---|
602 | message=util.sanitize_text( message ), |
---|
603 | status='error' ) ) |
---|
604 | # If access to the dataset is restricted, then use the roles associated with the DATASET_ACCESS permission to |
---|
605 | # determine the legitimate roles. If the dataset is public, see if access to the library is restricted. If |
---|
606 | # it is, use the roles associated with the LIBRARY_ACCESS permission to determine the legitimate roles. If both |
---|
607 | # the dataset and the library are public, all roles are legitimate. All of the datasets will have the same |
---|
608 | # permissions at this point. |
---|
609 | ldda = lddas[0] |
---|
610 | if trans.app.security_agent.dataset_is_public( ldda.dataset ): |
---|
611 | # The dataset is public, so check access to the library |
---|
612 | roles = trans.app.security_agent.get_legitimate_roles( trans, library, cntrller ) |
---|
613 | else: |
---|
614 | roles = trans.app.security_agent.get_legitimate_roles( trans, ldda.dataset, cntrller ) |
---|
615 | if params.get( 'update_roles_button', False ): |
---|
616 | a = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action ) |
---|
617 | permissions, in_roles, error, message = \ |
---|
618 | trans.app.security_agent.derive_roles_from_access( trans, trans.app.security.decode_id( library_id ), cntrller, library=True, **kwd ) |
---|
619 | for ldda in lddas: |
---|
620 | # Set the DATASET permissions on the Dataset. |
---|
621 | if error: |
---|
622 | # Keep the original role associations for the DATASET_ACCESS permission on the ldda. |
---|
623 | permissions[ a ] = ldda.get_access_roles( trans ) |
---|
624 | trans.app.security_agent.set_all_dataset_permissions( ldda.dataset, permissions ) |
---|
625 | trans.sa_session.refresh( ldda.dataset ) |
---|
626 | # Set the LIBRARY permissions on the LibraryDataset. The LibraryDataset and |
---|
627 | # LibraryDatasetDatasetAssociation will be set with the same permissions. |
---|
628 | permissions = {} |
---|
629 | for k, v in trans.app.model.Library.permitted_actions.items(): |
---|
630 | if k != 'LIBRARY_ACCESS': |
---|
631 | # LIBRARY_ACCESS is a special permission set only at the library level and it is not inherited. |
---|
632 | in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ] |
---|
633 | permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles |
---|
634 | for ldda in lddas: |
---|
635 | trans.app.security_agent.set_all_library_permissions( ldda.library_dataset, permissions ) |
---|
636 | trans.sa_session.refresh( ldda.library_dataset ) |
---|
637 | # Set the LIBRARY permissions on the LibraryDatasetDatasetAssociation |
---|
638 | trans.app.security_agent.set_all_library_permissions( ldda, permissions ) |
---|
639 | trans.sa_session.refresh( ldda ) |
---|
640 | if error: |
---|
641 | status = 'error' |
---|
642 | else: |
---|
643 | if len( lddas ) == 1: |
---|
644 | message = "Permissions updated for dataset '%s'." % ldda.name |
---|
645 | else: |
---|
646 | message = 'Permissions updated for %d datasets.' % len( lddas ) |
---|
647 | status= 'done' |
---|
648 | return trans.fill_template( "/library/common/ldda_permissions.mako", |
---|
649 | cntrller=cntrller, |
---|
650 | use_panels=use_panels, |
---|
651 | lddas=lddas, |
---|
652 | library_id=library_id, |
---|
653 | roles=roles, |
---|
654 | show_deleted=show_deleted, |
---|
655 | message=message, |
---|
656 | status=status ) |
---|
657 | if len( ids ) > 1: |
---|
658 | # Ensure that the permissions across all library items are identical, otherwise we can't update them together. |
---|
659 | check_list = [] |
---|
660 | for ldda in lddas: |
---|
661 | permissions = [] |
---|
662 | # Check the library level permissions - the permissions on the LibraryDatasetDatasetAssociation |
---|
663 | # will always be the same as the permissions on the associated LibraryDataset. |
---|
664 | for library_permission in trans.app.security_agent.get_permissions( ldda.library_dataset ): |
---|
665 | if library_permission.action not in permissions: |
---|
666 | permissions.append( library_permission.action ) |
---|
667 | for dataset_permission in trans.app.security_agent.get_permissions( ldda.dataset ): |
---|
668 | if dataset_permission.action not in permissions: |
---|
669 | permissions.append( dataset_permission.action ) |
---|
670 | permissions.sort() |
---|
671 | if not check_list: |
---|
672 | check_list = permissions |
---|
673 | if permissions != check_list: |
---|
674 | message = 'The datasets you selected do not have identical permissions, so they can not be updated together' |
---|
675 | trans.response.send_redirect( web.url_for( controller='library_common', |
---|
676 | action='browse_library', |
---|
677 | cntrller=cntrller, |
---|
678 | use_panels=use_panels, |
---|
679 | id=library_id, |
---|
680 | show_deleted=show_deleted, |
---|
681 | message=util.sanitize_text( message ), |
---|
682 | status='error' ) ) |
---|
683 | # Display permission form, permissions will be updated for all lddas simultaneously. |
---|
684 | return trans.fill_template( "/library/common/ldda_permissions.mako", |
---|
685 | cntrller=cntrller, |
---|
686 | use_panels=use_panels, |
---|
687 | lddas=lddas, |
---|
688 | library_id=library_id, |
---|
689 | roles=roles, |
---|
690 | show_deleted=show_deleted, |
---|
691 | message=message, |
---|
692 | status=status ) |
---|
693 | @web.expose |
---|
694 | def upload_library_dataset( self, trans, cntrller, library_id, folder_id, **kwd ): |
---|
695 | params = util.Params( kwd ) |
---|
696 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
697 | status = params.get( 'status', 'done' ) |
---|
698 | ldda_message = util.restore_text( params.get( 'ldda_message', '' ) ) |
---|
699 | deleted = util.string_as_bool( params.get( 'deleted', False ) ) |
---|
700 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
701 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
702 | replace_id = params.get( 'replace_id', None ) |
---|
703 | replace_dataset = None |
---|
704 | upload_option = params.get( 'upload_option', 'upload_file' ) |
---|
705 | if params.get( 'files_0|space_to_tab', False ): |
---|
706 | space_to_tab = params.get( 'files_0|space_to_tab', '' ) |
---|
707 | else: |
---|
708 | space_to_tab = params.get( 'space_to_tab', '' ) |
---|
709 | link_data_only = params.get( 'link_data_only', '' ) |
---|
710 | dbkey = params.get( 'dbkey', '?' ) |
---|
711 | if isinstance( dbkey, list ): |
---|
712 | last_used_build = dbkey[0] |
---|
713 | else: |
---|
714 | last_used_build = dbkey |
---|
715 | roles = params.get( 'roles', '' ) |
---|
716 | is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' ) |
---|
717 | current_user_roles = trans.get_current_user_roles() |
---|
718 | if replace_id not in [ None, 'None' ]: |
---|
719 | try: |
---|
720 | replace_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( trans.security.decode_id( replace_id ) ) |
---|
721 | except: |
---|
722 | replace_dataset = None |
---|
723 | self._check_access( trans, cntrller, is_admin, replace_dataset, current_user_roles, use_panels, library_id, show_deleted ) |
---|
724 | self._check_modify( trans, cntrller, is_admin, replace_dataset, current_user_roles, use_panels, library_id, show_deleted ) |
---|
725 | library = replace_dataset.folder.parent_library |
---|
726 | folder = replace_dataset.folder |
---|
727 | # The name is stored - by the time the new ldda is created, replace_dataset.name |
---|
728 | # will point to the new ldda, not the one it's replacing. |
---|
729 | replace_dataset_name = replace_dataset.name |
---|
730 | if not last_used_build: |
---|
731 | last_used_build = replace_dataset.library_dataset_dataset_association.dbkey |
---|
732 | # Don't allow multiple datasets to be uploaded when replacing a dataset with a new version |
---|
733 | upload_option = 'upload_file' |
---|
734 | else: |
---|
735 | folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) ) |
---|
736 | self._check_access( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
737 | self._check_add( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
738 | library = folder.parent_library |
---|
739 | if folder and last_used_build in [ 'None', None, '?' ]: |
---|
740 | last_used_build = folder.genome_build |
---|
741 | if params.get( 'runtool_btn', False ) or params.get( 'ajax_upload', False ) or cntrller == 'api': |
---|
742 | error = False |
---|
743 | if upload_option == 'upload_paths' and not trans.app.config.allow_library_path_paste: |
---|
744 | error = True |
---|
745 | message = '"allow_library_path_paste" is not defined in the Galaxy configuration file' |
---|
746 | elif roles: |
---|
747 | # Check to see if the user selected roles to associate with the DATASET_ACCESS permission |
---|
748 | # on the dataset that would cause accessibility issues. |
---|
749 | vars = dict( DATASET_ACCESS_in=roles ) |
---|
750 | permissions, in_roles, error, message = \ |
---|
751 | trans.app.security_agent.derive_roles_from_access( trans, library.id, cntrller, library=True, **vars ) |
---|
752 | if error: |
---|
753 | if cntrller == 'api': |
---|
754 | return 400, message |
---|
755 | trans.response.send_redirect( web.url_for( controller='library_common', |
---|
756 | action='upload_library_dataset', |
---|
757 | cntrller=cntrller, |
---|
758 | library_id=library_id, |
---|
759 | folder_id=folder_id, |
---|
760 | replace_id=replace_id, |
---|
761 | upload_option=upload_option, |
---|
762 | show_deleted=show_deleted, |
---|
763 | message=util.sanitize_text( message ), |
---|
764 | status='error' ) ) |
---|
765 | |
---|
766 | else: |
---|
767 | # See if we have any inherited templates. |
---|
768 | info_association, inherited = folder.get_info_association( inherited=True ) |
---|
769 | if info_association and info_association.inheritable: |
---|
770 | template_id = str( info_association.template.id ) |
---|
771 | widgets = folder.get_template_widgets( trans, get_contents=True ) |
---|
772 | processed_widgets = [] |
---|
773 | # The list of widgets may include an AddressField which we need to save if it is new |
---|
774 | for index, widget_dict in enumerate( widgets ): |
---|
775 | widget = widget_dict[ 'widget' ] |
---|
776 | if isinstance( widget, AddressField ): |
---|
777 | value = util.restore_text( params.get( 'field_%i' % index, '' ) ) |
---|
778 | if value == 'new': |
---|
779 | if self.field_param_values_ok( index, 'AddressField', **kwd ): |
---|
780 | # Save the new address |
---|
781 | address = trans.app.model.UserAddress( user=trans.user ) |
---|
782 | self.save_widget_field( trans, address, index, **kwd ) |
---|
783 | widget.value = str( address.id ) |
---|
784 | widget_dict[ 'widget' ] = widget |
---|
785 | processed_widgets.append( widget_dict ) |
---|
786 | # It is now critical to update the value of 'field_%i', replacing the string |
---|
787 | # 'new' with the new address id. This is necessary because the upload_dataset() |
---|
788 | # method below calls the handle_library_params() method, which does not parse the |
---|
789 | # widget fields, it instead pulls form values from kwd. See the FIXME comments in the |
---|
790 | # handle_library_params() method, and the CheckboxField code in the next conditional. |
---|
791 | kwd[ 'field_%i' % index ] = str( address.id ) |
---|
792 | else: |
---|
793 | # The invalid address won't be saved, but we cannot display error |
---|
794 | # messages on the upload form due to the ajax upload already occurring. |
---|
795 | # When we re-engineer the upload process ( currently under way ), we |
---|
796 | # will be able to check the form values before the ajax upload occurs |
---|
797 | # in the background. For now, we'll do nothing... |
---|
798 | pass |
---|
799 | elif isinstance( widget, CheckboxField ): |
---|
800 | # We need to check the value from kwd since util.Params would have munged the list if |
---|
801 | # the checkbox is checked. |
---|
802 | value = kwd.get( 'field_%i' % index, '' ) |
---|
803 | if CheckboxField.is_checked( value ): |
---|
804 | widget.value = 'true' |
---|
805 | widget_dict[ 'widget' ] = widget |
---|
806 | processed_widgets.append( widget_dict ) |
---|
807 | kwd[ 'field_%i' % index ] = 'true' |
---|
808 | else: |
---|
809 | processed_widgets.append( widget_dict ) |
---|
810 | widgets = processed_widgets |
---|
811 | else: |
---|
812 | template_id = 'None' |
---|
813 | widgets = [] |
---|
814 | created_outputs_dict = trans.webapp.controllers[ 'library_common' ].upload_dataset( trans, |
---|
815 | cntrller=cntrller, |
---|
816 | library_id=trans.security.encode_id( library.id ), |
---|
817 | folder_id=trans.security.encode_id( folder.id ), |
---|
818 | template_id=template_id, |
---|
819 | widgets=widgets, |
---|
820 | replace_dataset=replace_dataset, |
---|
821 | **kwd ) |
---|
822 | if created_outputs_dict: |
---|
823 | if cntrller == 'api': |
---|
824 | # created_outputs_dict can only ever be a string if cntrller == 'api' |
---|
825 | if type( created_outputs_dict ) == str: |
---|
826 | return 400, created_outputs_dict |
---|
827 | return 200, created_outputs_dict |
---|
828 | total_added = len( created_outputs_dict.keys() ) |
---|
829 | ldda_id_list = [ str( v.id ) for k, v in created_outputs_dict.items() ] |
---|
830 | created_ldda_ids=",".join( ldda_id_list ) |
---|
831 | if replace_dataset: |
---|
832 | message = "Added %d dataset versions to the library dataset '%s' in the folder '%s'." % ( total_added, replace_dataset_name, folder.name ) |
---|
833 | else: |
---|
834 | if not folder.parent: |
---|
835 | # Libraries have the same name as their root_folder |
---|
836 | message = "Added %d datasets to the library '%s' (each is selected). " % ( total_added, folder.name ) |
---|
837 | else: |
---|
838 | message = "Added %d datasets to the folder '%s' (each is selected). " % ( total_added, folder.name ) |
---|
839 | if cntrller == 'library_admin': |
---|
840 | message += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." |
---|
841 | status='done' |
---|
842 | else: |
---|
843 | # Since permissions on all LibraryDatasetDatasetAssociations must be the same at this point, we only need |
---|
844 | # to check one of them to see if the current user can manage permissions on them. |
---|
845 | check_ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( ldda_id_list[0] ) |
---|
846 | if trans.app.security_agent.can_manage_library_item( current_user_roles, check_ldda ): |
---|
847 | if replace_dataset: |
---|
848 | default_action = '' |
---|
849 | else: |
---|
850 | message += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." |
---|
851 | default_action = 'manage_permissions' |
---|
852 | else: |
---|
853 | default_action = 'add' |
---|
854 | trans.response.send_redirect( web.url_for( controller='library_common', |
---|
855 | action='browse_library', |
---|
856 | cntrller=cntrller, |
---|
857 | id=library_id, |
---|
858 | default_action=default_action, |
---|
859 | created_ldda_ids=created_ldda_ids, |
---|
860 | show_deleted=show_deleted, |
---|
861 | message=util.sanitize_text( message ), |
---|
862 | status='done' ) ) |
---|
863 | else: |
---|
864 | created_ldda_ids = '' |
---|
865 | message = "Upload failed" |
---|
866 | status='error' |
---|
867 | if cntrller == 'api': |
---|
868 | return 400, message |
---|
869 | response_code = 400 |
---|
870 | trans.response.send_redirect( web.url_for( controller='library_common', |
---|
871 | action='browse_library', |
---|
872 | cntrller=cntrller, |
---|
873 | id=library_id, |
---|
874 | created_ldda_ids=created_ldda_ids, |
---|
875 | show_deleted=show_deleted, |
---|
876 | message=util.sanitize_text( message ), |
---|
877 | status=status ) ) |
---|
878 | # Note: if the upload form was submitted due to refresh_on_change for a form field, we cannot re-populate |
---|
879 | # the field for the selected file ( files_0|file_data ) if the user selected one. This is because the value |
---|
880 | # attribute of the html input file type field is typically ignored by browsers as a security precaution. |
---|
881 | |
---|
882 | # See if we have any inherited templates. |
---|
883 | info_association, inherited = folder.get_info_association( inherited=True ) |
---|
884 | if info_association and info_association.inheritable: |
---|
885 | widgets = folder.get_template_widgets( trans, get_contents=True ) |
---|
886 | # Retain contents of widget fields when form was submitted via refresh_on_change. |
---|
887 | widgets = self.populate_widgets_from_kwd( trans, widgets, **kwd ) |
---|
888 | else: |
---|
889 | widgets = [] |
---|
890 | # Send list of data formats to the upload form so the "extension" select list can be populated dynamically |
---|
891 | file_formats = trans.app.datatypes_registry.upload_file_formats |
---|
892 | # Send list of genome builds to the form so the "dbkey" select list can be populated dynamically |
---|
893 | def get_dbkey_options( last_used_build ): |
---|
894 | for dbkey, build_name in util.dbnames: |
---|
895 | yield build_name, dbkey, ( dbkey==last_used_build ) |
---|
896 | dbkeys = get_dbkey_options( last_used_build ) |
---|
897 | # Send the current history to the form to enable importing datasets from history to library |
---|
898 | history = trans.get_history() |
---|
899 | trans.sa_session.refresh( history ) |
---|
900 | if upload_option == 'upload_file' and trans.app.config.nginx_upload_path: |
---|
901 | # If we're using nginx upload, override the form action - |
---|
902 | # url_for is intentionally not used on the base URL here - |
---|
903 | # nginx_upload_path is expected to include the proxy prefix if the |
---|
904 | # administrator intends for it to be part of the URL. |
---|
905 | action = trans.app.config.nginx_upload_path + '?nginx_redir=' + web.url_for( controller='library_common', action='upload_library_dataset' ) |
---|
906 | else: |
---|
907 | action = web.url_for( controller='library_common', action='upload_library_dataset' ) |
---|
908 | upload_option_select_list = self._build_upload_option_select_list( trans, upload_option ) |
---|
909 | roles_select_list = self._build_roles_select_list( trans, cntrller, library, util.listify( roles ) ) |
---|
910 | return trans.fill_template( '/library/common/upload.mako', |
---|
911 | cntrller=cntrller, |
---|
912 | upload_option_select_list=upload_option_select_list, |
---|
913 | upload_option=upload_option, |
---|
914 | action=action, |
---|
915 | library_id=library_id, |
---|
916 | folder_id=folder_id, |
---|
917 | replace_dataset=replace_dataset, |
---|
918 | file_formats=file_formats, |
---|
919 | dbkeys=dbkeys, |
---|
920 | last_used_build=last_used_build, |
---|
921 | roles_select_list=roles_select_list, |
---|
922 | history=history, |
---|
923 | widgets=widgets, |
---|
924 | space_to_tab=space_to_tab, |
---|
925 | link_data_only=link_data_only, |
---|
926 | show_deleted=show_deleted, |
---|
927 | ldda_message=ldda_message, |
---|
928 | message=message, |
---|
929 | status=status ) |
---|
930 | def upload_dataset( self, trans, cntrller, library_id, folder_id, replace_dataset=None, **kwd ): |
---|
931 | # Set up the traditional tool state/params |
---|
932 | tool_id = 'upload1' |
---|
933 | tool = trans.app.toolbox.tools_by_id[ tool_id ] |
---|
934 | state = tool.new_state( trans ) |
---|
935 | errors = tool.update_state( trans, tool.inputs_by_page[0], state.inputs, kwd ) |
---|
936 | tool_params = state.inputs |
---|
937 | dataset_upload_inputs = [] |
---|
938 | for input_name, input in tool.inputs.iteritems(): |
---|
939 | if input.type == "upload_dataset": |
---|
940 | dataset_upload_inputs.append( input ) |
---|
941 | # Library-specific params |
---|
942 | params = util.Params( kwd ) # is this filetoolparam safe? |
---|
943 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
944 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
945 | status = params.get( 'status', 'done' ) |
---|
946 | server_dir = util.restore_text( params.get( 'server_dir', '' ) ) |
---|
947 | if replace_dataset not in [ None, 'None' ]: |
---|
948 | replace_id = trans.security.encode_id( replace_dataset.id ) |
---|
949 | else: |
---|
950 | replace_id = None |
---|
951 | upload_option = params.get( 'upload_option', 'upload_file' ) |
---|
952 | response_code = 200 |
---|
953 | if upload_option == 'upload_directory': |
---|
954 | if server_dir in [ None, 'None', '' ]: |
---|
955 | response_code = 400 |
---|
956 | if cntrller == 'library_admin' or ( cntrller == 'api' and trans.user_is_admin ): |
---|
957 | import_dir = trans.app.config.library_import_dir |
---|
958 | import_dir_desc = 'library_import_dir' |
---|
959 | full_dir = os.path.join( import_dir, server_dir ) |
---|
960 | else: |
---|
961 | import_dir = trans.app.config.user_library_import_dir |
---|
962 | import_dir_desc = 'user_library_import_dir' |
---|
963 | if server_dir == trans.user.email: |
---|
964 | full_dir = os.path.join( import_dir, server_dir ) |
---|
965 | else: |
---|
966 | full_dir = os.path.join( import_dir, trans.user.email, server_dir ) |
---|
967 | if import_dir: |
---|
968 | message = 'Select a directory' |
---|
969 | else: |
---|
970 | response_code = 403 |
---|
971 | message = '"%s" is not defined in the Galaxy configuration file' % import_dir_desc |
---|
972 | elif upload_option == 'upload_paths': |
---|
973 | if not trans.app.config.allow_library_path_paste: |
---|
974 | response_code = 403 |
---|
975 | message = '"allow_library_path_paste" is not defined in the Galaxy configuration file' |
---|
976 | # Some error handling should be added to this method. |
---|
977 | try: |
---|
978 | # FIXME: instead of passing params here ( chiech have been process by util.Params(), the original kwd |
---|
979 | # should be passed so that complex objects that may have been included in the initial request remain. |
---|
980 | library_bunch = upload_common.handle_library_params( trans, params, folder_id, replace_dataset ) |
---|
981 | except: |
---|
982 | response_code = 500 |
---|
983 | message = "Unable to parse upload parameters, please report this error." |
---|
984 | # Proceed with (mostly) regular upload processing if we're still errorless |
---|
985 | if response_code == 200: |
---|
986 | precreated_datasets = upload_common.get_precreated_datasets( trans, tool_params, trans.app.model.LibraryDatasetDatasetAssociation, controller=cntrller ) |
---|
987 | if upload_option == 'upload_file': |
---|
988 | tool_params = upload_common.persist_uploads( tool_params ) |
---|
989 | uploaded_datasets = upload_common.get_uploaded_datasets( trans, cntrller, tool_params, precreated_datasets, dataset_upload_inputs, library_bunch=library_bunch ) |
---|
990 | elif upload_option == 'upload_directory': |
---|
991 | uploaded_datasets, response_code, message = self.get_server_dir_uploaded_datasets( trans, cntrller, params, full_dir, import_dir_desc, library_bunch, response_code, message ) |
---|
992 | elif upload_option == 'upload_paths': |
---|
993 | uploaded_datasets, response_code, message = self.get_path_paste_uploaded_datasets( trans, cntrller, params, library_bunch, response_code, message ) |
---|
994 | upload_common.cleanup_unused_precreated_datasets( precreated_datasets ) |
---|
995 | if upload_option == 'upload_file' and not uploaded_datasets: |
---|
996 | response_code = 400 |
---|
997 | message = 'Select a file, enter a URL or enter text' |
---|
998 | if response_code != 200: |
---|
999 | if cntrller == 'api': |
---|
1000 | return ( response_code, message ) |
---|
1001 | trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1002 | action='upload_library_dataset', |
---|
1003 | cntrller=cntrller, |
---|
1004 | library_id=library_id, |
---|
1005 | folder_id=folder_id, |
---|
1006 | replace_id=replace_id, |
---|
1007 | upload_option=upload_option, |
---|
1008 | show_deleted=show_deleted, |
---|
1009 | message=util.sanitize_text( message ), |
---|
1010 | status='error' ) ) |
---|
1011 | json_file_path = upload_common.create_paramfile( trans, uploaded_datasets ) |
---|
1012 | data_list = [ ud.data for ud in uploaded_datasets ] |
---|
1013 | return upload_common.create_job( trans, tool_params, tool, json_file_path, data_list, folder=library_bunch.folder ) |
---|
1014 | def make_library_uploaded_dataset( self, trans, cntrller, params, name, path, type, library_bunch, in_folder=None ): |
---|
1015 | library_bunch.replace_dataset = None # not valid for these types of upload |
---|
1016 | uploaded_dataset = util.bunch.Bunch() |
---|
1017 | # Remove compressed file extensions, if any |
---|
1018 | new_name = name |
---|
1019 | if new_name.endswith( '.gz' ): |
---|
1020 | new_name = new_name.rstrip( '.gz' ) |
---|
1021 | elif new_name.endswith( '.zip' ): |
---|
1022 | new_name = new_name.rstrip( '.zip' ) |
---|
1023 | uploaded_dataset.name = new_name |
---|
1024 | uploaded_dataset.path = path |
---|
1025 | uploaded_dataset.type = type |
---|
1026 | uploaded_dataset.ext = None |
---|
1027 | uploaded_dataset.file_type = params.file_type |
---|
1028 | uploaded_dataset.dbkey = params.dbkey |
---|
1029 | uploaded_dataset.space_to_tab = params.space_to_tab |
---|
1030 | if in_folder: |
---|
1031 | uploaded_dataset.in_folder = in_folder |
---|
1032 | uploaded_dataset.data = upload_common.new_upload( trans, cntrller, uploaded_dataset, library_bunch ) |
---|
1033 | if params.get( 'link_data_only', False ): |
---|
1034 | uploaded_dataset.link_data_only = True |
---|
1035 | uploaded_dataset.data.file_name = os.path.abspath( path ) |
---|
1036 | # Since we are not copying the file into Galaxy's managed |
---|
1037 | # default file location, the dataset should never be purgable. |
---|
1038 | uploaded_dataset.data.dataset.purgable = False |
---|
1039 | trans.sa_session.add_all( ( uploaded_dataset.data, uploaded_dataset.data.dataset ) ) |
---|
1040 | trans.sa_session.flush() |
---|
1041 | return uploaded_dataset |
---|
1042 | def get_server_dir_uploaded_datasets( self, trans, cntrller, params, full_dir, import_dir_desc, library_bunch, response_code, message ): |
---|
1043 | files = [] |
---|
1044 | try: |
---|
1045 | for entry in os.listdir( full_dir ): |
---|
1046 | # Only import regular files |
---|
1047 | path = os.path.join( full_dir, entry ) |
---|
1048 | if os.path.islink( full_dir ) and params.get( 'link_data_only', False ): |
---|
1049 | # If we're linking instead of copying and the |
---|
1050 | # sub-"directory" in the import dir is actually a symlink, |
---|
1051 | # dereference the symlink, but not any of its contents. |
---|
1052 | link_path = os.readlink( full_dir ) |
---|
1053 | if os.path.isabs( link_path ): |
---|
1054 | path = os.path.join( link_path, entry ) |
---|
1055 | else: |
---|
1056 | path = os.path.abspath( os.path.join( link_path, entry ) ) |
---|
1057 | elif os.path.islink( path ) and os.path.isfile( path ) and params.get( 'link_data_only', False ): |
---|
1058 | # If we're linking instead of copying and the "file" in the |
---|
1059 | # sub-directory of the import dir is actually a symlink, |
---|
1060 | # dereference the symlink (one dereference only, Vasili). |
---|
1061 | link_path = os.readlink( path ) |
---|
1062 | if os.path.isabs( link_path ): |
---|
1063 | path = link_path |
---|
1064 | else: |
---|
1065 | path = os.path.abspath( os.path.join( os.path.dirname( path ), link_path ) ) |
---|
1066 | if os.path.isfile( path ): |
---|
1067 | files.append( path ) |
---|
1068 | except Exception, e: |
---|
1069 | message = "Unable to get file list for configured %s, error: %s" % ( import_dir_desc, str( e ) ) |
---|
1070 | response_code = 500 |
---|
1071 | return None, response_code, message |
---|
1072 | if not files: |
---|
1073 | message = "The directory '%s' contains no valid files" % full_dir |
---|
1074 | response_code = 400 |
---|
1075 | return None, response_code, message |
---|
1076 | uploaded_datasets = [] |
---|
1077 | for file in files: |
---|
1078 | name = os.path.basename( file ) |
---|
1079 | uploaded_datasets.append( self.make_library_uploaded_dataset( trans, cntrller, params, name, file, 'server_dir', library_bunch ) ) |
---|
1080 | return uploaded_datasets, 200, None |
---|
1081 | def get_path_paste_uploaded_datasets( self, trans, cntrller, params, library_bunch, response_code, message ): |
---|
1082 | if params.get( 'filesystem_paths', '' ) == '': |
---|
1083 | message = "No paths entered in the upload form" |
---|
1084 | response_code = 400 |
---|
1085 | return None, response_code, message |
---|
1086 | preserve_dirs = True |
---|
1087 | if params.get( 'dont_preserve_dirs', False ): |
---|
1088 | preserve_dirs = False |
---|
1089 | # locate files |
---|
1090 | bad_paths = [] |
---|
1091 | uploaded_datasets = [] |
---|
1092 | for line in [ l.strip() for l in params.filesystem_paths.splitlines() if l.strip() ]: |
---|
1093 | path = os.path.abspath( line ) |
---|
1094 | if not os.path.exists( path ): |
---|
1095 | bad_paths.append( path ) |
---|
1096 | continue |
---|
1097 | # don't bother processing if we're just going to return an error |
---|
1098 | if not bad_paths: |
---|
1099 | if os.path.isfile( path ): |
---|
1100 | name = os.path.basename( path ) |
---|
1101 | uploaded_datasets.append( self.make_library_uploaded_dataset( trans, cntrller, params, name, path, 'path_paste', library_bunch ) ) |
---|
1102 | for basedir, dirs, files in os.walk( line ): |
---|
1103 | for file in files: |
---|
1104 | file_path = os.path.abspath( os.path.join( basedir, file ) ) |
---|
1105 | if preserve_dirs: |
---|
1106 | in_folder = os.path.dirname( file_path.replace( path, '', 1 ).lstrip( '/' ) ) |
---|
1107 | else: |
---|
1108 | in_folder = None |
---|
1109 | uploaded_datasets.append( self.make_library_uploaded_dataset( trans, |
---|
1110 | cntrller, |
---|
1111 | params, |
---|
1112 | file, |
---|
1113 | file_path, |
---|
1114 | 'path_paste', |
---|
1115 | library_bunch, |
---|
1116 | in_folder ) ) |
---|
1117 | if bad_paths: |
---|
1118 | message = "Invalid paths:<br><ul><li>%s</li></ul>" % "</li><li>".join( bad_paths ) |
---|
1119 | response_code = 400 |
---|
1120 | return None, response_code, message |
---|
1121 | return uploaded_datasets, 200, None |
---|
1122 | @web.expose |
---|
1123 | def add_history_datasets_to_library( self, trans, cntrller, library_id, folder_id, hda_ids='', **kwd ): |
---|
1124 | params = util.Params( kwd ) |
---|
1125 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
1126 | status = params.get( 'status', 'done' ) |
---|
1127 | ldda_message = util.restore_text( params.get( 'ldda_message', '' ) ) |
---|
1128 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
1129 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
1130 | replace_id = params.get( 'replace_id', None ) |
---|
1131 | replace_dataset = None |
---|
1132 | upload_option = params.get( 'upload_option', 'import_from_history' ) |
---|
1133 | if params.get( 'files_0|space_to_tab', False ): |
---|
1134 | space_to_tab = params.get( 'files_0|space_to_tab', '' ) |
---|
1135 | else: |
---|
1136 | space_to_tab = params.get( 'space_to_tab', '' ) |
---|
1137 | link_data_only = params.get( 'link_data_only', '' ) |
---|
1138 | dbkey = params.get( 'dbkey', '?' ) |
---|
1139 | if isinstance( dbkey, list ): |
---|
1140 | last_used_build = dbkey[0] |
---|
1141 | else: |
---|
1142 | last_used_build = dbkey |
---|
1143 | roles = params.get( 'roles', '' ) |
---|
1144 | is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' ) |
---|
1145 | current_user_roles = trans.get_current_user_roles() |
---|
1146 | if replace_id not in [ None, 'None' ]: |
---|
1147 | try: |
---|
1148 | replace_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( trans.security.decode_id( replace_id ) ) |
---|
1149 | except: |
---|
1150 | replace_dataset = None |
---|
1151 | self._check_access( trans, cntrller, is_admin, replace_dataset, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1152 | self._check_modify( trans, cntrller, is_admin, replace_dataset, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1153 | library = replace_dataset.folder.parent_library |
---|
1154 | folder = replace_dataset.folder |
---|
1155 | last_used_build = replace_dataset.library_dataset_dataset_association.dbkey |
---|
1156 | else: |
---|
1157 | folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) ) |
---|
1158 | self._check_access( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1159 | self._check_add( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1160 | library = folder.parent_library |
---|
1161 | last_used_build = folder.genome_build |
---|
1162 | # See if the current history is empty |
---|
1163 | history = trans.get_history() |
---|
1164 | trans.sa_session.refresh( history ) |
---|
1165 | if not history.active_datasets: |
---|
1166 | message = 'Your current history is empty' |
---|
1167 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1168 | action='browse_library', |
---|
1169 | cntrller=cntrller, |
---|
1170 | id=library_id, |
---|
1171 | show_deleted=show_deleted, |
---|
1172 | message=util.sanitize_text( message ), |
---|
1173 | status='error' ) ) |
---|
1174 | if params.get( 'add_history_datasets_to_library_button', False ): |
---|
1175 | hda_ids = util.listify( hda_ids ) |
---|
1176 | if hda_ids: |
---|
1177 | dataset_names = [] |
---|
1178 | created_ldda_ids = '' |
---|
1179 | for hda_id in hda_ids: |
---|
1180 | try: |
---|
1181 | hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( hda_id ) ) |
---|
1182 | except: |
---|
1183 | hda = None |
---|
1184 | self._check_access( trans, cntrller, is_admin, hda, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1185 | if roles: |
---|
1186 | role_ids = roles.split( ',' ) |
---|
1187 | role_obj_list = [ trans.sa_session.query( trans.model.Role ).get( role_id ) for role_id in role_ids ] |
---|
1188 | else: |
---|
1189 | role_obj_list = [] |
---|
1190 | ldda = hda.to_library_dataset_dataset_association( trans, |
---|
1191 | target_folder=folder, |
---|
1192 | replace_dataset=replace_dataset, |
---|
1193 | roles=role_obj_list, |
---|
1194 | ldda_message=ldda_message ) |
---|
1195 | created_ldda_ids = '%s,%s' % ( created_ldda_ids, str( ldda.id ) ) |
---|
1196 | dataset_names.append( ldda.name ) |
---|
1197 | if not replace_dataset: |
---|
1198 | # If replace_dataset is None, the Library level permissions will be taken from the folder and applied to the new |
---|
1199 | # LDDA and LibraryDataset. |
---|
1200 | trans.app.security_agent.copy_library_permissions( folder, ldda ) |
---|
1201 | trans.app.security_agent.copy_library_permissions( folder, ldda.library_dataset ) |
---|
1202 | # Permissions must be the same on the LibraryDatasetDatasetAssociation and the associated LibraryDataset |
---|
1203 | trans.app.security_agent.copy_library_permissions( ldda.library_dataset, ldda ) |
---|
1204 | if created_ldda_ids: |
---|
1205 | created_ldda_ids = created_ldda_ids.lstrip( ',' ) |
---|
1206 | ldda_id_list = created_ldda_ids.split( ',' ) |
---|
1207 | total_added = len( ldda_id_list ) |
---|
1208 | if replace_dataset: |
---|
1209 | message = "Added %d dataset versions to the library dataset '%s' in the folder '%s'." % ( total_added, replace_dataset.name, folder.name ) |
---|
1210 | else: |
---|
1211 | if not folder.parent: |
---|
1212 | # Libraries have the same name as their root_folder |
---|
1213 | message = "Added %d datasets to the library '%s' (each is selected). " % ( total_added, folder.name ) |
---|
1214 | else: |
---|
1215 | message = "Added %d datasets to the folder '%s' (each is selected). " % ( total_added, folder.name ) |
---|
1216 | if cntrller == 'library_admin': |
---|
1217 | message += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." |
---|
1218 | else: |
---|
1219 | # Since permissions on all LibraryDatasetDatasetAssociations must be the same at this point, we only need |
---|
1220 | # to check one of them to see if the current user can manage permissions on them. |
---|
1221 | check_ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( ldda_id_list[0] ) |
---|
1222 | if trans.app.security_agent.can_manage_library_item( current_user_roles, check_ldda ): |
---|
1223 | if replace_dataset: |
---|
1224 | default_action = '' |
---|
1225 | else: |
---|
1226 | message += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." |
---|
1227 | default_action = 'manage_permissions' |
---|
1228 | else: |
---|
1229 | default_action = 'add' |
---|
1230 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1231 | action='browse_library', |
---|
1232 | cntrller=cntrller, |
---|
1233 | id=library_id, |
---|
1234 | created_ldda_ids=created_ldda_ids, |
---|
1235 | show_deleted=show_deleted, |
---|
1236 | message=util.sanitize_text( message ), |
---|
1237 | status='done' ) ) |
---|
1238 | else: |
---|
1239 | message = 'Select at least one dataset from the list of active datasets in your current history' |
---|
1240 | status = 'error' |
---|
1241 | upload_option = params.get( 'upload_option', 'import_from_history' ) |
---|
1242 | widgets = self._get_populated_widgets( folder ) |
---|
1243 | # Send list of data formats to the upload form so the "extension" select list can be populated dynamically |
---|
1244 | file_formats = trans.app.datatypes_registry.upload_file_formats |
---|
1245 | # Send list of genome builds to the form so the "dbkey" select list can be populated dynamically |
---|
1246 | def get_dbkey_options( last_used_build ): |
---|
1247 | for dbkey, build_name in util.dbnames: |
---|
1248 | yield build_name, dbkey, ( dbkey==last_used_build ) |
---|
1249 | dbkeys = get_dbkey_options( last_used_build ) |
---|
1250 | # Send the current history to the form to enable importing datasets from history to library |
---|
1251 | history = trans.get_history() |
---|
1252 | trans.sa_session.refresh( history ) |
---|
1253 | action = 'add_history_datasets_to_library' |
---|
1254 | upload_option_select_list = self._build_upload_option_select_list( trans, upload_option ) |
---|
1255 | roles_select_list = self._build_roles_select_list( trans, cntrller, library, util.listify( roles ) ) |
---|
1256 | return trans.fill_template( "/library/common/upload.mako", |
---|
1257 | cntrller=cntrller, |
---|
1258 | upload_option_select_list=upload_option_select_list, |
---|
1259 | upload_option=upload_option, |
---|
1260 | action=action, |
---|
1261 | library_id=library_id, |
---|
1262 | folder_id=folder_id, |
---|
1263 | replace_dataset=replace_dataset, |
---|
1264 | file_formats=file_formats, |
---|
1265 | dbkeys=dbkeys, |
---|
1266 | last_used_build=last_used_build, |
---|
1267 | roles_select_list=roles_select_list, |
---|
1268 | history=history, |
---|
1269 | widgets=widgets, |
---|
1270 | space_to_tab=space_to_tab, |
---|
1271 | link_data_only=link_data_only, |
---|
1272 | show_deleted=show_deleted, |
---|
1273 | ldda_message=ldda_message, |
---|
1274 | message=message, |
---|
1275 | status=status ) |
---|
1276 | def _build_roles_select_list( self, trans, cntrller, library, selected_role_ids=[] ): |
---|
1277 | # Get the list of legitimate roles to display on the upload form. If the library is public, |
---|
1278 | # all active roles are legitimate. If the library is restricted by the LIBRARY_ACCESS permission, only |
---|
1279 | # the set of all roles associated with users that have that permission are legitimate. |
---|
1280 | legitimate_roles = trans.app.security_agent.get_legitimate_roles( trans, library, cntrller ) |
---|
1281 | if legitimate_roles: |
---|
1282 | # Build the roles multi-select list using the list of legitimate roles, making sure to select any that |
---|
1283 | # were selected before refresh_on_change, if one occurred. |
---|
1284 | roles_select_list = SelectField( "roles", multiple="true", size="5" ) |
---|
1285 | for role in legitimate_roles: |
---|
1286 | selected = str( role.id ) in selected_role_ids |
---|
1287 | roles_select_list.add_option( text=role.name, value=str( role.id ), selected=selected ) |
---|
1288 | return roles_select_list |
---|
1289 | else: |
---|
1290 | return None |
---|
1291 | def _build_upload_option_select_list( self, trans, upload_option ): |
---|
1292 | # Build the upload_option select list |
---|
1293 | upload_refresh_on_change_values = [ option_value for option_value, option_label in trans.model.LibraryDataset.upload_options ] |
---|
1294 | upload_option_select_list = SelectField( 'upload_option', |
---|
1295 | refresh_on_change=True, |
---|
1296 | refresh_on_change_values=upload_refresh_on_change_values ) |
---|
1297 | for option_value, option_label in trans.model.LibraryDataset.upload_options: |
---|
1298 | upload_option_select_list.add_option( option_label, option_value, selected=option_value==upload_option ) |
---|
1299 | return upload_option_select_list |
---|
1300 | def _get_populated_widgets( self, folder ): |
---|
1301 | # See if we have any inherited templates. |
---|
1302 | info_association, inherited = folder.get_info_association( inherited=True ) |
---|
1303 | if info_association and info_association.inheritable: |
---|
1304 | widgets = folder.get_template_widgets( trans, get_contents=True ) |
---|
1305 | # Retain contents of widget fields when form was submitted via refresh_on_change. |
---|
1306 | return self.populate_widgets_from_kwd( trans, widgets, **kwd ) |
---|
1307 | else: |
---|
1308 | return [] |
---|
1309 | @web.expose |
---|
1310 | def download_dataset_from_folder( self, trans, cntrller, id, library_id=None, **kwd ): |
---|
1311 | """Catches the dataset id and displays file contents as directed""" |
---|
1312 | show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) ) |
---|
1313 | params = util.Params( kwd ) |
---|
1314 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
1315 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
1316 | current_user_roles = trans.get_current_user_roles() |
---|
1317 | try: |
---|
1318 | ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( id ) ) |
---|
1319 | except: |
---|
1320 | ldda = None |
---|
1321 | self._check_access( trans, cntrller, is_admin, ldda, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1322 | composite_extensions = trans.app.datatypes_registry.get_composite_extensions( ) |
---|
1323 | ext = ldda.extension |
---|
1324 | if ext in composite_extensions: |
---|
1325 | # is composite - must return a zip of contents and the html file itself - ugh - should be reversible at upload! |
---|
1326 | # use act_on_multiple_datasets( self, trans, cntrller, library_id, ldda_ids='', **kwd ) since it does what we need |
---|
1327 | kwd['do_action'] = 'zip' |
---|
1328 | return self.act_on_multiple_datasets( trans, cntrller, library_id, ldda_ids=[id,], **kwd ) |
---|
1329 | else: |
---|
1330 | mime = trans.app.datatypes_registry.get_mimetype_by_extension( ldda.extension.lower() ) |
---|
1331 | trans.response.set_content_type( mime ) |
---|
1332 | fStat = os.stat( ldda.file_name ) |
---|
1333 | trans.response.headers[ 'Content-Length' ] = int( fStat.st_size ) |
---|
1334 | valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' |
---|
1335 | fname = ldda.name |
---|
1336 | fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ] |
---|
1337 | trans.response.headers[ "Content-Disposition" ] = "attachment; filename=%s" % fname |
---|
1338 | try: |
---|
1339 | return open( ldda.file_name ) |
---|
1340 | except: |
---|
1341 | message = 'This dataset contains no content' |
---|
1342 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1343 | action='browse_library', |
---|
1344 | cntrller=cntrller, |
---|
1345 | use_panels=use_panels, |
---|
1346 | id=library_id, |
---|
1347 | show_deleted=show_deleted, |
---|
1348 | message=util.sanitize_text( message ), |
---|
1349 | status='error' ) ) |
---|
1350 | @web.expose |
---|
1351 | def library_dataset_info( self, trans, cntrller, id, library_id, **kwd ): |
---|
1352 | params = util.Params( kwd ) |
---|
1353 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
1354 | status = params.get( 'status', 'done' ) |
---|
1355 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
1356 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
1357 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
1358 | current_user_roles = trans.get_current_user_roles() |
---|
1359 | try: |
---|
1360 | library_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( trans.security.decode_id( id ) ) |
---|
1361 | except: |
---|
1362 | library_dataset = None |
---|
1363 | self._check_access( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1364 | if params.get( 'edit_attributes_button', False ): |
---|
1365 | self._check_modify( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1366 | old_name = library_dataset.name |
---|
1367 | new_name = util.restore_text( params.get( 'name', '' ) ) |
---|
1368 | new_info = util.restore_text( params.get( 'info', '' ) ) |
---|
1369 | if not new_name: |
---|
1370 | message = 'Enter a valid name' |
---|
1371 | status = 'error' |
---|
1372 | else: |
---|
1373 | library_dataset.name = new_name |
---|
1374 | library_dataset.info = new_info |
---|
1375 | trans.sa_session.add( library_dataset ) |
---|
1376 | trans.sa_session.flush() |
---|
1377 | message = "Information updated for library dataset '%s'." % library_dataset.name |
---|
1378 | status = 'done' |
---|
1379 | # See if we have any associated templates |
---|
1380 | widgets = [] |
---|
1381 | widget_fields_have_contents = False |
---|
1382 | info_association, inherited = library_dataset.library_dataset_dataset_association.get_info_association() |
---|
1383 | if info_association and ( not( inherited ) or info_association.inheritable ): |
---|
1384 | widgets = library_dataset.library_dataset_dataset_association.get_template_widgets( trans ) |
---|
1385 | widget_fields_have_contents = self.widget_fields_have_contents( widgets ) |
---|
1386 | return trans.fill_template( '/library/common/library_dataset_info.mako', |
---|
1387 | cntrller=cntrller, |
---|
1388 | use_panels=use_panels, |
---|
1389 | library_dataset=library_dataset, |
---|
1390 | library_id=library_id, |
---|
1391 | current_user_roles=current_user_roles, |
---|
1392 | info_association=info_association, |
---|
1393 | inherited=inherited, |
---|
1394 | widgets=widgets, |
---|
1395 | widget_fields_have_contents=widget_fields_have_contents, |
---|
1396 | show_deleted=show_deleted, |
---|
1397 | message=message, |
---|
1398 | status=status ) |
---|
1399 | @web.expose |
---|
1400 | def library_dataset_permissions( self, trans, cntrller, id, library_id, **kwd ): |
---|
1401 | params = util.Params( kwd ) |
---|
1402 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
1403 | status = params.get( 'status', 'done' ) |
---|
1404 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
1405 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
1406 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
1407 | current_user_roles = trans.get_current_user_roles() |
---|
1408 | try: |
---|
1409 | library_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( trans.security.decode_id( id ) ) |
---|
1410 | except: |
---|
1411 | library_dataset = None |
---|
1412 | self._check_access( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1413 | self._check_manage( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1414 | if params.get( 'update_roles_button', False ): |
---|
1415 | # The user clicked the Save button on the 'Associate With Roles' form |
---|
1416 | permissions = {} |
---|
1417 | for k, v in trans.app.model.Library.permitted_actions.items(): |
---|
1418 | if k != 'LIBRARY_ACCESS': |
---|
1419 | # LIBRARY_ACCESS is a special permission set only at the library level |
---|
1420 | # and it is not inherited. |
---|
1421 | in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ] |
---|
1422 | permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles |
---|
1423 | # Set the LIBRARY permissions on the LibraryDataset |
---|
1424 | # NOTE: the LibraryDataset and LibraryDatasetDatasetAssociation will be set with the same permissions |
---|
1425 | trans.app.security_agent.set_all_library_permissions( library_dataset, permissions ) |
---|
1426 | trans.sa_session.refresh( library_dataset ) |
---|
1427 | # Set the LIBRARY permissions on the LibraryDatasetDatasetAssociation |
---|
1428 | trans.app.security_agent.set_all_library_permissions( library_dataset.library_dataset_dataset_association, permissions ) |
---|
1429 | trans.sa_session.refresh( library_dataset.library_dataset_dataset_association ) |
---|
1430 | message = "Permisisons updated for library dataset '%s'." % library_dataset.name |
---|
1431 | status = 'done' |
---|
1432 | roles = trans.app.security_agent.get_legitimate_roles( trans, library_dataset, cntrller ) |
---|
1433 | return trans.fill_template( '/library/common/library_dataset_permissions.mako', |
---|
1434 | cntrller=cntrller, |
---|
1435 | use_panels=use_panels, |
---|
1436 | library_dataset=library_dataset, |
---|
1437 | library_id=library_id, |
---|
1438 | roles=roles, |
---|
1439 | current_user_roles=current_user_roles, |
---|
1440 | show_deleted=show_deleted, |
---|
1441 | message=message, |
---|
1442 | status=status ) |
---|
1443 | @web.expose |
---|
1444 | def make_library_item_public( self, trans, cntrller, library_id, item_type, id, **kwd ): |
---|
1445 | params = util.Params( kwd ) |
---|
1446 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
1447 | status = params.get( 'status', 'done' ) |
---|
1448 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
1449 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
1450 | current_user_roles = trans.get_current_user_roles() |
---|
1451 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
1452 | if item_type == 'library': |
---|
1453 | library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( id ) ) |
---|
1454 | self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1455 | self._check_manage( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1456 | contents = util.string_as_bool( params.get( 'contents', 'False' ) ) |
---|
1457 | trans.app.security_agent.make_library_public( library, contents=contents ) |
---|
1458 | if contents: |
---|
1459 | message = "The data library (%s) and all it's contents have been made publicly accessible." % library.name |
---|
1460 | else: |
---|
1461 | message = "The data library (%s) has been made publicly accessible, but access to it's contents has been left unchanged." % library.name |
---|
1462 | elif item_type == 'folder': |
---|
1463 | folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( id ) ) |
---|
1464 | self._check_access( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1465 | self._check_manage( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1466 | trans.app.security_agent.make_folder_public( folder ) |
---|
1467 | message = "All of the contents of folder (%s) have been made publicly accessible." % folder.name |
---|
1468 | elif item_type == 'ldda': |
---|
1469 | ldda = trans.sa_session.query( trans.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( id ) ) |
---|
1470 | self._check_access( trans, cntrller, is_admin, ldda.library_dataset, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1471 | self._check_manage( trans, cntrller, is_admin, ldda.library_dataset, current_user_roles, use_panels, library_id, show_deleted ) |
---|
1472 | trans.app.security_agent.make_dataset_public( ldda.dataset ) |
---|
1473 | message = "The libary dataset (%s) has been made publicly accessible." % ldda.name |
---|
1474 | else: |
---|
1475 | message = "Invalid item_type (%s) received." % str( item_type ) |
---|
1476 | status = 'error' |
---|
1477 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1478 | action='browse_library', |
---|
1479 | cntrller=cntrller, |
---|
1480 | use_panels=use_panels, |
---|
1481 | id=library_id, |
---|
1482 | show_deleted=show_deleted, |
---|
1483 | message=util.sanitize_text( message ), |
---|
1484 | status=status ) ) |
---|
1485 | @web.expose |
---|
1486 | def act_on_multiple_datasets( self, trans, cntrller, library_id, ldda_ids='', **kwd ): |
---|
1487 | class NgxZip( object ): |
---|
1488 | def __init__( self, url_base ): |
---|
1489 | self.files = {} |
---|
1490 | self.url_base = url_base |
---|
1491 | def add( self, file, relpath ): |
---|
1492 | self.files[file] = relpath |
---|
1493 | def __str__( self ): |
---|
1494 | rval = '' |
---|
1495 | for fname, relpath in self.files.items(): |
---|
1496 | crc = '-' |
---|
1497 | size = os.stat( fname ).st_size |
---|
1498 | quoted_fname = urllib.quote_plus( fname, '/' ) |
---|
1499 | rval += '%s %i %s%s %s\r\n' % ( crc, size, self.url_base, quoted_fname, relpath ) |
---|
1500 | return rval |
---|
1501 | # Perform an action on a list of library datasets. |
---|
1502 | params = util.Params( kwd ) |
---|
1503 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
1504 | status = params.get( 'status', 'done' ) |
---|
1505 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
1506 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
1507 | action = params.get( 'do_action', None ) |
---|
1508 | lddas = [] |
---|
1509 | error = False |
---|
1510 | is_admin = trans.user_is_admin() and cntrller == 'library_admin' |
---|
1511 | current_user_roles = trans.get_current_user_roles() |
---|
1512 | if not ldda_ids: |
---|
1513 | error = True |
---|
1514 | message = 'You must select at least one dataset.' |
---|
1515 | elif not action: |
---|
1516 | error = True |
---|
1517 | message = 'You must select an action to perform on the selected datasets.' |
---|
1518 | else: |
---|
1519 | # Set up the list of lddas for later, and get permission checks out of the way so we don't have to do it in multiple places later. |
---|
1520 | ldda_ids = util.listify( ldda_ids ) |
---|
1521 | for ldda_id in ldda_ids: |
---|
1522 | try: |
---|
1523 | ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( ldda_id ) ) |
---|
1524 | except: |
---|
1525 | ldda = None |
---|
1526 | if not ldda or ( not is_admin and not trans.app.security_agent.can_access_library_item( current_user_roles, ldda, trans.user ) ): |
---|
1527 | error = True |
---|
1528 | message = "Invalid library dataset id ( %s ) specified." % str( ldda_id ) |
---|
1529 | break |
---|
1530 | lddas.append( ldda ) |
---|
1531 | if action == 'import_to_history' or action == 'add': |
---|
1532 | if trans.get_history() is None: |
---|
1533 | # Must be a bot sending a request without having a history. |
---|
1534 | error = True |
---|
1535 | message = "You do not have a current history" |
---|
1536 | elif action == 'manage_permissions': |
---|
1537 | if not is_admin: |
---|
1538 | for ldda in lddas: |
---|
1539 | if not ( trans.app.security_agent.can_manage_library_item( current_user_roles, ldda ) and \ |
---|
1540 | trans.app.security_agent.can_manage_dataset( current_user_roles, ldda.dataset ) ): |
---|
1541 | error = True |
---|
1542 | message = "You are not authorized to manage permissions on library dataset '%s'." % ldda.name |
---|
1543 | break |
---|
1544 | elif action == 'delete': |
---|
1545 | if not is_admin: |
---|
1546 | for ldda in lddas: |
---|
1547 | if not trans.app.security_agent.can_modify_library_item( current_user_roles, ldda ): |
---|
1548 | error = True |
---|
1549 | message = "You are not authorized to modify library dataset '%s'." % ldda.name |
---|
1550 | break |
---|
1551 | if error: |
---|
1552 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1553 | action='browse_library', |
---|
1554 | cntrller=cntrller, |
---|
1555 | use_panels=use_panels, |
---|
1556 | id=library_id, |
---|
1557 | show_deleted=show_deleted, |
---|
1558 | message=util.sanitize_text( message ), |
---|
1559 | status='error' ) ) |
---|
1560 | if action == 'import_to_history' or action == 'add': |
---|
1561 | history = trans.get_history() |
---|
1562 | total_imported_lddas = 0 |
---|
1563 | message = '' |
---|
1564 | status = 'done' |
---|
1565 | for ldda in lddas: |
---|
1566 | if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]: |
---|
1567 | message += "Cannot import dataset '%s' since its state is '%s'. " % ( ldda.name, ldda.dataset.state ) |
---|
1568 | status = 'error' |
---|
1569 | elif ldda.dataset.state in [ 'ok', 'error' ]: |
---|
1570 | hda = ldda.to_history_dataset_association( target_history=history, add_to_history=True ) |
---|
1571 | total_imported_lddas += 1 |
---|
1572 | if total_imported_lddas: |
---|
1573 | trans.sa_session.add( history ) |
---|
1574 | trans.sa_session.flush() |
---|
1575 | message += "%i dataset(s) have been imported into your history. " % total_imported_lddas |
---|
1576 | elif action == 'manage_permissions': |
---|
1577 | trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1578 | action='ldda_permissions', |
---|
1579 | cntrller=cntrller, |
---|
1580 | use_panels=use_panels, |
---|
1581 | library_id=library_id, |
---|
1582 | folder_id=trans.security.encode_id( lddas[0].library_dataset.folder.id ), |
---|
1583 | id=",".join( ldda_ids ), |
---|
1584 | show_deleted=show_deleted, |
---|
1585 | message=util.sanitize_text( message ), |
---|
1586 | status=status ) ) |
---|
1587 | elif action == 'delete': |
---|
1588 | for ldda in lddas: |
---|
1589 | # Do not delete the association, just delete the library_dataset. The |
---|
1590 | # cleanup_datasets.py script handles everything else. |
---|
1591 | ld = ldda.library_dataset |
---|
1592 | ld.deleted = True |
---|
1593 | trans.sa_session.add( ld ) |
---|
1594 | trans.sa_session.flush() |
---|
1595 | message = "The selected datasets have been removed from this data library" |
---|
1596 | elif action in ['zip','tgz','tbz','ngxzip']: |
---|
1597 | error = False |
---|
1598 | killme = string.punctuation + string.whitespace |
---|
1599 | trantab = string.maketrans(killme,'_'*len(killme)) |
---|
1600 | try: |
---|
1601 | outext = 'zip' |
---|
1602 | if action == 'zip': |
---|
1603 | # Can't use mkstemp - the file must not exist first |
---|
1604 | tmpd = tempfile.mkdtemp() |
---|
1605 | tmpf = os.path.join( tmpd, 'library_download.' + action ) |
---|
1606 | if ziptype == '64' and trans.app.config.upstream_gzip: |
---|
1607 | archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True ) |
---|
1608 | elif ziptype == '64': |
---|
1609 | archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True ) |
---|
1610 | elif trans.app.config.upstream_gzip: |
---|
1611 | archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED ) |
---|
1612 | else: |
---|
1613 | archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED ) |
---|
1614 | archive.add = lambda x, y: archive.write( x, y.encode('CP437') ) |
---|
1615 | elif action == 'tgz': |
---|
1616 | if trans.app.config.upstream_gzip: |
---|
1617 | archive = util.streamball.StreamBall( 'w|' ) |
---|
1618 | outext = 'tar' |
---|
1619 | else: |
---|
1620 | archive = util.streamball.StreamBall( 'w|gz' ) |
---|
1621 | outext = 'tgz' |
---|
1622 | elif action == 'tbz': |
---|
1623 | archive = util.streamball.StreamBall( 'w|bz2' ) |
---|
1624 | outext = 'tbz2' |
---|
1625 | elif action == 'ngxzip': |
---|
1626 | archive = NgxZip( trans.app.config.nginx_x_archive_files_base ) |
---|
1627 | except (OSError, zipfile.BadZipfile): |
---|
1628 | error = True |
---|
1629 | log.exception( "Unable to create archive for download" ) |
---|
1630 | message = "Unable to create archive for download, please report this error" |
---|
1631 | status = 'error' |
---|
1632 | except: |
---|
1633 | error = True |
---|
1634 | log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0]) |
---|
1635 | message = "Unable to create archive for download, please report - %s" % sys.exc_info()[0] |
---|
1636 | status = 'error' |
---|
1637 | if not error: |
---|
1638 | composite_extensions = trans.app.datatypes_registry.get_composite_extensions( ) |
---|
1639 | seen = [] |
---|
1640 | for ldda in lddas: |
---|
1641 | if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]: |
---|
1642 | continue |
---|
1643 | ext = ldda.extension |
---|
1644 | is_composite = ext in composite_extensions |
---|
1645 | path = "" |
---|
1646 | parent_folder = ldda.library_dataset.folder |
---|
1647 | while parent_folder is not None: |
---|
1648 | # Exclude the now-hidden "root folder" |
---|
1649 | if parent_folder.parent is None: |
---|
1650 | path = os.path.join( parent_folder.library_root[0].name, path ) |
---|
1651 | break |
---|
1652 | path = os.path.join( parent_folder.name, path ) |
---|
1653 | parent_folder = parent_folder.parent |
---|
1654 | path += ldda.name |
---|
1655 | while path in seen: |
---|
1656 | path += '_' |
---|
1657 | seen.append( path ) |
---|
1658 | zpath = os.path.split(path)[-1] # comes as base_name/fname |
---|
1659 | outfname,zpathext = os.path.splitext(zpath) |
---|
1660 | if is_composite: |
---|
1661 | # need to add all the components from the extra_files_path to the zip |
---|
1662 | if zpathext == '': |
---|
1663 | zpath = '%s.html' % zpath # fake the real nature of the html file |
---|
1664 | try: |
---|
1665 | archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set |
---|
1666 | except IOError: |
---|
1667 | error = True |
---|
1668 | log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name) |
---|
1669 | message = "Unable to create archive for download, please report this error" |
---|
1670 | status = 'error' |
---|
1671 | continue |
---|
1672 | flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths |
---|
1673 | for fpath in flist: |
---|
1674 | efp,fname = os.path.split(fpath) |
---|
1675 | if fname > '': |
---|
1676 | fname = fname.translate(trantab) |
---|
1677 | try: |
---|
1678 | archive.add( fpath,fname ) |
---|
1679 | except IOError: |
---|
1680 | error = True |
---|
1681 | log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname)) |
---|
1682 | message = "Unable to create archive for download, please report this error" |
---|
1683 | status = 'error' |
---|
1684 | continue |
---|
1685 | else: # simple case |
---|
1686 | try: |
---|
1687 | archive.add( ldda.dataset.file_name, path ) |
---|
1688 | except IOError: |
---|
1689 | error = True |
---|
1690 | log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name) |
---|
1691 | message = "Unable to create archive for download, please report this error" |
---|
1692 | status = 'error' |
---|
1693 | if not error: |
---|
1694 | lname = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ).name |
---|
1695 | fname = lname.replace( ' ', '_' ) + '_files' |
---|
1696 | if action == 'zip': |
---|
1697 | archive.close() |
---|
1698 | tmpfh = open( tmpf ) |
---|
1699 | # clean up now |
---|
1700 | try: |
---|
1701 | os.unlink( tmpf ) |
---|
1702 | os.rmdir( tmpd ) |
---|
1703 | except OSError: |
---|
1704 | error = True |
---|
1705 | log.exception( "Unable to remove temporary library download archive and directory" ) |
---|
1706 | message = "Unable to create archive for download, please report this error" |
---|
1707 | status = 'error' |
---|
1708 | if not error: |
---|
1709 | trans.response.set_content_type( "application/x-zip-compressed" ) |
---|
1710 | trans.response.headers[ "Content-Disposition" ] = "attachment; filename=%s.%s" % (fname,outext) |
---|
1711 | return tmpfh |
---|
1712 | elif action == 'ngxzip': |
---|
1713 | trans.response.set_content_type( "application/zip" ) |
---|
1714 | trans.response.headers[ "Content-Disposition" ] = "attachment; filename=%s.%s" % (fname,outext) |
---|
1715 | trans.response.headers[ "X-Archive-Files" ] = "zip" |
---|
1716 | return archive |
---|
1717 | else: |
---|
1718 | trans.response.set_content_type( "application/x-tar" ) |
---|
1719 | trans.response.headers[ "Content-Disposition" ] = "attachment; filename=%s.%s" % (fname,outext) |
---|
1720 | archive.wsgi_status = trans.response.wsgi_status() |
---|
1721 | archive.wsgi_headeritems = trans.response.wsgi_headeritems() |
---|
1722 | return archive.stream |
---|
1723 | else: |
---|
1724 | status = 'error' |
---|
1725 | message = 'Invalid action ( %s ) specified.' % action |
---|
1726 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1727 | action='browse_library', |
---|
1728 | cntrller=cntrller, |
---|
1729 | use_panels=use_panels, |
---|
1730 | id=library_id, |
---|
1731 | show_deleted=show_deleted, |
---|
1732 | message=util.sanitize_text( message ), |
---|
1733 | status=status ) ) |
---|
1734 | def get_item_and_stuff( self, trans, item_type, library_id, folder_id, ldda_id, is_admin ): |
---|
1735 | # Return an item, description, action and an id based on the item_type. |
---|
1736 | message = None |
---|
1737 | current_user_roles = trans.get_current_user_roles() |
---|
1738 | if item_type == 'library': |
---|
1739 | try: |
---|
1740 | item = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) |
---|
1741 | except: |
---|
1742 | item = None |
---|
1743 | item_desc = 'data library' |
---|
1744 | action = 'library_info' |
---|
1745 | id = library_id |
---|
1746 | elif item_type == 'folder': |
---|
1747 | try: |
---|
1748 | item = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) ) |
---|
1749 | except: |
---|
1750 | item = None |
---|
1751 | item_desc = 'folder' |
---|
1752 | action = 'folder_info' |
---|
1753 | id = folder_id |
---|
1754 | elif item_type == 'ldda': |
---|
1755 | try: |
---|
1756 | item = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( ldda_id ) ) |
---|
1757 | except: |
---|
1758 | item = None |
---|
1759 | item_desc = 'dataset' |
---|
1760 | action = 'ldda_edit_info' |
---|
1761 | id = ldda_id |
---|
1762 | else: |
---|
1763 | item = None |
---|
1764 | message = "Invalid library item type ( %s )" % str( item_type ) |
---|
1765 | if not item or not ( is_admin or trans.app.security_agent.can_access_library_item( current_user_roles, item, trans.user ) ): |
---|
1766 | if message is None: |
---|
1767 | message = "Invalid %s id ( %s ) specified." % ( item_desc, str( id ) ) |
---|
1768 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1769 | action='browse_library', |
---|
1770 | cntrller='library', # cheating a bit here |
---|
1771 | id=library_id, |
---|
1772 | message=util.sanitize_text( message ), |
---|
1773 | status='error' ) ) |
---|
1774 | return item, item_desc, action, id |
---|
1775 | @web.expose |
---|
1776 | def add_template( self, trans, cntrller, item_type, library_id, folder_id=None, ldda_id=None, **kwd ): |
---|
1777 | # Template can only be added to a Library, Folder or LibraryDatasetDatasetAssociation. |
---|
1778 | forms = self.get_all_forms( trans, |
---|
1779 | filter=dict( deleted=False ), |
---|
1780 | form_type=trans.app.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE ) |
---|
1781 | if not forms: |
---|
1782 | message = "There are no forms on which to base the template, so create a form and then add the template." |
---|
1783 | return trans.response.send_redirect( web.url_for( controller='forms', |
---|
1784 | action='create_request', |
---|
1785 | message=message, |
---|
1786 | status='done', |
---|
1787 | form_type=trans.app.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE ) ) |
---|
1788 | params = util.Params( kwd ) |
---|
1789 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
1790 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
1791 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
1792 | action = '' |
---|
1793 | status = params.get( 'status', 'done' ) |
---|
1794 | is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' ) |
---|
1795 | current_user_roles = trans.get_current_user_roles() |
---|
1796 | try: |
---|
1797 | item, item_desc, action, id = self.get_item_and_stuff( trans, item_type, library_id, folder_id, ldda_id, is_admin ) |
---|
1798 | except ValueError: |
---|
1799 | # At this point, the client has already redirected, so this is just here to prevent the unnecessary traceback |
---|
1800 | return None |
---|
1801 | if not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, item ) ): |
---|
1802 | message = "You are not authorized to modify %s '%s'." % ( item_desc, item.name ) |
---|
1803 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1804 | action='browse_library', |
---|
1805 | cntrller=cntrller, |
---|
1806 | id=library_id, |
---|
1807 | show_deleted=show_deleted, |
---|
1808 | message=util.sanitize_text( message ), |
---|
1809 | status='error' ) ) |
---|
1810 | # If the inheritable checkbox is checked, the param will be in the request |
---|
1811 | inheritable = CheckboxField.is_checked( params.get( 'inheritable', '' ) ) |
---|
1812 | if params.get( 'add_template_button', False ): |
---|
1813 | form_id = params.get( 'form_id', 'none' ) |
---|
1814 | if form_id not in [ None, 'None', 'none' ]: |
---|
1815 | form = trans.sa_session.query( trans.app.model.FormDefinition ).get( trans.security.decode_id( form_id ) ) |
---|
1816 | form_values = trans.app.model.FormValues( form, [] ) |
---|
1817 | trans.sa_session.add( form_values ) |
---|
1818 | trans.sa_session.flush() |
---|
1819 | if item_type == 'library': |
---|
1820 | assoc = trans.app.model.LibraryInfoAssociation( item, form, form_values, inheritable=inheritable ) |
---|
1821 | elif item_type == 'folder': |
---|
1822 | assoc = trans.app.model.LibraryFolderInfoAssociation( item, form, form_values, inheritable=inheritable ) |
---|
1823 | elif item_type == 'ldda': |
---|
1824 | assoc = trans.app.model.LibraryDatasetDatasetInfoAssociation( item, form, form_values ) |
---|
1825 | trans.sa_session.add( assoc ) |
---|
1826 | trans.sa_session.flush() |
---|
1827 | message = 'A template based on the form "%s" has been added to this %s.' % ( form.name, item_desc ) |
---|
1828 | trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1829 | action=action, |
---|
1830 | cntrller=cntrller, |
---|
1831 | use_panels=use_panels, |
---|
1832 | library_id=library_id, |
---|
1833 | folder_id=folder_id, |
---|
1834 | id=id, |
---|
1835 | show_deleted=show_deleted, |
---|
1836 | message=message, |
---|
1837 | status='done' ) ) |
---|
1838 | else: |
---|
1839 | message = "Select a form on which to base the template." |
---|
1840 | status = "error" |
---|
1841 | def generate_template_stuff( trans, forms, form_id ): |
---|
1842 | # Returns the following: |
---|
1843 | # - a list of template ids |
---|
1844 | # - a list of dictionaries whose keys are template ids and whose values are templates widgets. |
---|
1845 | # The dictionary built using the received forms param |
---|
1846 | # - a select list whose options are templates |
---|
1847 | template_ids = [ 'none' ] |
---|
1848 | widgets = [] |
---|
1849 | for form in forms: |
---|
1850 | template_ids.append( trans.security.encode_id( form.id ) ) |
---|
1851 | template_select_list = SelectField( 'form_id', |
---|
1852 | refresh_on_change=True, |
---|
1853 | refresh_on_change_values=template_ids[1:] ) |
---|
1854 | if form_id == 'none': |
---|
1855 | template_select_list.add_option( 'Select one', 'none', selected=True ) |
---|
1856 | decoded_form_id = None |
---|
1857 | else: |
---|
1858 | template_select_list.add_option( 'Select one', 'none' ) |
---|
1859 | decoded_form_id = trans.security.decode_id( form_id ) |
---|
1860 | for form in forms: |
---|
1861 | if decoded_form_id and decoded_form_id == form.id: |
---|
1862 | template_select_list.add_option( form.name, trans.security.encode_id( form.id ), selected=True ) |
---|
1863 | widgets = form.get_widgets( trans.user ) |
---|
1864 | else: |
---|
1865 | template_select_list.add_option( form.name, trans.security.encode_id( form.id ) ) |
---|
1866 | return template_ids, widgets, template_select_list |
---|
1867 | if params.get( 'refresh', False ): |
---|
1868 | template_ids, widgets, template_select_list = generate_template_stuff( trans, forms, kwd.get( 'form_id' ) ) |
---|
1869 | else: |
---|
1870 | template_ids, widgets, template_select_list = generate_template_stuff( trans, forms, 'none' ) |
---|
1871 | return trans.fill_template( '/library/common/select_template.mako', |
---|
1872 | cntrller=cntrller, |
---|
1873 | use_panels=use_panels, |
---|
1874 | item_name=item.name, |
---|
1875 | item_desc=item_desc, |
---|
1876 | item_type=item_type, |
---|
1877 | library_id=library_id, |
---|
1878 | folder_id=folder_id, |
---|
1879 | ldda_id=ldda_id, |
---|
1880 | template_ids=template_ids, |
---|
1881 | widgets=widgets, |
---|
1882 | template_select_list=template_select_list, |
---|
1883 | inheritable_checked=inheritable, |
---|
1884 | show_deleted=show_deleted, |
---|
1885 | message=message, |
---|
1886 | status=status ) |
---|
1887 | @web.expose |
---|
1888 | def manage_template_inheritance( self, trans, cntrller, item_type, library_id, folder_id=None, ldda_id=None, **kwd ): |
---|
1889 | params = util.Params( kwd ) |
---|
1890 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
1891 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
1892 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
1893 | status = params.get( 'status', 'done' ) |
---|
1894 | is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' ) |
---|
1895 | current_user_roles = trans.get_current_user_roles() |
---|
1896 | try: |
---|
1897 | item, item_desc, action, id = self.get_item_and_stuff( trans, item_type, library_id, folder_id, ldda_id, is_admin ) |
---|
1898 | except ValueError: |
---|
1899 | return None |
---|
1900 | if not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, item ) ): |
---|
1901 | message = "You are not authorized to modify %s '%s'." % ( item_desc, item.name ) |
---|
1902 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1903 | action='browse_library', |
---|
1904 | cntrller=cntrller, |
---|
1905 | id=library_id, |
---|
1906 | show_deleted=show_deleted, |
---|
1907 | message=util.sanitize_text( message ), |
---|
1908 | status='error' ) ) |
---|
1909 | info_association, inherited = item.get_info_association( restrict=True ) |
---|
1910 | if info_association: |
---|
1911 | if info_association.inheritable: |
---|
1912 | message = "The template for this %s will no longer be inherited to contained folders and datasets." % item_desc |
---|
1913 | else: |
---|
1914 | message = "The template for this %s will now be inherited to contained folders and datasets." % item_desc |
---|
1915 | info_association.inheritable = not( info_association.inheritable ) |
---|
1916 | trans.sa_session.add( info_association ) |
---|
1917 | trans.sa_session.flush() |
---|
1918 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1919 | action=action, |
---|
1920 | cntrller=cntrller, |
---|
1921 | use_panels=use_panels, |
---|
1922 | library_id=library_id, |
---|
1923 | folder_id=folder_id, |
---|
1924 | id=id, |
---|
1925 | show_deleted=show_deleted, |
---|
1926 | message=util.sanitize_text( message ), |
---|
1927 | status='done' ) ) |
---|
1928 | @web.expose |
---|
1929 | def edit_template( self, trans, cntrller, item_type, library_id, folder_id=None, ldda_id=None, edited=False, **kwd ): |
---|
1930 | # Edit the template itself, keeping existing field contents, if any. |
---|
1931 | params = util.Params( kwd ) |
---|
1932 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
1933 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
1934 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
1935 | status = params.get( 'status', 'done' ) |
---|
1936 | is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' ) |
---|
1937 | current_user_roles = trans.get_current_user_roles() |
---|
1938 | try: |
---|
1939 | item, item_desc, action, id = self.get_item_and_stuff( trans, item_type, library_id, folder_id, ldda_id, is_admin ) |
---|
1940 | except ValueError: |
---|
1941 | return None |
---|
1942 | if not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, item ) ): |
---|
1943 | message = "You are not authorized to modify %s '%s'." % ( item_desc, item.name ) |
---|
1944 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1945 | action='browse_library', |
---|
1946 | cntrller=cntrller, |
---|
1947 | id=library_id, |
---|
1948 | show_deleted=show_deleted, |
---|
1949 | message=util.sanitize_text( message ), |
---|
1950 | status='error' ) ) |
---|
1951 | # An info_association must exist at this point |
---|
1952 | info_association, inherited = item.get_info_association( restrict=True ) |
---|
1953 | template = info_association.template |
---|
1954 | info = info_association.info |
---|
1955 | form_values = trans.sa_session.query( trans.app.model.FormValues ).get( info.id ) |
---|
1956 | if edited: |
---|
1957 | # The form on which the template is based has been edited, so we need to update the |
---|
1958 | # info_association with the current form |
---|
1959 | fdc = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ).get( template.form_definition_current_id ) |
---|
1960 | info_association.template = fdc.latest_form |
---|
1961 | trans.sa_session.add( info_association ) |
---|
1962 | trans.sa_session.flush() |
---|
1963 | message = "The template for this %s has been updated with your changes." % item_desc |
---|
1964 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
1965 | action=action, |
---|
1966 | cntrller=cntrller, |
---|
1967 | use_panels=use_panels, |
---|
1968 | library_id=library_id, |
---|
1969 | folder_id=folder_id, |
---|
1970 | id=id, |
---|
1971 | show_deleted=show_deleted, |
---|
1972 | message=util.sanitize_text( message ), |
---|
1973 | status='done' ) ) |
---|
1974 | # "template" is a FormDefinition, so since we're changing it, we need to use the latest version of it. |
---|
1975 | vars = dict( id=trans.security.encode_id( template.form_definition_current_id ), |
---|
1976 | response_redirect=web.url_for( controller='library_common', |
---|
1977 | action='edit_template', |
---|
1978 | cntrller=cntrller, |
---|
1979 | item_type=item_type, |
---|
1980 | library_id=library_id, |
---|
1981 | folder_id=folder_id, |
---|
1982 | ldda_id=ldda_id, |
---|
1983 | edited=True, |
---|
1984 | **kwd ) ) |
---|
1985 | return trans.response.send_redirect( web.url_for( controller='forms', action='edit', **vars ) ) |
---|
1986 | @web.expose |
---|
1987 | def edit_template_info( self, trans, cntrller, item_type, library_id, folder_id=None, ldda_id=None, **kwd ): |
---|
1988 | # Edit the contents of the template fields without altering the template itself. |
---|
1989 | params = util.Params( kwd ) |
---|
1990 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
1991 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
1992 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
1993 | status = params.get( 'status', 'done' ) |
---|
1994 | is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' ) |
---|
1995 | current_user_roles = trans.get_current_user_roles() |
---|
1996 | try: |
---|
1997 | item, item_desc, action, id = self.get_item_and_stuff( trans, item_type, library_id, folder_id, ldda_id, is_admin ) |
---|
1998 | except ValueError: |
---|
1999 | return None |
---|
2000 | if not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, item ) ): |
---|
2001 | message = "You are not authorized to modify %s '%s'." % ( item_desc, item.name ) |
---|
2002 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2003 | action='browse_library', |
---|
2004 | cntrller=cntrller, |
---|
2005 | id=library_id, |
---|
2006 | show_deleted=show_deleted, |
---|
2007 | message=util.sanitize_text( message ), |
---|
2008 | status='error' ) ) |
---|
2009 | # We need the type of each template field widget |
---|
2010 | widgets = item.get_template_widgets( trans ) |
---|
2011 | # The list of widgets may include an AddressField which we need to save if it is new |
---|
2012 | for index, widget_dict in enumerate( widgets ): |
---|
2013 | widget = widget_dict[ 'widget' ] |
---|
2014 | if isinstance( widget, AddressField ): |
---|
2015 | value = util.restore_text( params.get( 'field_%i' % index, '' ) ) |
---|
2016 | if value == 'new': |
---|
2017 | if params.get( 'edit_info_button', False ): |
---|
2018 | if self.field_param_values_ok( index, 'AddressField', **kwd ): |
---|
2019 | # Save the new address |
---|
2020 | address = trans.app.model.UserAddress( user=trans.user ) |
---|
2021 | self.save_widget_field( trans, address, index, **kwd ) |
---|
2022 | widget.value = str( address.id ) |
---|
2023 | else: |
---|
2024 | message = 'Required fields are missing contents.' |
---|
2025 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2026 | action=action, |
---|
2027 | cntrller=cntrller, |
---|
2028 | use_panels=use_panels, |
---|
2029 | library_id=library_id, |
---|
2030 | folder_id=folder_id, |
---|
2031 | id=id, |
---|
2032 | show_deleted=show_deleted, |
---|
2033 | message=util.sanitize_text( message ), |
---|
2034 | status='error' ) ) |
---|
2035 | else: |
---|
2036 | # Form was submitted via refresh_on_change |
---|
2037 | widget.value = 'new' |
---|
2038 | elif value == unicode( 'none' ): |
---|
2039 | widget.value = '' |
---|
2040 | else: |
---|
2041 | widget.value = value |
---|
2042 | elif isinstance( widget, CheckboxField ): |
---|
2043 | # We need to check the value from kwd since util.Params would have munged the list if |
---|
2044 | # the checkbox is checked. |
---|
2045 | value = kwd.get( 'field_%i' % index, '' ) |
---|
2046 | if CheckboxField.is_checked( value ): |
---|
2047 | widget.value = 'true' |
---|
2048 | else: |
---|
2049 | widget.value = util.restore_text( params.get( 'field_%i' % index, '' ) ) |
---|
2050 | # Save updated template field contents |
---|
2051 | field_contents = self.clean_field_contents( widgets, **kwd ) |
---|
2052 | if field_contents: |
---|
2053 | # Since information templates are inherited, the template fields can be displayed on the information |
---|
2054 | # page for a folder or ldda when it has no info_association object. If the user has added |
---|
2055 | # field contents on an inherited template via a parent's info_association, we'll need to create a new |
---|
2056 | # form_values and info_association for the current object. The value for the returned inherited variable |
---|
2057 | # is not applicable at this level. |
---|
2058 | info_association, inherited = item.get_info_association( restrict=True ) |
---|
2059 | if info_association: |
---|
2060 | template = info_association.template |
---|
2061 | info = info_association.info |
---|
2062 | form_values = trans.sa_session.query( trans.app.model.FormValues ).get( info.id ) |
---|
2063 | # Update existing content only if it has changed |
---|
2064 | if form_values.content != field_contents: |
---|
2065 | form_values.content = field_contents |
---|
2066 | trans.sa_session.add( form_values ) |
---|
2067 | trans.sa_session.flush() |
---|
2068 | else: |
---|
2069 | # Inherit the next available info_association so we can get the template |
---|
2070 | info_association, inherited = item.get_info_association() |
---|
2071 | template = info_association.template |
---|
2072 | # Create a new FormValues object |
---|
2073 | form_values = trans.app.model.FormValues( template, field_contents ) |
---|
2074 | trans.sa_session.add( form_values ) |
---|
2075 | trans.sa_session.flush() |
---|
2076 | # Create a new info_association between the current library item and form_values |
---|
2077 | if item_type == 'folder': |
---|
2078 | # A LibraryFolder is a special case because if it inherited the template from it's parent, |
---|
2079 | # we want to set inheritable to True for it's info_association. This allows for the default |
---|
2080 | # inheritance to be False for each level in the Library hierarchy unless we're creating a new |
---|
2081 | # level in the hierarchy, in which case we'll inherit the "inheritable" setting from the parent |
---|
2082 | # level. |
---|
2083 | info_association = trans.app.model.LibraryFolderInfoAssociation( item, template, form_values, inheritable=inherited ) |
---|
2084 | trans.sa_session.add( info_association ) |
---|
2085 | trans.sa_session.flush() |
---|
2086 | elif item_type == 'ldda': |
---|
2087 | # TODO: Currently info_associations at teh ldda level are not inheritable to the associated LibraryDataset. |
---|
2088 | # We need to figure out if this is optimal. |
---|
2089 | info_association = trans.app.model.LibraryDatasetDatasetInfoAssociation( item, template, form_values ) |
---|
2090 | trans.sa_session.add( info_association ) |
---|
2091 | trans.sa_session.flush() |
---|
2092 | message = 'The information has been updated.' |
---|
2093 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2094 | action=action, |
---|
2095 | cntrller=cntrller, |
---|
2096 | use_panels=use_panels, |
---|
2097 | library_id=library_id, |
---|
2098 | folder_id=folder_id, |
---|
2099 | id=id, |
---|
2100 | show_deleted=show_deleted, |
---|
2101 | message=util.sanitize_text( message ), |
---|
2102 | status='done' ) ) |
---|
2103 | @web.expose |
---|
2104 | def delete_template( self, trans, cntrller, item_type, library_id, id=None, folder_id=None, ldda_id=None, **kwd ): |
---|
2105 | # Only adding a new template to a library or folder is currently allowed. Editing an existing template is |
---|
2106 | # a future enhancement. |
---|
2107 | params = util.Params( kwd ) |
---|
2108 | show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) |
---|
2109 | use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) |
---|
2110 | message = util.restore_text( params.get( 'message', '' ) ) |
---|
2111 | status = params.get( 'status', 'done' ) |
---|
2112 | is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' ) |
---|
2113 | current_user_roles = trans.get_current_user_roles() |
---|
2114 | try: |
---|
2115 | item, item_desc, action, id = self.get_item_and_stuff( trans, item_type, library_id, folder_id, ldda_id, is_admin ) |
---|
2116 | except ValueError: |
---|
2117 | return None |
---|
2118 | if not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, item ) ): |
---|
2119 | message = "You are not authorized to modify %s '%s'." % ( item_desc, item.name ) |
---|
2120 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2121 | action='browse_library', |
---|
2122 | cntrller=cntrller, |
---|
2123 | id=library_id, |
---|
2124 | show_deleted=show_deleted, |
---|
2125 | message=util.sanitize_text( message ), |
---|
2126 | status='error' ) ) |
---|
2127 | info_association, inherited = item.get_info_association() |
---|
2128 | if not info_association: |
---|
2129 | message = "There is no template for this %s" % item_type |
---|
2130 | status = 'error' |
---|
2131 | else: |
---|
2132 | info_association.deleted = True |
---|
2133 | trans.sa_session.add( info_association ) |
---|
2134 | trans.sa_session.flush() |
---|
2135 | message = 'The template for this %s has been deleted.' % item_type |
---|
2136 | status = 'done' |
---|
2137 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2138 | action=action, |
---|
2139 | cntrller=cntrller, |
---|
2140 | use_panels=use_panels, |
---|
2141 | library_id=library_id, |
---|
2142 | folder_id=folder_id, |
---|
2143 | id=id, |
---|
2144 | show_deleted=show_deleted, |
---|
2145 | message=util.sanitize_text( message ), |
---|
2146 | status=status ) ) |
---|
2147 | @web.expose |
---|
2148 | def delete_library_item( self, trans, cntrller, library_id, item_id, item_type, **kwd ): |
---|
2149 | # This action will handle deleting all types of library items. State is saved for libraries and |
---|
2150 | # folders ( i.e., if undeleted, the state of contents of the library or folder will remain, so previously |
---|
2151 | # deleted / purged contents will have the same state ). When a library or folder has been deleted for |
---|
2152 | # the amount of time defined in the cleanup_datasets.py script, the library or folder and all of its |
---|
2153 | # contents will be purged. The association between this method and the cleanup_datasets.py script |
---|
2154 | # enables clean maintenance of libraries and library dataset disk files. This is also why the item_types |
---|
2155 | # are not any of the associations ( the cleanup_datasets.py script handles everything ). |
---|
2156 | show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) ) |
---|
2157 | item_types = { 'library': trans.app.model.Library, |
---|
2158 | 'folder': trans.app.model.LibraryFolder, |
---|
2159 | 'library_dataset': trans.app.model.LibraryDataset } |
---|
2160 | is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' ) |
---|
2161 | current_user_roles = trans.get_current_user_roles() |
---|
2162 | if item_type not in item_types: |
---|
2163 | message = 'Bad item_type specified: %s' % str( item_type ) |
---|
2164 | status = 'error' |
---|
2165 | else: |
---|
2166 | if item_type == 'library_dataset': |
---|
2167 | item_desc = 'Dataset' |
---|
2168 | else: |
---|
2169 | item_desc = item_type.capitalize() |
---|
2170 | try: |
---|
2171 | library_item = trans.sa_session.query( item_types[ item_type ] ).get( trans.security.decode_id( item_id ) ) |
---|
2172 | except: |
---|
2173 | library_item = None |
---|
2174 | if not library_item or not ( is_admin or trans.app.security_agent.can_access_library_item( current_user_roles, library_item, trans.user ) ): |
---|
2175 | message = 'Invalid %s id ( %s ) specifield.' % ( item_desc, item_id ) |
---|
2176 | status = 'error' |
---|
2177 | elif not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, library_item ) ): |
---|
2178 | message = "You are not authorized to delete %s '%s'." % ( item_desc, library_item.name ) |
---|
2179 | status = 'error' |
---|
2180 | else: |
---|
2181 | library_item.deleted = True |
---|
2182 | trans.sa_session.add( library_item ) |
---|
2183 | trans.sa_session.flush() |
---|
2184 | message = util.sanitize_text( "%s '%s' has been marked deleted" % ( item_desc, library_item.name ) ) |
---|
2185 | status = 'done' |
---|
2186 | if item_type == 'library': |
---|
2187 | return trans.response.send_redirect( web.url_for( controller=cntrller, |
---|
2188 | action='browse_libraries', |
---|
2189 | message=message, |
---|
2190 | status=status ) ) |
---|
2191 | else: |
---|
2192 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2193 | action='browse_library', |
---|
2194 | cntrller=cntrller, |
---|
2195 | id=library_id, |
---|
2196 | show_deleted=show_deleted, |
---|
2197 | message=message, |
---|
2198 | status=status ) ) |
---|
2199 | @web.expose |
---|
2200 | def undelete_library_item( self, trans, cntrller, library_id, item_id, item_type, **kwd ): |
---|
2201 | # This action will handle undeleting all types of library items |
---|
2202 | show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) ) |
---|
2203 | item_types = { 'library': trans.app.model.Library, |
---|
2204 | 'folder': trans.app.model.LibraryFolder, |
---|
2205 | 'library_dataset': trans.app.model.LibraryDataset } |
---|
2206 | is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' ) |
---|
2207 | current_user_roles = trans.get_current_user_roles() |
---|
2208 | if item_type not in item_types: |
---|
2209 | message = 'Bad item_type specified: %s' % str( item_type ) |
---|
2210 | status = ERROR |
---|
2211 | else: |
---|
2212 | if item_type == 'library_dataset': |
---|
2213 | item_desc = 'Dataset' |
---|
2214 | else: |
---|
2215 | item_desc = item_type.capitalize() |
---|
2216 | try: |
---|
2217 | library_item = trans.sa_session.query( item_types[ item_type ] ).get( trans.security.decode_id( item_id ) ) |
---|
2218 | except: |
---|
2219 | library_item = None |
---|
2220 | if not library_item or not ( is_admin or trans.app.security_agent.can_access_library_item( current_user_roles, library_item, trans.user ) ): |
---|
2221 | message = 'Invalid %s id ( %s ) specifield.' % ( item_desc, item_id ) |
---|
2222 | status = 'error' |
---|
2223 | elif library_item.purged: |
---|
2224 | message = '%s %s has been purged, so it cannot be undeleted' % ( item_desc, library_item.name ) |
---|
2225 | status = ERROR |
---|
2226 | elif not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, library_item ) ): |
---|
2227 | message = "You are not authorized to delete %s '%s'." % ( item_desc, library_item.name ) |
---|
2228 | status = 'error' |
---|
2229 | else: |
---|
2230 | library_item.deleted = False |
---|
2231 | trans.sa_session.add( library_item ) |
---|
2232 | trans.sa_session.flush() |
---|
2233 | message = util.sanitize_text( "%s '%s' has been marked undeleted" % ( item_desc, library_item.name ) ) |
---|
2234 | status = SUCCESS |
---|
2235 | if item_type == 'library': |
---|
2236 | return trans.response.send_redirect( web.url_for( controller=cntrller, |
---|
2237 | action='browse_libraries', |
---|
2238 | message=message, |
---|
2239 | status=status ) ) |
---|
2240 | else: |
---|
2241 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2242 | action='browse_library', |
---|
2243 | cntrller=cntrller, |
---|
2244 | id=library_id, |
---|
2245 | show_deleted=show_deleted, |
---|
2246 | message=message, |
---|
2247 | status=status ) ) |
---|
2248 | def _check_access( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ): |
---|
2249 | can_access = True |
---|
2250 | if isinstance( item, trans.model.HistoryDatasetAssociation ): |
---|
2251 | # Make sure the user has the DATASET_ACCESS permission on the history_dataset_association. |
---|
2252 | if not item: |
---|
2253 | message = "Invalid history dataset (%s) specified." % str( item ) |
---|
2254 | can_access = False |
---|
2255 | elif not trans.app.security_agent.can_access_dataset( current_user_roles, item.dataset ) and item.history.user==trans.user: |
---|
2256 | message = "You do not have permission to access the history dataset with id (%s)." % str( item.id ) |
---|
2257 | can_access = False |
---|
2258 | else: |
---|
2259 | # Make sure the user has the LIBRARY_ACCESS permission on the library item. |
---|
2260 | if not item: |
---|
2261 | message = "Invalid library item (%s) specified." % str( item ) |
---|
2262 | can_access = False |
---|
2263 | elif not ( is_admin or trans.app.security_agent.can_access_library_item( current_user_roles, item, trans.user ) ): |
---|
2264 | if isinstance( item, trans.model.Library ): |
---|
2265 | item_type = 'data library' |
---|
2266 | elif isinstance( item, trans.model.LibraryFolder ): |
---|
2267 | item_type = 'folder' |
---|
2268 | else: |
---|
2269 | item_type = '(unknown item type)' |
---|
2270 | message = "You do not have permission to access the %s with id (%s)." % ( item_type, str( item.id ) ) |
---|
2271 | can_access = False |
---|
2272 | if not can_access: |
---|
2273 | if cntrller == 'api': |
---|
2274 | return 400, message |
---|
2275 | if isinstance( item, trans.model.Library ): |
---|
2276 | return trans.response.send_redirect( web.url_for( controller=cntrller, |
---|
2277 | action='browse_libraries', |
---|
2278 | cntrller=cntrller, |
---|
2279 | use_panels=use_panels, |
---|
2280 | message=util.sanitize_text( message ), |
---|
2281 | status='error' ) ) |
---|
2282 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2283 | action='browse_library', |
---|
2284 | cntrller=cntrller, |
---|
2285 | use_panels=use_panels, |
---|
2286 | id=library_id, |
---|
2287 | show_deleted=show_deleted, |
---|
2288 | message=util.sanitize_text( message ), |
---|
2289 | status='error' ) ) |
---|
2290 | def _check_add( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ): |
---|
2291 | # Deny access if the user is not an admin and does not have the LIBRARY_ADD permission. |
---|
2292 | if not ( is_admin or trans.app.security_agent.can_add_library_item( current_user_roles, item ) ): |
---|
2293 | message = "You are not authorized to add an item to (%s)." % item.name |
---|
2294 | # Redirect to the real parent library since we know we have access to it. |
---|
2295 | if cntrller == 'api': |
---|
2296 | return 403, message |
---|
2297 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2298 | action='browse_library', |
---|
2299 | cntrller=cntrller, |
---|
2300 | use_panels=use_panels, |
---|
2301 | id=library_id, |
---|
2302 | show_deleted=show_deleted, |
---|
2303 | message=util.sanitize_text( message ), |
---|
2304 | status='error' ) ) |
---|
2305 | def _check_manage( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ): |
---|
2306 | if isinstance( item, trans.model.LibraryDataset ): |
---|
2307 | # Deny access if the user is not an admin and does not have the LIBRARY_MANAGE and DATASET_MANAGE_PERMISSIONS permissions. |
---|
2308 | if not ( is_admin or \ |
---|
2309 | ( trans.app.security_agent.can_manage_library_item( current_user_roles, item ) and |
---|
2310 | trans.app.security_agent.can_manage_dataset( current_user_roles, library_dataset.library_dataset_dataset_association.dataset ) ) ): |
---|
2311 | message = "You are not authorized to manage permissions on library dataset (%s)." % library_dataset.name |
---|
2312 | if cntrller == 'api': |
---|
2313 | return 403, message |
---|
2314 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2315 | action='browse_library', |
---|
2316 | id=library_id, |
---|
2317 | cntrller=cntrller, |
---|
2318 | use_panels=use_panels, |
---|
2319 | message=util.sanitize_text( message ), |
---|
2320 | status='error' ) ) |
---|
2321 | # Deny access if the user is not an admin and does not have the LIBRARY_MANAGE permission. |
---|
2322 | if not ( is_admin or trans.app.security_agent.can_manage_library_item( current_user_roles, item ) ): |
---|
2323 | message = "You are not authorized to manage permissions on (%s)." % item.name |
---|
2324 | if cntrller == 'api': |
---|
2325 | return 403, message |
---|
2326 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2327 | action='browse_library', |
---|
2328 | id=library_id, |
---|
2329 | cntrller=cntrller, |
---|
2330 | use_panels=use_panels, |
---|
2331 | message=util.sanitize_text( message ), |
---|
2332 | status='error' ) ) |
---|
2333 | def _check_modify( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ): |
---|
2334 | # Deny modification if the user is not an admin and does not have the LIBRARY_MODIFY permission. |
---|
2335 | if not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, item ) ): |
---|
2336 | message = "You are not authorized to modify (%s)." % item.name |
---|
2337 | if cntrller == 'api': |
---|
2338 | return 403, message |
---|
2339 | return trans.response.send_redirect( web.url_for( controller='library_common', |
---|
2340 | action='browse_library', |
---|
2341 | cntrller=cntrller, |
---|
2342 | id=library_id, |
---|
2343 | use_panels=use_panels, |
---|
2344 | show_deleted=show_deleted, |
---|
2345 | message=util.sanitize_text( message ), |
---|
2346 | status='error' ) ) |
---|
2347 | |
---|
2348 | # ---- Utility methods ------------------------------------------------------- |
---|
2349 | |
---|
2350 | def active_folders( trans, folder ): |
---|
2351 | # Much faster way of retrieving all active sub-folders within a given folder than the |
---|
2352 | # performance of the mapper. This query also eagerloads the permissions on each folder. |
---|
2353 | return trans.sa_session.query( trans.app.model.LibraryFolder ) \ |
---|
2354 | .filter_by( parent=folder, deleted=False ) \ |
---|
2355 | .options( eagerload_all( "actions" ) ) \ |
---|
2356 | .order_by( trans.app.model.LibraryFolder.table.c.name ) \ |
---|
2357 | .all() |
---|
2358 | def activatable_folders( trans, folder ): |
---|
2359 | return trans.sa_session.query( trans.app.model.LibraryFolder ) \ |
---|
2360 | .filter_by( parent=folder, purged=False ) \ |
---|
2361 | .options( eagerload_all( "actions" ) ) \ |
---|
2362 | .order_by( trans.app.model.LibraryFolder.table.c.name ) \ |
---|
2363 | .all() |
---|
2364 | def active_folders_and_lddas( trans, folder ): |
---|
2365 | folders = active_folders( trans, folder ) |
---|
2366 | # This query is much faster than the folder.active_library_datasets property |
---|
2367 | lddas = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ) \ |
---|
2368 | .filter_by( deleted=False ) \ |
---|
2369 | .join( "library_dataset" ) \ |
---|
2370 | .filter( trans.app.model.LibraryDataset.table.c.folder_id==folder.id ) \ |
---|
2371 | .order_by( trans.app.model.LibraryDatasetDatasetAssociation.table.c.name ) \ |
---|
2372 | .all() |
---|
2373 | return folders, lddas |
---|
2374 | def activatable_folders_and_lddas( trans, folder ): |
---|
2375 | folders = activatable_folders( trans, folder ) |
---|
2376 | # This query is much faster than the folder.activatable_library_datasets property |
---|
2377 | lddas = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ) \ |
---|
2378 | .join( "library_dataset" ) \ |
---|
2379 | .filter( trans.app.model.LibraryDataset.table.c.folder_id==folder.id ) \ |
---|
2380 | .join( "dataset" ) \ |
---|
2381 | .filter( trans.app.model.Dataset.table.c.deleted==False ) \ |
---|
2382 | .order_by( trans.app.model.LibraryDatasetDatasetAssociation.table.c.name ) \ |
---|
2383 | .all() |
---|
2384 | return folders, lddas |
---|
2385 | def branch_deleted( folder ): |
---|
2386 | # Return True if a folder belongs to a branch that has been deleted |
---|
2387 | if folder.deleted: |
---|
2388 | return True |
---|
2389 | if folder.parent: |
---|
2390 | return branch_deleted( folder.parent ) |
---|
2391 | return False |
---|
2392 | def get_containing_library_from_library_dataset( trans, library_dataset ): |
---|
2393 | """Given a library_dataset, get the containing library""" |
---|
2394 | folder = library_dataset.folder |
---|
2395 | while folder.parent: |
---|
2396 | folder = folder.parent |
---|
2397 | # We have folder set to the library's root folder, which has the same name as the library |
---|
2398 | for library in trans.sa_session.query( trans.model.Library ) \ |
---|
2399 | .filter( and_( trans.model.Library.table.c.deleted == False, |
---|
2400 | trans.model.Library.table.c.name == folder.name ) ): |
---|
2401 | # Just to double-check |
---|
2402 | if library.root_folder == folder: |
---|
2403 | return library |
---|
2404 | return None |
---|