From a013bd61d0e7d88b5eba48dc96d57a48cda061bf Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 16 Sep 2024 19:00:21 -0400 Subject: [PATCH 01/23] Virtual Collection Draft --- .../views/_directory_contents.html.erb | 75 ++++++ .../views/_file_collection.html.erb | 36 +++ .../views/_file_collection.json.erb | 1 + .../views/_file_collection_form.html.erb | 58 +++++ .../views/_file_collection_top_table.html.erb | 48 ++++ .../views/_plain_file_list_row.html.erb | 75 ++++++ .../virtual_file_collection.rb | 232 ++++++++++++++++++ 7 files changed, 525 insertions(+) create mode 100644 BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb create mode 100644 BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.html.erb create mode 100644 BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb create mode 100644 BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb create mode 100644 BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb create mode 100644 BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb create mode 100644 BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb new file mode 100644 index 000000000..7bb70d3a2 --- /dev/null +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb @@ -0,0 +1,75 @@ + +<%- +# +# CBRAIN Project +# +# Copyright (C) 2008-2012 +# The Royal Institution for the Advancement of Learning +# McGill University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +-%> +why this is shows +<% limit = 500 %> +<% base_dir = base_directory rescue params[:base_directory] %> +<% base_dir = base_dir.presence || "." %> + +<% file_list ||= ( @userfile.list_linked_files(base_dir, [:regular, :directory, :link]) rescue [] ) %> + +<% if file_list.blank? %> + + "> + + (Empty) + + + +<% else %> + + <% for file in file_list[0,limit] %> + <% if file.symbolic_type == :directory %> + <%= on_click_ajax_replace( { :element => "tr", + :url => url_for(:action => :display, + :viewer => "directory_contents", + :viewer_userfile_class => "FileCollection", + :base_directory => file.name.sub(/\A[^\/]+\//, ""), + :apply_div => "false" + ), + :position => "after", + :before => "Loading..." + }, + { :class => "#{cycle("list-odd", "list-even")}", + :id => file.name.gsub(/\W+/, "_") + } + ) do %> + <%= render :file => @viewer.partial_path(:plain_file_list_row), :locals => {:file => file} %> + <% end %> + <% else %> + "> + <%= render :file => @viewer.partial_path(:plain_file_list_row), :locals => {:file => file} %> + + <% end %> + <% end %> + + <% if file_list.size > limit %> + "> + + <%= (" " * 6 * file_list.first.depth).html_safe %> ... <%= image_tag "/images/lotsa_files_icon.png" %> <%= pluralize(file_list.size-limit, "more entry") %> + + + <% end %> + +<% end %> + diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.html.erb new file mode 100644 index 000000000..2c7292876 --- /dev/null +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.html.erb @@ -0,0 +1,36 @@ + +<%- +# +# CBRAIN Project +# +# Copyright (C) 2008-2012 +# The Royal Institution for the Advancement of Learning +# McGill University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +-%> + +<% + # This partial can be invoked directly as a viewer from the display action, + # or rendered as part of another piece of view code. As such it will + # accept a "base_directory" either as a params[] or as a local variable + base_dir = base_directory rescue params[:base_directory] +%> + +<%= render :file => VirtualFileCollection.view_path(:file_collection_form), + :locals => { :base_directory => base_dir } %> + +
+ diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb new file mode 100644 index 000000000..f6da5eaa1 --- /dev/null +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb @@ -0,0 +1 @@ +<%= @userfile.list_linked_files.to_json %> diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb new file mode 100644 index 000000000..924fcbc51 --- /dev/null +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb @@ -0,0 +1,58 @@ + +<%- +# +# CBRAIN Project +# +# Copyright (C) 2008-2012 +# The Royal Institution for the Advancement of Learning +# McGill University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +-%> + +<% + # This partial requires one local variable: + # + # base_directory : the subdirectory inside the userfile where we start to render the directory content +%> +Debug Virtual File Collection Form 1 +<% if true # @userfile.num_files && @userfile.num_files > 0 %> + + <%= form_for @userfile, :as => :userfile, + :url => { :controller => :userfiles, + :action => :extract_from_collection + }, + :html => { :method => :post, + :id => "userfile_edit_#{@userfile.id}_#{base_directory}" + } do |f| %> + <%= ajax_element(display_userfile_path(@userfile, + :viewer => :file_collection_top_table, + :viewer_userfile_class => :VirtualFileCollection, + :base_directory => base_directory, + ), :class => "loading_message") do %> +
+ Loading... +
+ <% end %> + + <% if @userfile.is_locally_synced? %> +

+ <%= submit_tag "Extract Files from Virtual Collection" %> + <% end %> + + <% end %> + +<% end %> + diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb new file mode 100644 index 000000000..ca17ae472 --- /dev/null +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb @@ -0,0 +1,48 @@ + +<%- +# +# CBRAIN Project +# +# Copyright (C) 2008-2012 +# The Royal Institution for the Advancement of Learning +# McGill University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +-%> + +<% + # This partial requires one params variable: + # params[:base_directory] : a relative path to the subdirectory inside the userfile; we will list from that point. +%> + +<% + base_dir = params[:base_directory].presence || "" +%> + + + + + + + + <%= render :file => VirtualFileCollection.view_path(:directory_contents), + :locals => { :base_directory => base_dir } + %> +
+ <% if @userfile.is_locally_synced? %> + <%= select_all_checkbox "collection_checkbox" %> + <% end %> + FileDLSize
+ diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb new file mode 100644 index 000000000..6627c1ce5 --- /dev/null +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb @@ -0,0 +1,75 @@ + +<%- +# +# CBRAIN Project +# +# Copyright (C) 2008-2012 +# The Royal Institution for the Advancement of Learning +# McGill University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +-%> + +<% + # This partial receives one local variable: + # file : a file structure as returned by the FileCollection listing method + # Note that is contains a full relative path, starting with the @userfile's name itself. +%> + + + <% if @userfile.is_locally_synced? && file.symbolic_type == :regular %> + <%= check_box_tag("file_names[]", file.name, false, :class => "collection_checkbox", :id => nil) %> + <% end %> + + + + <%= (" " * 6 * file.depth).html_safe %> + +

+ <% if file.symbolic_type == :directory %> + <%= image_tag "/images/folder_icon_solid.png" %> + <% else %> + <%= image_tag "/images/file_icon.png" %> + <% end %> + +   + + <% if file.size > 0 %> + <%= data_link file.name, @userfile %> + <% else %> + <%= Pathname.new(file.name).basename.to_s %> + <% end %> + + <% if file.symbolic_type == :directory %> +   + Expand + + <% end %> +
+ + + + <% if file.symbolic_type == :regular && file.size > 0 && file.size < UserfilesController::MAX_DOWNLOAD_MEGABYTES.megabytes %> + <%= link_to url_for(:action => :content, :content_loader => :collection_file, :arguments => file.name) do %> + + <% end %> + <% end %> + + + + <% if file.symbolic_type != :directory %> + <%= colored_pretty_size(file.size) %> + <% end %> + diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb new file mode 100644 index 000000000..0d6193772 --- /dev/null +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb @@ -0,0 +1,232 @@ + +# +# CBRAIN Project +# +# Copyright (C) 2008-2024 +# The Royal Institution for the Advancement of Learning +# McGill University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This file collection is collection of other Userfiles or Collections +# It is implemented using soft links +# two level collections are forbidden to prevent recursion or other issues +class VirtualFileCollection < FileCollection + + Revision_info=CbrainFileRevision[__FILE__] #:nodoc: + + CSV_BASENAME = "_virtual_file_collection.cbcsv" + # todo. add .bidsignore file, otherwise bids validation. Or we can allow CBRAIN filenames starting with dot + + CBRAIN_ARCHIVE_CONTENT_BASENAME = nil + + #reset_viewers # we don't need any of the superclass viewers + has_viewer :name => 'Virtual File Collection', :partial => :file_collection , :if => :is_locally_synced? + + def self.pretty_type #:nodoc: + "Virtual File Collection" + end + + # Sync the VirtualFileCollection, with the files too + def sync_to_cache(deep=true) #:nodoc: + syncstat = self.local_sync_status(:refresh) + return true if syncstat && syncstat.status == 'InSync' + super() + if deep && ! self.archived? + self.sync_files + self.update_cache_symlinks + end + @cbfl = files= nil # flush internal cache + true + end + + # Invokes the local sync_to_cache with deep=false; this means the + # CivetOutputs are not synchronized and symlinks not created. + # This method is used by FileCollection when archiving or unarchiving. + def sync_to_cache_for_archiving + result = sync_to_cache(false) + self.erase_cache_symlinks rescue nil + result + end + + # When syncing to the provider, we locally erase + # the symlinks, because they make no sense outside + # of the local Rails app. + # FIXME: this method has a slight race condition, + # after syncing to the provider we recreate the + # symlinks, but if another program tries to access + # them during that time they might not yet be there. + def sync_to_provider #:nodoc: + self.cache_writehandle do # when the block ends, it will trigger the provider upload + self.erase_cache_symlinks unless self.archived? + end + self.make_cache_symlinks unless self.archived? + true + end + + # Sets the set of CivetOutputs belonging to this study. + # The CSV file inside the study will be created/updated, + # as well as all the symbolic links. The content of the + # study is NOT synced to the provider side. + def create_virtual_file_collection(files) + cb_error "Imbedded collections are not supported." if files.any? { |f| ! f.is_a?(VirtualFileCollection) } + + # Prepare CSV content + content = CbrainFileList.create_csv_file_from_userfiles(civetoutputs) + + # This optimize so we don't reload the content for making the symlinks + @cbfl = CbrainFileList.new + @cbfl.load_from_content(content) + @files = nil + + # Write CSV content to the interal CSV file + self.cache_prepare + Dir.mkdir(self.cache_full_path) unless Dir.exist?(self.cache_full_path) + File.write(csv_cache_full_path.to_s, content) + self.update_cache_symlinks + self.cache_is_newer + end + require 'pry' + # List linked files or directories, as if present directly + def list_linked_files(dir=:all, allowed_types = :regular) + #binding.pry + if allowed_types.is_a? Array + types = allowed_types.dup + else + types = [allowed_types] + end + types.map!(&:to_sym) + types << :file if types.delete(:regular) + + # for :top + # the and :directory file type data are maid up, to avoid running os command which should not affect file browsing + # alternatively, new option(s) can be added to list_files/cache_collection_index + if (dir == :top || dir == '.') + cloned_files = self.list_files(:top, :link).cb_deep_clone # no altering the cache of list_files methods + userfiles_by_name = self.get_userfiles.index_by(&:name) + return cloned_files.filter_map do |file| + fname = file.name.split('/')[1] # gets basename + userfile = userfiles_by_name[fname] + if types.include?(:directory) && userfile.is_a?(FileCollection) + file.symbolic_type = :directory + # binding.pry + file + elsif types.include?(:file) && userfile.is_a?(SingleFile) + file = userfile.list_files.first.clone + file&.name = self.name + '/' + fname + file&.symbolic_type = :regular + # binding.pry + file + end + end + end + + userfiles = self.get_userfiles + + if dir.is_a? String + name, dir = dir.split '/' + dir |= '.' + userfiles = userfiles.select { |x| x.name == name } + end + + userfiles.map do |userfile| + userfile.list_files(dir, allowed_types).each do |f| + f.name = self.name + '/' + f.name + end + end.flatten + end + + # todo - remove unless needed for creation? + # def validate_componets + # ufiles = self.get_userfiles + # error "Nested Virtual Collection" if ufile.is_a?(VirtualFileCollection) || ufile.is_a?(CivetVirtualStudy) || ufile.type.lower.include?('virtual') + # end + + # Returns the files IDs + def get_ids + self.get_userfiles.map(&:id) + end + + # Returns the list of files in the internal CbrainFileList + # The list is cached internally and access control is applied + # based on the owner of the VirtualFileCollection. + def get_userfiles #:nodoc: + + if @cbfl.blank? + @cbfl = CbrainFileList.new + file_content = File.read(csv_cache_full_path.to_s) + @cbfl.load_from_content(file_content) + end + + @files ||= @cbfl.userfiles_accessible_by_user!(self.user).compact + file_names = @files.map(&:name) + dup_names = file_names.select { |name| file_names.count(name) >1 }.uniq + cb_error "Virtual file collection contains duplicate filenames #{dup_names.join(',')}" if dup_names.present? + @files.each do |f| + cb_error "Nested virtual file collections are not supported, remove file with id #{f.id}" if ( + f.is_a?(VirtualFileCollection) || + f.is_a?(CivetVirtualStudy) || + f.type.downcase.include?('virtual') + ) + end + end + + + + #==================================================================== + # Support methods, not part of this model's API. + #==================================================================== + + protected + + # Synchronize each file + def sync_files #:nodoc: + self.get_userfiles.each { |co| co.sync_to_cache } + end + + # Clean up ALL symbolic links + def erase_cache_symlinks #:nodoc: + Dir.chdir(self.cache_full_path) do + Dir.glob('*').each do |entry| + # FIXME how to only erase symlinks that points to a CBRAIN cache or local DP? + # Parsing the value of the symlink is tricky... + File.unlink(entry) if File.symlink?(entry) + end + end + end + + # This cleans up any old symbolic links, then recreates them. + # Note that this does not sync the files themselves. + def update_cache_symlinks #:nodoc: + self.erase_cache_symlinks + self.make_cache_symlinks + end + + # Create symbolic links in cache + # Note that this does not sync the files themselves. + def make_cache_symlinks #:nodoc: + self.get_userfiles.each do |co| + link_value = co.cache_full_path + link_path = self.cache_full_path + link_value.basename + File.unlink(link_path) if File.symlink?(link_path) && File.readlink(link_path) != link_value + File.symlink(link_value, link_path) unless File.exist?(link_path) + end + end + + def csv_cache_full_path #:nodoc: + self.cache_full_path + CSV_BASENAME + end + +end From 7b123068ef73fc96dd238c4fd8018d36db33ab84 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 16 Sep 2024 19:14:48 -0400 Subject: [PATCH 02/23] Virtual Collection Draft --- .../virtual_file_collection/views/_plain_file_list_row.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb index 6627c1ce5..34f40bdf0 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb @@ -47,7 +47,7 @@   <% if file.size > 0 %> - <%= data_link file.name, @userfile %> + <%= data_link file.name, @userfile # file.userfile? %> <% else %> <%= Pathname.new(file.name).basename.to_s %> <% end %> From a8a0d8fc2eebc1912015f411ada4033df3515ec6 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 23 Sep 2024 17:01:40 -0400 Subject: [PATCH 03/23] Virtual Collection minor improvement to a userfile helper --- BrainPortal/app/helpers/userfiles_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BrainPortal/app/helpers/userfiles_helper.rb b/BrainPortal/app/helpers/userfiles_helper.rb index ad0eb5089..3432b3a47 100644 --- a/BrainPortal/app/helpers/userfiles_helper.rb +++ b/BrainPortal/app/helpers/userfiles_helper.rb @@ -110,11 +110,11 @@ def data_link(file_name, userfile, replace_div_id="sub_viewer_filecollection_cbr end elsif display_name =~ /\.html$/i # TODO: this will never happen if we ever create a HtmlFile model with at least one viewer link_to "#{display_name}", - stream_userfile_path(@userfile, :file_path => file_name, :disposition => 'inline'), + stream_userfile_path(userfile, :file_path => file_name, :disposition => 'inline'), :target => '_BLANK' else link_to h(display_name), - url_for(:action => :content, :content_loader => :collection_file, :arguments => file_name) + url_for(:action => :content, :id => userfile.id, :content_loader => :collection_file, :arguments => file_name) end end From f9eb56a13437639844b7b9bfcad0e0991a7cc3ab Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 23 Sep 2024 17:02:57 -0400 Subject: [PATCH 04/23] Virtual Collection render collection tree --- .../views/_directory_contents.html.erb | 1 + .../views/_directory_contents.html.erb | 30 +++++++++++-- .../views/_file_collection.json.erb | 2 +- .../views/_file_collection_form.html.erb | 15 ++++--- .../views/_file_collection_top_table.html.erb | 4 +- .../views/_plain_file_list_row.html.erb | 23 +++++++--- .../virtual_file_collection.rb | 42 ++++++++++++++----- 7 files changed, 88 insertions(+), 29 deletions(-) diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/file_collection/views/_directory_contents.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/file_collection/views/_directory_contents.html.erb index b6f0e50a7..204b28c71 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/file_collection/views/_directory_contents.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/file_collection/views/_directory_contents.html.erb @@ -58,6 +58,7 @@ <% end %> <% else %> "> + debug 1 <%= render :file => @viewer.partial_path(:plain_file_list_row), :locals => {:file => file} %> <% end %> diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb index 7bb70d3a2..8de7174e1 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb @@ -21,13 +21,13 @@ # along with this program. If not, see . # -%> -why this is shows + <% limit = 500 %> <% base_dir = base_directory rescue params[:base_directory] %> <% base_dir = base_dir.presence || "." %> <% file_list ||= ( @userfile.list_linked_files(base_dir, [:regular, :directory, :link]) rescue [] ) %> - +base <%= base_dir %> <% if file_list.blank? %> "> @@ -39,14 +39,36 @@ why this is shows <% else %> <% for file in file_list[0,limit] %> + <% fname = file.name %> + <% fname = fname.delete_prefix(@userfile.name + '/') unless @userfile.id == file.userfile.id %> <% if file.symbolic_type == :directory %> + <%= h = {:id => file.userfile.id, + :action => :display, + :viewer => "directory_contents", + :viewer_userfile_class => "FileCollection", + :base_directory => ".", + :apply_div => "false"} +%> + <%= link_to h.to_s, url_for(h) + + + %> <%= on_click_ajax_replace( { :element => "tr", - :url => url_for(:action => :display, + :url => url_for( + :id => file.userfile.id, + :action => :display, :viewer => "directory_contents", :viewer_userfile_class => "FileCollection", - :base_directory => file.name.sub(/\A[^\/]+\//, ""), + :base_directory => ".", :apply_div => "false" ), + # :url => url_for( + # :action => :display, + # :viewer => "virtual_file_collection", + # :viewer_userfile_class => "VirtualFileCollection", + # :base_directory => file.name.sub(/\A[^\/]+\//, ""), + # :apply_div => "false" + # ), :position => "after", :before => "Loading..." }, diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb index f6da5eaa1..dc596e8ad 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb @@ -1 +1 @@ -<%= @userfile.list_linked_files.to_json %> +<%#= @userfile.list_linked_files.to_json %> diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb index 924fcbc51..f9aab81cf 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb @@ -27,8 +27,9 @@ # # base_directory : the subdirectory inside the userfile where we start to render the directory content %> -Debug Virtual File Collection Form 1 -<% if true # @userfile.num_files && @userfile.num_files > 0 %> + +<% if @userfile.num_files && @userfile.num_files > 0 %> + <%= form_for @userfile, :as => :userfile, :url => { :controller => :userfiles, @@ -37,6 +38,7 @@ Debug Virtual File Collection Form 1 :html => { :method => :post, :id => "userfile_edit_#{@userfile.id}_#{base_directory}" } do |f| %> + DEBUG file collection form <%= ajax_element(display_userfile_path(@userfile, :viewer => :file_collection_top_table, :viewer_userfile_class => :VirtualFileCollection, @@ -47,10 +49,11 @@ Debug Virtual File Collection Form 1
<% end %> - <% if @userfile.is_locally_synced? %> -

- <%= submit_tag "Extract Files from Virtual Collection" %> - <% end %> + <%# todo delete %> + <%# if @userfile.is_locally_synced? %> + + <%#= submit_tag "Extract Files from Virtual Collection" %> + <%# end %> <% end %> diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb index ca17ae472..a3114b88d 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb @@ -33,9 +33,7 @@ diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb index 34f40bdf0..5d1559c3a 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb @@ -35,7 +35,7 @@ diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb index 0d6193772..80da104b8 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb @@ -32,13 +32,32 @@ class VirtualFileCollection < FileCollection CBRAIN_ARCHIVE_CONTENT_BASENAME = nil - #reset_viewers # we don't need any of the superclass viewers + before_validation :set_size + + reset_viewers # we don't need any of the superclass viewers has_viewer :name => 'Virtual File Collection', :partial => :file_collection , :if => :is_locally_synced? def self.pretty_type #:nodoc: "Virtual File Collection" end + def num_files + Rails.cache.fetch("VirtualFileCollection#num_files", expires_in: 5.minutes) do + self.get_userfiles.sum(&:num_files) + end + end + + def size + Rails.cache.fetch("VirtualFileCollection#size", expires_in: 5.minutes) do + self.get_userfiles.sum(&:size) + end + end + + + def set_size #:nodoc: + user.assign_attributes(size: self.size , num_files: num_files) + end + # Sync the VirtualFileCollection, with the files too def sync_to_cache(deep=true) #:nodoc: syncstat = self.local_sync_status(:refresh) @@ -98,10 +117,9 @@ def create_virtual_file_collection(files) self.update_cache_symlinks self.cache_is_newer end - require 'pry' + # List linked files or directories, as if present directly def list_linked_files(dir=:all, allowed_types = :regular) - #binding.pry if allowed_types.is_a? Array types = allowed_types.dup else @@ -110,9 +128,10 @@ def list_linked_files(dir=:all, allowed_types = :regular) types.map!(&:to_sym) types << :file if types.delete(:regular) - # for :top - # the and :directory file type data are maid up, to avoid running os command which should not affect file browsing - # alternatively, new option(s) can be added to list_files/cache_collection_index + # for combination of :top and :directory file type data are maid up, + # to avoid running file stats command which should not affect file browsing + # alternatively, new option(s) can be added to list_files/cache_collection_index, + # or new dir_info method if (dir == :top || dir == '.') cloned_files = self.list_files(:top, :link).cb_deep_clone # no altering the cache of list_files methods userfiles_by_name = self.get_userfiles.index_by(&:name) @@ -121,12 +140,14 @@ def list_linked_files(dir=:all, allowed_types = :regular) userfile = userfiles_by_name[fname] if types.include?(:directory) && userfile.is_a?(FileCollection) file.symbolic_type = :directory + file.userfile = userfile # binding.pry file elsif types.include?(:file) && userfile.is_a?(SingleFile) file = userfile.list_files.first.clone file&.name = self.name + '/' + fname file&.symbolic_type = :regular + file.userfile = userfile # binding.pry file end @@ -145,6 +166,7 @@ def list_linked_files(dir=:all, allowed_types = :regular) userfile.list_files(dir, allowed_types).each do |f| f.name = self.name + '/' + f.name end + f.userfile = userfile end.flatten end @@ -193,7 +215,7 @@ def get_userfiles #:nodoc: # Synchronize each file def sync_files #:nodoc: - self.get_userfiles.each { |co| co.sync_to_cache } + self.get_userfiles.each { |uf| uf.sync_to_cache } end # Clean up ALL symbolic links @@ -214,11 +236,11 @@ def update_cache_symlinks #:nodoc: self.make_cache_symlinks end - # Create symbolic links in cache + # Create symbolic links in cache for each element of the virtual collection # Note that this does not sync the files themselves. def make_cache_symlinks #:nodoc: - self.get_userfiles.each do |co| - link_value = co.cache_full_path + self.get_userfiles.each do |uf| + link_value = uf.cache_full_path link_path = self.cache_full_path + link_value.basename File.unlink(link_path) if File.symlink?(link_path) && File.readlink(link_path) != link_value File.symlink(link_value, link_path) unless File.exist?(link_path) From 5982f47b192b7156caadd9343d1c3fc915faf0a9 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Tue, 24 Sep 2024 16:27:07 -0400 Subject: [PATCH 05/23] remove debug info --- .../views/_directory_contents.html.erb | 22 ++----------------- .../views/_file_collection_form.html.erb | 8 ++----- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb index 8de7174e1..cb8a144f6 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb @@ -27,7 +27,7 @@ <% base_dir = base_dir.presence || "." %> <% file_list ||= ( @userfile.list_linked_files(base_dir, [:regular, :directory, :link]) rescue [] ) %> -base <%= base_dir %> + <% if file_list.blank? %> "> @@ -42,17 +42,6 @@ base <%= base_dir %> <% fname = file.name %> <% fname = fname.delete_prefix(@userfile.name + '/') unless @userfile.id == file.userfile.id %> <% if file.symbolic_type == :directory %> - <%= h = {:id => file.userfile.id, - :action => :display, - :viewer => "directory_contents", - :viewer_userfile_class => "FileCollection", - :base_directory => ".", - :apply_div => "false"} -%> - <%= link_to h.to_s, url_for(h) - - - %> <%= on_click_ajax_replace( { :element => "tr", :url => url_for( :id => file.userfile.id, @@ -62,14 +51,7 @@ base <%= base_dir %> :base_directory => ".", :apply_div => "false" ), - # :url => url_for( - # :action => :display, - # :viewer => "virtual_file_collection", - # :viewer_userfile_class => "VirtualFileCollection", - # :base_directory => file.name.sub(/\A[^\/]+\//, ""), - # :apply_div => "false" - # ), - :position => "after", + :position => "after", :before => "" }, { :class => "#{cycle("list-odd", "list-even")}", diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb index f9aab81cf..b9ad42ce8 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb @@ -38,7 +38,7 @@ :html => { :method => :post, :id => "userfile_edit_#{@userfile.id}_#{base_directory}" } do |f| %> - DEBUG file collection form + <%= ajax_element(display_userfile_path(@userfile, :viewer => :file_collection_top_table, :viewer_userfile_class => :VirtualFileCollection, @@ -49,11 +49,7 @@
<% end %> - <%# todo delete %> - <%# if @userfile.is_locally_synced? %> - - <%#= submit_tag "Extract Files from Virtual Collection" %> - <%# end %> + <% end %> From b268c809cc47c1f9a1b18dc9a81bcb0da735bd03 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Wed, 2 Oct 2024 05:40:02 -0400 Subject: [PATCH 06/23] add virtual collection creation to menu, minor corrections #1135 --- .../app/controllers/userfiles_controller.rb | 80 ++++++++++++++++++- .../app/views/userfiles/_dialogs.html.erb | 30 +++++++ .../app/views/userfiles/_file_menu.html.erb | 11 +++ .../views/_directory_contents.html.erb | 1 - .../virtual_file_collection.rb | 38 ++++----- BrainPortal/config/routes.rb | 1 + BrainPortal/public/javascripts/userfiles.js | 51 +++++++++++- 7 files changed, 186 insertions(+), 26 deletions(-) diff --git a/BrainPortal/app/controllers/userfiles_controller.rb b/BrainPortal/app/controllers/userfiles_controller.rb index 3aaf0ad38..d3d3bd8b9 100644 --- a/BrainPortal/app/controllers/userfiles_controller.rb +++ b/BrainPortal/app/controllers/userfiles_controller.rb @@ -39,7 +39,7 @@ class UserfilesController < ApplicationController around_action :permission_check, :only => [ :download, :update_multiple, :delete_files, - :create_collection, :change_provider, :quality_control, + :create_collection, :create_virtual_collection, :change_provider, :quality_control, :export_file_list ] @@ -1040,6 +1040,84 @@ def create_collection #:nodoc: end + #Create a collection from the selected files. + def create_virtual_collection #:nodoc: + filelist = params[:file_ids].uniq || [] + data_provider_id = params[:data_provider_id_for_collection] + collection_name = params[:collection_name] + file_group = current_assignable_group.id + + if data_provider_id.blank? + flash[:error] = "No data provider selected.\n" + redirect_to :action => :index + return + end + + # Handle collection name + if collection_name.blank? + suffix = Time.now.to_i + while Userfile.where(:user_id => current_user.id, :name => "VirtualCollection-#{suffix}").first.present? + suffix += 1 + end + collection_name = "VirtualCollection-#{suffix}" + end + + if ! Userfile.is_legal_filename?(collection_name) + flash[:error] = "Error: collection name '#{collection_name}' is not acceptable (illegal characters?)." + redirect_to :action => :index, :format => request.format.to_sym + return + end + + # Check if the collection name chosen by the user already exists for this user on the data_provider + if current_user.userfiles.exists?(:name => collection_name, :data_provider_id => data_provider_id) + flash[:error] = "Error: collection with name '#{collection_name}' already exists." + redirect_to :action => :index, :format => request.format.to_sym + return + end + + userfiles = Userfile.find_accessible_by_user(filelist, current_user, :access_requested => :read) + + # todo double check how 0 is possible, bad files should cause exception + if userfiles.count == 0 + flash[:error] = "Error: Inaccessible files selected." + redirect_to :action => :index, :format => request.format.to_sym + return + end + + collection = VirtualFileCollection.new( + :user_id => current_user.id, + :group_id => file_group, + :data_provider_id => data_provider_id, + :name => collection_name + ) + + collection.save! + collection.cache_prepare + coldir = collection.cache_full_path + Dir.mkdir(coldir) + + collection.set_virtual_file_collection(userfiles) + + # Save the content and DB model + + collection.sync_to_provider + collection.save + collection.set_size + + # Find the files + userfiles = Userfile + .find_all_accessible_by_user(current_user, :access_requested => :read) + .where(:id => filelist).all.to_a + + if userfiles.empty? + flash[:error] = "You need to select some files first." + redirect_to(:action => :index) + return + end + redirect_to :action => :index + + end + # Copy or move files to a new provider. def change_provider #:nodoc: diff --git a/BrainPortal/app/views/userfiles/_dialogs.html.erb b/BrainPortal/app/views/userfiles/_dialogs.html.erb index 4e670c292..08a395e4c 100644 --- a/BrainPortal/app/views/userfiles/_dialogs.html.erb +++ b/BrainPortal/app/views/userfiles/_dialogs.html.erb @@ -349,6 +349,36 @@ +
+
+ + + + ⚠ Invalid! + +
+ + + <%= + data_provider_select('data_provider_id_for_collection', + { :data_providers => writable_dps }, + { + :id => 'co-dp', + :class => 'dlg-fld', + :'data-placeholder' => "A data provider..." + } + ) + %> +

+ +
+ +
+
  • +
    + New virtual collection + +
    +
  • + +
  • <% else %>
  • "> - debug 1 <%= render :file => @viewer.partial_path(:plain_file_list_row), :locals => {:file => file} %> <% end %> diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb index 80da104b8..c2e73cdce 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb @@ -32,30 +32,22 @@ class VirtualFileCollection < FileCollection CBRAIN_ARCHIVE_CONTENT_BASENAME = nil - before_validation :set_size - reset_viewers # we don't need any of the superclass viewers + reset_viewers # we opted to ignore superclass viewers rather than adjust them has_viewer :name => 'Virtual File Collection', :partial => :file_collection , :if => :is_locally_synced? def self.pretty_type #:nodoc: "Virtual File Collection" end - def num_files - Rails.cache.fetch("VirtualFileCollection#num_files", expires_in: 5.minutes) do - self.get_userfiles.sum(&:num_files) - end - end - def size - Rails.cache.fetch("VirtualFileCollection#size", expires_in: 5.minutes) do - self.get_userfiles.sum(&:size) + def set_size! + self.size, self.num_files = Rails.cache.fetch("VirtualFileCollection_#{self.id || "#{current_user.id}_#{self.data_provider_id}_#{self.name}"}#size", expires_in: 3.minutes) do + userfiles = self.get_userfiles + [userfiles.sum(&:size), userfiles.sum(&:num_files)] end - end - - - def set_size #:nodoc: - user.assign_attributes(size: self.size , num_files: num_files) + self.assign_attributes(size: self.size , num_files: self.num_files) if self.id + true end # Sync the VirtualFileCollection, with the files too @@ -72,7 +64,7 @@ def sync_to_cache(deep=true) #:nodoc: end # Invokes the local sync_to_cache with deep=false; this means the - # CivetOutputs are not synchronized and symlinks not created. + # constitute FileCollection are not synchronized and symlinks not created. # This method is used by FileCollection when archiving or unarchiving. def sync_to_cache_for_archiving result = sync_to_cache(false) @@ -95,18 +87,18 @@ def sync_to_provider #:nodoc: true end - # Sets the set of CivetOutputs belonging to this study. + # Sets the set of FileCollections that constitute VirtualFileCollection. # The CSV file inside the study will be created/updated, - # as well as all the symbolic links. The content of the - # study is NOT synced to the provider side. - def create_virtual_file_collection(files) - cb_error "Imbedded collections are not supported." if files.any? { |f| ! f.is_a?(VirtualFileCollection) } + # as well as all the symbolic links. The content + # is NOT synced to the provider side. + def set_virtual_file_collection(userfiles) + cb_error "Multi layer collections are not supported." if userfiles.any? { |f| f.is_a?(VirtualFileCollection) || f.is_a?(CivetVirtualStudy) } # Prepare CSV content - content = CbrainFileList.create_csv_file_from_userfiles(civetoutputs) + content = CbrainFileList.create_csv_file_from_userfiles(userfiles) # This optimize so we don't reload the content for making the symlinks - @cbfl = CbrainFileList.new + @cbfl = CbrainFileList.new @cbfl.load_from_content(content) @files = nil diff --git a/BrainPortal/config/routes.rb b/BrainPortal/config/routes.rb index d8aa1c02e..92bc2b666 100644 --- a/BrainPortal/config/routes.rb +++ b/BrainPortal/config/routes.rb @@ -149,6 +149,7 @@ post 'create_parent_child' delete 'delete_files' post 'create_collection' + post 'create_virtual_collection' put 'update_multiple' post 'change_provider' post 'compress' diff --git a/BrainPortal/public/javascripts/userfiles.js b/BrainPortal/public/javascripts/userfiles.js index e22b4046f..62b7a32a9 100644 --- a/BrainPortal/public/javascripts/userfiles.js +++ b/BrainPortal/public/javascripts/userfiles.js @@ -124,7 +124,8 @@ $(function() { rename: '/userfiles/:id', update: $('#prop-dialog > form').attr('action'), tags: '/tags/:id', - create_collection: $('#collection-dialog > form').attr('action') + create_collection: $('#collection-dialog > form').attr('action'), + create_virtual_collection: $('#virtual-collection-dialog > form').attr('action') }; /* Userfiles actions/operations */ @@ -386,6 +387,20 @@ $(function() { return defer(function () { uform.submit(); }).promise(); }, + /* + * Create a new VirtualFileCollection containing the currently selected files. + * The new collection's name and target data provider are to be specified + * in the HTML form argument +form. + */ + create_virtual_collection: function (form) { + var uform = userfiles.children('form'); + + setup_form(uform, urls.create_virtual_collection, 'POST', form); + clear_selection(true); + + return defer(function () { uform.submit(); }).promise(); + }, + /* * Tag-related operations; generic CRUD with parameters +id+ (tag ID) and * +data+ (tag attributes as a JS object) @@ -1097,6 +1112,40 @@ $(function() { .toggleClass('ui-state-disabled', !valid); }); + /* New virtual collection dialog */ + $('#virtual-collection-dialog') + .dialog('option', 'buttons', { + 'Cancel': function (event) { + $(this).trigger('close.uf'); + }, + 'Create': function (event) { + var dialog = $(this); + + dialog.trigger('close.uf'); + userfiles.create_virtual_collection(dialog.children('form')[0]); + } + }) + .unbind('open.uf.col-open') + .bind( 'open.uf.col-open', function () { + $(this).dialog('option', 'title', + 'New virtual collection - ' + formatted_selection() + ); + }) + .undelegate('#co-name', 'input.uf.co-name-check') + .delegate( '#co-name', 'input.uf.co-name-check', function () { + var valid = /^\w[\w~!@#%^&*()-+=:[\]{}|<>,.?]*$/.test($(this).val()); + + $('#co-invalid-name').css({ + visibility: valid ? 'hidden' : 'visible' + }); + + $('#virtual-collection-dialog') + .parent() + .find(':button:contains("Create")') + .prop('disabled', !valid) + .toggleClass('ui-state-disabled', !valid); + }); + /* Delete files confirmation dialog */ $('#delete-confirm') .unbind('open.uf.del-cfrm-open') From f051e5d2dde7e18b3357cfda5f08598ca0d32c38 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Fri, 4 Oct 2024 13:18:01 -0400 Subject: [PATCH 07/23] remove a debug statement #1135 --- .../virtual_file_collection/views/_plain_file_list_row.html.erb | 1 - 1 file changed, 1 deletion(-) diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb index 5d1559c3a..37049256e 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb @@ -51,7 +51,6 @@ <% fname = fname.delete_prefix(@userfile.name + '/') if @userfile != file.userfile %> <% if file.size > 0 %> - fname=<%= fname %> <%= data_link fname, file.userfile %> <% else %> <%= bname %> From 9143d3488b75f6e5e09fb338567abbfffc2210ad Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Tue, 8 Oct 2024 08:00:36 -0400 Subject: [PATCH 08/23] hide the checkboxes from virtual collection view #1135 --- .../views/_directory_contents.html.erb | 34 +++++++++++++++++++ .../views/_file_collection.json.erb | 3 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb index cb8a144f6..5caa4d001 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb @@ -77,3 +77,37 @@ <% end %> + + diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb index dc596e8ad..4cd7bc06f 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.json.erb @@ -1 +1,2 @@ -<%#= @userfile.list_linked_files.to_json %> +<%= @userfile.list_linked_files.to_json %> + From bc51f2e84ed306dc3ae4545bd7b93eead1eedd82 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Tue, 8 Oct 2024 08:01:21 -0400 Subject: [PATCH 09/23] redirect to created virtual collection view #1135 --- BrainPortal/app/controllers/userfiles_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BrainPortal/app/controllers/userfiles_controller.rb b/BrainPortal/app/controllers/userfiles_controller.rb index d3d3bd8b9..9c4499831 100644 --- a/BrainPortal/app/controllers/userfiles_controller.rb +++ b/BrainPortal/app/controllers/userfiles_controller.rb @@ -1114,7 +1114,7 @@ def create_virtual_collection #:nodoc: redirect_to(:action => :index) return end - redirect_to :action => :index + redirect_to(:controller => :userfiles, :action => :show, :id => collection.id) end From 50bc2f97fb681a16e6371ae48da2a192d9c7d7bb Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Tue, 15 Oct 2024 09:35:33 -0400 Subject: [PATCH 10/23] checkpoint - validation and view #1135 --- .../views/_directory_contents.html.erb | 69 ++++++++++--------- .../virtual_file_collection.rb | 26 ++++++- 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb index 5caa4d001..594bf7e12 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb @@ -27,6 +27,12 @@ <% base_dir = base_dir.presence || "." %> <% file_list ||= ( @userfile.list_linked_files(base_dir, [:regular, :directory, :link]) rescue [] ) %> +<% @userfile.list_errors&.each do |error_message| %> + "> + + + +<% end %> <% if file_list.blank? %> @@ -78,36 +84,33 @@ <% end %> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb index c2e73cdce..eaddbc90e 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb @@ -35,7 +35,7 @@ class VirtualFileCollection < FileCollection reset_viewers # we opted to ignore superclass viewers rather than adjust them has_viewer :name => 'Virtual File Collection', :partial => :file_collection , :if => :is_locally_synced? - + has_viewer :name => 'List File Collection', :partial => :cbrain_file_list , :if => :is_locally_synced? def self.pretty_type #:nodoc: "Virtual File Collection" end @@ -185,6 +185,7 @@ def get_userfiles #:nodoc: end @files ||= @cbfl.userfiles_accessible_by_user!(self.user).compact + file_names = @files.map(&:name) dup_names = file_names.select { |name| file_names.count(name) >1 }.uniq cb_error "Virtual file collection contains duplicate filenames #{dup_names.join(',')}" if dup_names.present? @@ -197,9 +198,28 @@ def get_userfiles #:nodoc: end end + # list errors, to be shown to user and issues in the virtual collection + # despite exception above with some luck and effort user can render file view, so + # we show that error messages instead of the directory tree + def list_errors + if @cbfl.blank? + @cbfl = CbrainFileList.new + file_content = File.read(csv_cache_full_path.to_s) + @cbfl.load_from_content(file_content) + end + @files ||= @cbfl.userfiles_accessible_by_user!(self.user).compact.compact + file_names = @files.map(&:name) + dup_names = file_names.select { |name| file_names.count(name) >1 }.uniq + errors = [] + errors = ["Virtual file collection contains duplicate filenames #{dup_names.join(',')}"] if dup_names.present? + virtual_files_names = @files.select do |f| + f.is_a?(VirtualFileCollection) || f.is_a?(CivetVirtualStudy) || f.type.downcase.include?('virtual') + end.map(&:name) + errors << "Nested virtual file collections are not supported, remove files #{virtual_files_names.join ", "}" if virtual_files_names.present? + errors + end - - #==================================================================== + #==================================================================== # Support methods, not part of this model's API. #==================================================================== From 045139ff3e3896050f1c9eec405cd7882a90cf1e Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Tue, 15 Oct 2024 21:15:57 -0400 Subject: [PATCH 11/23] and a simple non-tree viewer #1135 --- .../virtual_file_collection.rb | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb index eaddbc90e..8223bcbe8 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb @@ -34,8 +34,10 @@ class VirtualFileCollection < FileCollection reset_viewers # we opted to ignore superclass viewers rather than adjust them + # this viewer closely resembles one for regular File Collection has_viewer :name => 'Virtual File Collection', :partial => :file_collection , :if => :is_locally_synced? - has_viewer :name => 'List File Collection', :partial => :cbrain_file_list , :if => :is_locally_synced? + # an alternative viewer more stable for large collections + has_viewer :name => 'Simple List', :partial => :cbrain_file_list, :if => :is_locally_synced? def self.pretty_type #:nodoc: "Virtual File Collection" end @@ -95,11 +97,7 @@ def set_virtual_file_collection(userfiles) cb_error "Multi layer collections are not supported." if userfiles.any? { |f| f.is_a?(VirtualFileCollection) || f.is_a?(CivetVirtualStudy) } # Prepare CSV content - content = CbrainFileList.create_csv_file_from_userfiles(userfiles) - - # This optimize so we don't reload the content for making the symlinks - @cbfl = CbrainFileList.new - @cbfl.load_from_content(content) + @cbfl = self.get_inmemory_cbrain_file_list @files = nil # Write CSV content to the interal CSV file @@ -202,16 +200,12 @@ def get_userfiles #:nodoc: # despite exception above with some luck and effort user can render file view, so # we show that error messages instead of the directory tree def list_errors - if @cbfl.blank? - @cbfl = CbrainFileList.new - file_content = File.read(csv_cache_full_path.to_s) - @cbfl.load_from_content(file_content) - end - @files ||= @cbfl.userfiles_accessible_by_user!(self.user).compact.compact + @cbfl = self.get_inmemory_cbrain_file_list + @files ||= @cbfl.userfiles_accessible_by_user!(self.user).compact.compact file_names = @files.map(&:name) - dup_names = file_names.select { |name| file_names.count(name) >1 }.uniq - errors = [] - errors = ["Virtual file collection contains duplicate filenames #{dup_names.join(',')}"] if dup_names.present? + dup_names = file_names.select { |name| file_names.count(name) >1 }.uniq + errors = [] + errors << "Virtual file collection contains duplicate filenames #{dup_names.join(',')}" if dup_names.present? virtual_files_names = @files.select do |f| f.is_a?(VirtualFileCollection) || f.is_a?(CivetVirtualStudy) || f.type.downcase.include?('virtual') end.map(&:name) @@ -219,7 +213,17 @@ def list_errors errors end - #==================================================================== + # updates in memory cbrain file list cache + def get_inmemory_cbrain_file_list + if @cbfl.blank? + @cbfl = CbrainFileList.new + file_content = File.read(csv_cache_full_path.to_s) + @cbfl.load_from_content(file_content) + end + @cbfl + end + + #==================================================================== # Support methods, not part of this model's API. #==================================================================== From 3108108fe3a65cdcdcfa0e0c065942c8ed7792f2 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Tue, 15 Oct 2024 21:16:24 -0400 Subject: [PATCH 12/23] add a non-tree viewer to virtual collection #1135 --- .../views/_cbrain_file_list.html.erb | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb new file mode 100644 index 000000000..6ea0be618 --- /dev/null +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb @@ -0,0 +1,107 @@ + +<%- +# +# CBRAIN Project +# +# Copyright (C) 2008-2012 +# The Royal Institution for the Advancement of Learning +# McGill University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +-%> + +<% if @userfile.is_locally_synced? %> + + <% @cbfl = @userfile.get_inmemory_cbrain_file_list %> + +

    + This Virtual File Collection consists of other CBRAIN files and collections + listed below. This simple view does allow you to explore sub-directories or + subfiles, for that you still can use Virtual File Collection view. + Note you only see files that you have assess too. +

    + The files and collection could change name or content over time. In the + table below, the names of the files are repeated: the first one is the name as + known within CBRAIN, and the second one is the original name of file when collection + was created. +

    + + <% + csv_array = @cbfl.cached_csv_array + per_page = 500 + nb_row = csv_array.size + page = (params[:page] || 1).to_i + page = 1 if page < 1 + csv_array = WillPaginate::Collection.create(page, per_page) do |pager| + pager.replace(csv_array[(page-1)*per_page, per_page] || []) + pager.total_entries = csv_array.size + pager + end + %> + +
    + + <%= will_paginate csv_array, + :params => { :controller => :userfiles, :action => :show, :sort_index => @sort_index }, + :container => false + %> + (<%= pluralize nb_row, "file" %> in this list) + +
    + +
    - <% if @userfile.is_locally_synced? %> - <%= select_all_checkbox "collection_checkbox" %> - <% end %> + File DL - <%= (" " * 6 * file.depth).html_safe %> +  
    <% if file.symbolic_type == :directory %> @@ -45,11 +45,16 @@ <% end %>   + <% bname = Pathname.new(file.name).basename.to_s %> + + <% fname = file.name %> + <% fname = fname.delete_prefix(@userfile.name + '/') if @userfile != file.userfile %> <% if file.size > 0 %> - <%= data_link file.name, @userfile # file.userfile? %> + fname=<%= fname %> + <%= data_link fname, file.userfile %> <% else %> - <%= Pathname.new(file.name).basename.to_s %> + <%= bname %> <% end %> <% if file.symbolic_type == :directory %> @@ -62,9 +67,17 @@
    <% if file.symbolic_type == :regular && file.size > 0 && file.size < UserfilesController::MAX_DOWNLOAD_MEGABYTES.megabytes %> - <%= link_to url_for(:action => :content, :content_loader => :collection_file, :arguments => file.name) do %> - + + <% if @userfile.id != file.userfile.id %> + <% download_url = url_for(:action => :content, :content_loader => :collection_file, :id => file.userfile.id, :arguments => fname) %> + <% else %> + <% download_url = url_for(:action => :download, :id => file.userfile.id) %> <% end %> + + <%= link_to download_url do %> + + <% end %> + <% end %>
    Loading...
    Error. <%= error_message %>
    + + <% + ################################ + # Top headers of table + ################################ + %> + + + <% attlist = @cbfl.class.const_get('ATTRIBUTES_LIST') %> + <% attlist.each do |att| %> + <% att = :project if att == :group_id %> + + <% end %> + + + <% + ################################ + # Main body of table + ################################ + %> + + <% csv_array.each do |cvs_row| %> + + <% cur_file = nil %> + <% attlist.each_with_index do |att,idx|%> + <% val = cvs_row[idx] %> + + <% end %> + + <% end %> + +
    <%= att.to_s.sub(/_id\z/,"").classify.gsub(/(.+)([A-Z])/, '\1 \2') %>
    + <% if att == :id %> + <% cur_file = Userfile.find_all_accessible_by_user(current_user, :access_requested => :read).where(:id => val).first %> + <%= val %> : <%= link_to_userfile_if_accessible(cur_file) %> + <% else %> + <%= val.nil? ? "-" : val %> + <% end %> +
    + + <%= link_to 'Extract a file list', { :action => 'export_file_list', :file_ids => @userfile.get_userfiles.pluck(:id) }, :class => "button", :method => :post %> + +<% end %> From f01c9496fa3bc448394d17b00b992eef862d4233 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 21 Oct 2024 18:35:21 -0400 Subject: [PATCH 13/23] extract userfile for virtual collection checkpoint --- .../app/controllers/userfiles_controller.rb | 95 ++++++++++++++++++- .../views/_directory_contents.html.erb | 4 +- .../views/_file_collection_form.html.erb | 2 +- .../views/_plain_file_list_row.html.erb | 2 +- BrainPortal/config/routes.rb | 1 + 5 files changed, 95 insertions(+), 9 deletions(-) diff --git a/BrainPortal/app/controllers/userfiles_controller.rb b/BrainPortal/app/controllers/userfiles_controller.rb index 9c4499831..f7aea8725 100644 --- a/BrainPortal/app/controllers/userfiles_controller.rb +++ b/BrainPortal/app/controllers/userfiles_controller.rb @@ -40,7 +40,7 @@ class UserfilesController < ApplicationController around_action :permission_check, :only => [ :download, :update_multiple, :delete_files, :create_collection, :create_virtual_collection, :change_provider, :quality_control, - :export_file_list + :export_file_list, :extract_from_virtual_collection ] MAX_DOWNLOAD_MEGABYTES = 400 @@ -1286,6 +1286,90 @@ def download #:nodoc: #Extract a file from a collection and register it separately #in the database. + def extract_from_virtual_collection #:nodoc: + success = failure = 0 + + unless params[:file_names] && params[:file_names].size > 0 + flash[:notice] = "No files selected for extraction" + redirect_to :action => :show + return + end + + collection_ids_file_names = params[:file_names].map {|x| x.split('#', 1)} + + collections_file_names = collection_ids_file_names.map do |collection_id, file| + unless collection_id == old_id + collection = FileCollection.find_accessible_by_user(params[:collection_id], current_user, :access_requested => :read) + data_provider = collection.data_provider + old_id = collection_id + if data_provider.read_only? + flash[:error] = "Unfortunately this file is located on a DataProvider that is not writable, so we can't extract its internal files." + redirect_to :action => :show + return + end + end + end + + results = collections_file_names.each do |collection, file_name| + + # Extract each file + + collection_path = collection.cache_full_path + + data_provider = collection.data_provider + if data_provider.read_only? + flash[:error] = "Unfortunately this file is located on a DataProvider that is not writable, so we can't extract its internal files." + redirect_to :action => :show + return + end + + # Validations; make sure "file" is a path inside the collection + rel_path = Pathname.new(file) + next :not_relative unless rel_path.relative? + full_path = collection_path.parent + rel_path + full_path = File.realpath(full_path.to_s) rescue nil + next :not_resolve unless full_path + next :is_symlink if File.symlink?(full_path.to_s) + next :not_file unless File.file?(full_path.to_s) + next :outside_col unless full_path.start_with? collection_path.to_s + + basename = rel_path.basename.to_s + file_type = Userfile.suggested_file_type(basename) || SingleFile + userfile = file_type.new( + :name => basename, + :user_id => current_user.id, + :group_id => collection.group_id, + :data_provider_id => data_provider.id + ) + Dir.chdir(collection_path.parent) do + next :cannot_save_userfile unless userfile.save + userfile.addlog("Extracted from collection '#{collection.name}'.") + begin + userfile.cache_copy_from_local_file(full_path.to_s) + next :ok + rescue + userfile.data_provider_id = nil # nullifying will skip the provider_erase() in the destroy() + userfile.destroy + next :exception_copy + end + end + end + + success = results.count { |x| x == :ok } + failure = results.size - success + if success > 0 + flash[:notice] = "#{success} files were successfully extracted." + end + if failure > 0 + flash[:error] = "#{failure} files could not be extracted." + end + + redirect_to :action => :index + end + + + #Extract files from a collection and register it separately + #in the database. def extract_from_collection #:nodoc: success = failure = 0 @@ -1321,10 +1405,10 @@ def extract_from_collection #:nodoc: basename = rel_path.basename.to_s file_type = Userfile.suggested_file_type(basename) || SingleFile userfile = file_type.new( - :name => basename, - :user_id => current_user.id, - :group_id => collection.group_id, - :data_provider_id => data_provider.id + :name => basename, + :user_id => current_user.id, + :group_id => collection.group_id, + :data_provider_id => data_provider.id ) Dir.chdir(collection_path.parent) do next :cannot_save_userfile unless userfile.save @@ -1354,6 +1438,7 @@ def extract_from_collection #:nodoc: redirect_to :action => :index end + # Compress/archive a set of userfiles. Wrapper action for # +manage_compression+ with operation :compress. def compress #:nodoc: diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb index 594bf7e12..6357afb63 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb @@ -27,7 +27,7 @@ <% base_dir = base_dir.presence || "." %> <% file_list ||= ( @userfile.list_linked_files(base_dir, [:regular, :directory, :link]) rescue [] ) %> -<% @userfile.list_errors&.each do |error_message| %> +<% @userfile&.list_errors&.each do |error_message| %> "> Error. <%= error_message %> @@ -53,7 +53,7 @@ :id => file.userfile.id, :action => :display, :viewer => "directory_contents", - :viewer_userfile_class => "FileCollection", + :viewer_userfile_class => "VirtualFileCollection", :base_directory => ".", :apply_div => "false" ), diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb index b9ad42ce8..3998fed1e 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb @@ -33,7 +33,7 @@ <%= form_for @userfile, :as => :userfile, :url => { :controller => :userfiles, - :action => :extract_from_collection + :action => :extract_from_virtual_collection }, :html => { :method => :post, :id => "userfile_edit_#{@userfile.id}_#{base_directory}" diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb index 37049256e..db1b0810b 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb @@ -30,7 +30,7 @@ <% if @userfile.is_locally_synced? && file.symbolic_type == :regular %> - <%= check_box_tag("file_names[]", file.name, false, :class => "collection_checkbox", :id => nil) %> + <%= check_box_tag("file_names[]", "#{@userfile.id}##{file.name}", false, :class => "collection_checkbox", :id => nil) %> <% end %> diff --git a/BrainPortal/config/routes.rb b/BrainPortal/config/routes.rb index 92bc2b666..6848b1f91 100644 --- a/BrainPortal/config/routes.rb +++ b/BrainPortal/config/routes.rb @@ -141,6 +141,7 @@ get 'stream/*file_path' => 'userfiles#stream' get 'display' post 'extract_from_collection' + post 'extract_from_virtual_collection' end collection do post 'download' From 056a07b9a264829922e3741f8d641b2a88929e1b Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Thu, 24 Oct 2024 15:18:48 -0400 Subject: [PATCH 14/23] extract userfiles for virtual collection #1135 --- .../app/controllers/userfiles_controller.rb | 101 ++++++++---------- .../views/_directory_contents.html.erb | 2 +- .../views/_file_collection_form.html.erb | 5 + .../views/_file_collection_top_table.html.erb | 1 + .../views/_plain_file_list_row.html.erb | 2 +- .../virtual_file_collection.rb | 2 +- 6 files changed, 55 insertions(+), 58 deletions(-) diff --git a/BrainPortal/app/controllers/userfiles_controller.rb b/BrainPortal/app/controllers/userfiles_controller.rb index f7aea8725..d5ac1b01c 100644 --- a/BrainPortal/app/controllers/userfiles_controller.rb +++ b/BrainPortal/app/controllers/userfiles_controller.rb @@ -40,7 +40,7 @@ class UserfilesController < ApplicationController around_action :permission_check, :only => [ :download, :update_multiple, :delete_files, :create_collection, :create_virtual_collection, :change_provider, :quality_control, - :export_file_list, :extract_from_virtual_collection + :export_file_list ] MAX_DOWNLOAD_MEGABYTES = 400 @@ -1284,86 +1284,77 @@ def download #:nodoc: end end - #Extract a file from a collection and register it separately - #in the database. + # Extract files from a virtual collection and register them separately + # in the database. def extract_from_virtual_collection #:nodoc: success = failure = 0 - unless params[:file_names] && params[:file_names].size > 0 + unless params[:collection_files] && params[:collection_files].size > 0 flash[:notice] = "No files selected for extraction" redirect_to :action => :show return end - collection_ids_file_names = params[:file_names].map {|x| x.split('#', 1)} + collection_ids_file_pairs = params[:collection_files].map {|x| x.split('#', 2)} + collections_file_names = collection_ids_file_pairs.group_by { |first, _| first } + # add the object to hash and validate provider access, while keeping primitive keys - collections_file_names = collection_ids_file_names.map do |collection_id, file| - unless collection_id == old_id - collection = FileCollection.find_accessible_by_user(params[:collection_id], current_user, :access_requested => :read) - data_provider = collection.data_provider - old_id = collection_id - if data_provider.read_only? - flash[:error] = "Unfortunately this file is located on a DataProvider that is not writable, so we can't extract its internal files." - redirect_to :action => :show - return - end - end - end - - results = collections_file_names.each do |collection, file_name| - - # Extract each file - - collection_path = collection.cache_full_path - - data_provider = collection.data_provider - if data_provider.read_only? - flash[:error] = "Unfortunately this file is located on a DataProvider that is not writable, so we can't extract its internal files." + # todo - add option to specify target data_provider + collections_file_names.each do |collection_id, obj_file_list| + collection = FileCollection.find_accessible_by_user(collection_id, current_user, :access_requested => :read) + obj_file_list.each { |pair| pair[0] = collection } + data_provider = collection.data_provider + if data_provider.read_only? + flash[:error] = "Unfortunately file #{obj_filelist.second.first} of collection #{collection.name} is located on a not writable DataProvider #{data_provider.name}, so we can't extract its internal files." redirect_to :action => :show return end + end - # Validations; make sure "file" is a path inside the collection - rel_path = Pathname.new(file) - next :not_relative unless rel_path.relative? - full_path = collection_path.parent + rel_path - full_path = File.realpath(full_path.to_s) rescue nil - next :not_resolve unless full_path - next :is_symlink if File.symlink?(full_path.to_s) - next :not_file unless File.file?(full_path.to_s) - next :outside_col unless full_path.start_with? collection_path.to_s - - basename = rel_path.basename.to_s - file_type = Userfile.suggested_file_type(basename) || SingleFile - userfile = file_type.new( + results = collections_file_names.map do |collection_id, collection_file_pairs| + collection = collection_file_pairs.first.first + data_provider = collection.data_provider + collection_path = collection.cache_full_path + collection_file_pairs.map do |_, file| # Extract each file + # Validations; make sure "file" is a path inside the collection + rel_path = Pathname.new(file) + next :not_relative unless rel_path.relative? + full_path = collection_path.parent + rel_path + full_path = File.realpath(full_path.to_s) rescue nil + next :not_resolve unless full_path + next :is_symlink if File.symlink?(full_path.to_s) + next :not_file unless File.file?(full_path.to_s) + next :outside_col unless full_path.start_with? collection_path.to_s + basename = rel_path.basename.to_s + file_type = Userfile.suggested_file_type(basename) || SingleFile + userfile = file_type.new( :name => basename, :user_id => current_user.id, :group_id => collection.group_id, :data_provider_id => data_provider.id - ) - Dir.chdir(collection_path.parent) do - next :cannot_save_userfile unless userfile.save - userfile.addlog("Extracted from collection '#{collection.name}'.") - begin - userfile.cache_copy_from_local_file(full_path.to_s) - next :ok - rescue - userfile.data_provider_id = nil # nullifying will skip the provider_erase() in the destroy() - userfile.destroy - next :exception_copy + ) + Dir.chdir(collection_path.parent) do + next :cannot_save_userfile unless userfile.save + userfile.addlog("Extracted from collection '#{collection.name}'.") + begin + userfile.cache_copy_from_local_file(full_path.to_s) + next :ok + rescue + userfile.data_provider_id = nil # nullifying will skip the provider_erase() in the destroy() + userfile.destroy + next :exception_copy + end end end end - - success = results.count { |x| x == :ok } - failure = results.size - success + success = results.flatten.count { |x| x == :ok } + failure = results.flatten.size - success if success > 0 flash[:notice] = "#{success} files were successfully extracted." end if failure > 0 flash[:error] = "#{failure} files could not be extracted." end - redirect_to :action => :index end diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb index 6357afb63..db6cad4dd 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb @@ -52,7 +52,7 @@ :url => url_for( :id => file.userfile.id, :action => :display, - :viewer => "directory_contents", + :viewer => "subdirectory_contents", :viewer_userfile_class => "VirtualFileCollection", :base_directory => ".", :apply_div => "false" diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb index 3998fed1e..c956f8616 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_form.html.erb @@ -49,6 +49,11 @@
    <% end %> + <% if @userfile.is_locally_synced? %> +

    + <%= submit_tag "Extract Files from Sub-Collections" %> + <% end %> + <% end %> diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb index a3114b88d..2c202d240 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection_top_table.html.erb @@ -30,6 +30,7 @@ <% base_dir = params[:base_directory].presence || "" %> +

    diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb index 8223bcbe8..25f08decd 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb @@ -89,7 +89,7 @@ def sync_to_provider #:nodoc: true end - # Sets the set of FileCollections that constitute VirtualFileCollection. + # Sets the set of SingleFiles and FileCollections that constitute VirtualFileCollection. # The CSV file inside the study will be created/updated, # as well as all the symbolic links. The content # is NOT synced to the provider side. From e32b6ff115dbcf90c60ec2513c659abff2bc4c72 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 28 Oct 2024 12:03:56 -0400 Subject: [PATCH 15/23] minor fixes and cleaning Virtural Collection #1135 --- .../app/controllers/userfiles_controller.rb | 4 +-- .../views/_cbrain_file_list.html.erb | 2 +- .../virtual_file_collection.rb | 34 ++++++++----------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/BrainPortal/app/controllers/userfiles_controller.rb b/BrainPortal/app/controllers/userfiles_controller.rb index d5ac1b01c..bd44a72ac 100644 --- a/BrainPortal/app/controllers/userfiles_controller.rb +++ b/BrainPortal/app/controllers/userfiles_controller.rb @@ -39,8 +39,8 @@ class UserfilesController < ApplicationController around_action :permission_check, :only => [ :download, :update_multiple, :delete_files, - :create_collection, :create_virtual_collection, :change_provider, :quality_control, - :export_file_list + :create_collection, :change_provider, :quality_control, + :export_file_list, :create_virtual_collection ] MAX_DOWNLOAD_MEGABYTES = 400 diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb index 6ea0be618..e802a220a 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb @@ -24,7 +24,7 @@ <% if @userfile.is_locally_synced? %> - <% @cbfl = @userfile.get_inmemory_cbrain_file_list %> + <% @cbfl = @userfile.cached_cbrain_file_list %>

    This Virtual File Collection consists of other CBRAIN files and collections diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb index 25f08decd..d160f268c 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb @@ -96,8 +96,12 @@ def sync_to_provider #:nodoc: def set_virtual_file_collection(userfiles) cb_error "Multi layer collections are not supported." if userfiles.any? { |f| f.is_a?(VirtualFileCollection) || f.is_a?(CivetVirtualStudy) } + content = CbrainFileList.create_csv_file_from_userfiles(userfiles) + # This optimize so we don't reload the content for making the symlinks + @cbfl = CbrainFileList.new + @cbfl.load_from_content(content) # Prepare CSV content - @cbfl = self.get_inmemory_cbrain_file_list + @files = nil # Write CSV content to the interal CSV file @@ -130,15 +134,13 @@ def list_linked_files(dir=:all, allowed_types = :regular) userfile = userfiles_by_name[fname] if types.include?(:directory) && userfile.is_a?(FileCollection) file.symbolic_type = :directory - file.userfile = userfile - # binding.pry + file.userfile = userfile file elsif types.include?(:file) && userfile.is_a?(SingleFile) - file = userfile.list_files.first.clone - file&.name = self.name + '/' + fname - file&.symbolic_type = :regular - file.userfile = userfile - # binding.pry + file = userfile.list_files.first.clone + file.name = self.name + '/' + fname + file.symbolic_type = :regular + file.userfile = userfile file end end @@ -148,7 +150,7 @@ def list_linked_files(dir=:all, allowed_types = :regular) if dir.is_a? String name, dir = dir.split '/' - dir |= '.' + dir |= '.' userfiles = userfiles.select { |x| x.name == name } end @@ -160,12 +162,6 @@ def list_linked_files(dir=:all, allowed_types = :regular) end.flatten end - # todo - remove unless needed for creation? - # def validate_componets - # ufiles = self.get_userfiles - # error "Nested Virtual Collection" if ufile.is_a?(VirtualFileCollection) || ufile.is_a?(CivetVirtualStudy) || ufile.type.lower.include?('virtual') - # end - # Returns the files IDs def get_ids self.get_userfiles.map(&:id) @@ -200,8 +196,8 @@ def get_userfiles #:nodoc: # despite exception above with some luck and effort user can render file view, so # we show that error messages instead of the directory tree def list_errors - @cbfl = self.get_inmemory_cbrain_file_list - @files ||= @cbfl.userfiles_accessible_by_user!(self.user).compact.compact + @cbfl = self.cached_cbrain_file_list + @files ||= @cbfl.userfiles_accessible_by_user!(self.user).compact file_names = @files.map(&:name) dup_names = file_names.select { |name| file_names.count(name) >1 }.uniq errors = [] @@ -213,8 +209,8 @@ def list_errors errors end - # updates in memory cbrain file list cache - def get_inmemory_cbrain_file_list + # caches cbrain file in memory + def cached_cbrain_file_list if @cbfl.blank? @cbfl = CbrainFileList.new file_content = File.read(csv_cache_full_path.to_s) From 591d892f6a5117895630f6fc1d63540a040a3922 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 28 Oct 2024 12:10:03 -0400 Subject: [PATCH 16/23] refactor userfile getter of Virtual Collection #1135 --- .../virtual_file_collection.rb | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb index d160f268c..17e491476 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb @@ -172,16 +172,10 @@ def get_ids # based on the owner of the VirtualFileCollection. def get_userfiles #:nodoc: - if @cbfl.blank? - @cbfl = CbrainFileList.new - file_content = File.read(csv_cache_full_path.to_s) - @cbfl.load_from_content(file_content) - end - - @files ||= @cbfl.userfiles_accessible_by_user!(self.user).compact - + @cbfl = self.cached_cbrain_file_list # loads cbrain file list content + @files ||= @cbfl.userfiles_accessible_by_user!(self.user).compact file_names = @files.map(&:name) - dup_names = file_names.select { |name| file_names.count(name) >1 }.uniq + dup_names = file_names.select { |name| file_names.count(name) >1 }.uniq cb_error "Virtual file collection contains duplicate filenames #{dup_names.join(',')}" if dup_names.present? @files.each do |f| cb_error "Nested virtual file collections are not supported, remove file with id #{f.id}" if ( @@ -209,7 +203,7 @@ def list_errors errors end - # caches cbrain file in memory + # caches cbrain file list file in memory def cached_cbrain_file_list if @cbfl.blank? @cbfl = CbrainFileList.new From dd6053b52194d671bd940fa85b46031703b399e5 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 28 Oct 2024 13:24:11 -0400 Subject: [PATCH 17/23] no CBRAIN_ARCHIVE_CONTENT_BASENAME Virtual Collection #113 --- .../virtual_file_collection/virtual_file_collection.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb index 17e491476..b6f0b7e8c 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb @@ -30,9 +30,6 @@ class VirtualFileCollection < FileCollection CSV_BASENAME = "_virtual_file_collection.cbcsv" # todo. add .bidsignore file, otherwise bids validation. Or we can allow CBRAIN filenames starting with dot - CBRAIN_ARCHIVE_CONTENT_BASENAME = nil - - reset_viewers # we opted to ignore superclass viewers rather than adjust them # this viewer closely resembles one for regular File Collection has_viewer :name => 'Virtual File Collection', :partial => :file_collection , :if => :is_locally_synced? From 62f138ff31f47d3f42dacb86c5589326e15dca57 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 28 Oct 2024 13:33:20 -0400 Subject: [PATCH 18/23] improve a controller comment Virtual Collection #113 --- BrainPortal/app/controllers/userfiles_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BrainPortal/app/controllers/userfiles_controller.rb b/BrainPortal/app/controllers/userfiles_controller.rb index bd44a72ac..9791cb006 100644 --- a/BrainPortal/app/controllers/userfiles_controller.rb +++ b/BrainPortal/app/controllers/userfiles_controller.rb @@ -1040,7 +1040,7 @@ def create_collection #:nodoc: end - #Create a collection from the selected files. + # Create a virtual collection from the selected files. def create_virtual_collection #:nodoc: filelist = params[:file_ids].uniq || [] data_provider_id = params[:data_provider_id_for_collection] From 6d3b3a77ea454e54d0768c8bd03e5b8a2976b24e Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 28 Oct 2024 13:48:25 -0400 Subject: [PATCH 19/23] undo irrelevant reformat Virtual Collection #1135 --- BrainPortal/app/controllers/userfiles_controller.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/BrainPortal/app/controllers/userfiles_controller.rb b/BrainPortal/app/controllers/userfiles_controller.rb index 9791cb006..df5b8aeda 100644 --- a/BrainPortal/app/controllers/userfiles_controller.rb +++ b/BrainPortal/app/controllers/userfiles_controller.rb @@ -1396,10 +1396,10 @@ def extract_from_collection #:nodoc: basename = rel_path.basename.to_s file_type = Userfile.suggested_file_type(basename) || SingleFile userfile = file_type.new( - :name => basename, - :user_id => current_user.id, - :group_id => collection.group_id, - :data_provider_id => data_provider.id + :name => basename, + :user_id => current_user.id, + :group_id => collection.group_id, + :data_provider_id => data_provider.id ) Dir.chdir(collection_path.parent) do next :cannot_save_userfile unless userfile.save @@ -1429,7 +1429,6 @@ def extract_from_collection #:nodoc: redirect_to :action => :index end - # Compress/archive a set of userfiles. Wrapper action for # +manage_compression+ with operation :compress. def compress #:nodoc: From 3daf39b086654cd8496af2f6ac943fe42efa0532 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 28 Oct 2024 13:49:37 -0400 Subject: [PATCH 20/23] unroll checkbox column deletion for dir tree browsing Virtual Collection #1135 --- .../views/_directory_contents.html.erb | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb index db6cad4dd..c237a665b 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_directory_contents.html.erb @@ -83,34 +83,3 @@ <% end %> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From ef7401b3e4a69ddb642ffa7fe513194f6fbeb359 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 28 Oct 2024 16:38:23 -0400 Subject: [PATCH 21/23] minor view improvements Virtual Collection #1135 --- .../views/_cbrain_file_list.html.erb | 7 +- .../views/_file_collection.html.erb | 3 + .../_subdir_plain_file_list_row.html.erb | 75 +++++++++++++++++++ .../views/_subdirectory_contents.html.erb | 75 +++++++++++++++++++ 4 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_subdir_plain_file_list_row.html.erb create mode 100644 BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_subdirectory_contents.html.erb diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb index e802a220a..95232a8d7 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_cbrain_file_list.html.erb @@ -31,8 +31,9 @@ listed below. This simple view does allow you to explore sub-directories or subfiles, for that you still can use Virtual File Collection view. Note you only see files that you have assess too. -

    - The files and collection could change name or content over time. In the +

    This is an experimental feature. + The files and collection could change name or content over time, potentially making collection unusable. + In the table below, the names of the files are repeated: the first one is the name as known within CBRAIN, and the second one is the original name of file when collection was created. @@ -101,7 +102,7 @@ <% end %>

    diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb index db1b0810b..98322cad1 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_plain_file_list_row.html.erb @@ -30,7 +30,7 @@ <% if @userfile.is_locally_synced? && file.symbolic_type == :regular %> - <%= check_box_tag("file_names[]", "#{@userfile.id}##{file.name}", false, :class => "collection_checkbox", :id => nil) %> + <%= check_box_tag("collection_files[]", "#{@userfile.id}##{file.name}", false, :class => "collection_checkbox", :id => nil) %> <% end %>
    - +

    <%= link_to 'Extract a file list', { :action => 'export_file_list', :file_ids => @userfile.get_userfiles.pluck(:id) }, :class => "button", :method => :post %> <% end %> diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.html.erb index 2c7292876..9ac5a84f8 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.html.erb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_file_collection.html.erb @@ -33,4 +33,7 @@ :locals => { :base_directory => base_dir } %>
    +This is a virtual file collection, which means the parts of this collections are not stored together, but are other +collections or individual files, registered in CBRAIN separately. This is an experimental feature. +You can navigate the data that you have access below. diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_subdir_plain_file_list_row.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_subdir_plain_file_list_row.html.erb new file mode 100644 index 000000000..5f292d7e2 --- /dev/null +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_subdir_plain_file_list_row.html.erb @@ -0,0 +1,75 @@ + +<%- +# +# CBRAIN Project +# +# Copyright (C) 2008-2012 +# The Royal Institution for the Advancement of Learning +# McGill University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +-%> + +<% + # This partial receives one local variable: + # file : a file structure as returned by the FileCollection listing method + # Note that is contains a full relative path, starting with the @userfile's name itself. +%> + + + <% if @userfile.is_locally_synced? && file.symbolic_type == :regular %> + <%= check_box_tag("collection_files[]", "#{@userfile.id}##{file.name}", false, :class => "collection_checkbox", :id => nil) %> + <% end %> + + + + <%= (" " * 6 * file.depth).html_safe %> + +
    + <% if file.symbolic_type == :directory %> + <%= image_tag "/images/folder_icon_solid.png" %> + <% else %> + <%= image_tag "/images/file_icon.png" %> + <% end %> + +   + + <% if file.size > 0 %> + <%= data_link file.name, @userfile %> + <% else %> + <%= Pathname.new(file.name).basename.to_s %> + <% end %> + + <% if file.symbolic_type == :directory %> +   + Expand + + <% end %> +
    + + + + <% if file.symbolic_type == :regular && file.size > 0 && file.size < UserfilesController::MAX_DOWNLOAD_MEGABYTES.megabytes %> + <%= link_to url_for(:action => :content, :content_loader => :collection_file, :arguments => file.name) do %> + + <% end %> + <% end %> + + + + <% if file.symbolic_type != :directory %> + <%= colored_pretty_size(file.size) %> + <% end %> + diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_subdirectory_contents.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_subdirectory_contents.html.erb new file mode 100644 index 000000000..a657e23f7 --- /dev/null +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/views/_subdirectory_contents.html.erb @@ -0,0 +1,75 @@ + +<%- +# +# CBRAIN Project +# +# Copyright (C) 2008-2012 +# The Royal Institution for the Advancement of Learning +# McGill University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +-%> + +<% limit = 500 %> +<% base_dir = base_directory rescue params[:base_directory] %> +<% base_dir = base_dir.presence || "." %> + +<% file_list ||= ( @userfile.list_files(base_dir, [:regular, :directory]) rescue [] ) %> + +<% if file_list.blank? %> + + "> + + (Empty) + + + +<% else %> + + <% for file in file_list[0,limit] %> + <% if file.symbolic_type == :directory %> + <%= on_click_ajax_replace( { :element => "tr", + :url => url_for(:action => :display, + :viewer => "subdirectory_contents", + :viewer_userfile_class => "VirtualFileCollection", + :base_directory => file.name.sub(/\A[^\/]+\//, ""), + :apply_div => "false" + ), + :position => "after", + :before => "Loading..." + }, + { :class => "#{cycle("list-odd", "list-even")}", + :id => file.name.gsub(/\W+/, "_") + } + ) do %> + <%= render :file => @viewer.partial_path(:subdir_plain_file_list_row), :locals => {:file => file} %> + <% end %> + <% else %> + "> + <%= render :file => @viewer.partial_path(:subdir_plain_file_list_row), :locals => {:file => file} %> + + <% end %> + <% end %> + + <% if file_list.size > limit %> + "> + + <%= (" " * 6 * file_list.first.depth).html_safe %> ... <%= image_tag "/images/lotsa_files_icon.png" %> <%= pluralize(file_list.size-limit, "more entry") %> + + + <% end %> + +<% end %> + From 64528f5f3b872b49ff6e2ba4526872e533395bbf Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Tue, 29 Oct 2024 08:15:32 -0400 Subject: [PATCH 22/23] forbid Virtual Collection inside Collections (as of now user will get only csv files) #1135 --- BrainPortal/app/controllers/userfiles_controller.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/BrainPortal/app/controllers/userfiles_controller.rb b/BrainPortal/app/controllers/userfiles_controller.rb index df5b8aeda..105ca5878 100644 --- a/BrainPortal/app/controllers/userfiles_controller.rb +++ b/BrainPortal/app/controllers/userfiles_controller.rb @@ -1001,6 +1001,15 @@ def create_collection #:nodoc: return end + virtual_files = VirtualFileCollection.where(id: filelist).to_a | CivetVirtualStudy.where(id: filelist).to_a + + if virtual_files.present? + virtual_files = Userfile.find_accessible_by_user(virtual_files.pluck(:id), current_user, :access_requested => :read) + flash[:error] = "Collections of Virtual Collection are not allowed. Exclude #{virtual_files.map(&:name).to_sentence} " if virtual_files + redirect_to :action => :index, :format => request.format.to_sym + return + end + collection = FileCollection.new( :user_id => current_user.id, :group_id => file_group, From 5eae85b8f766a3110bf3a5bf537f190ecfeec333 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Tue, 29 Oct 2024 11:42:31 -0400 Subject: [PATCH 23/23] remove direct CivetVirtual study type checking Virtual Collection #1135 --- BrainPortal/app/controllers/userfiles_controller.rb | 2 +- .../virtual_file_collection/virtual_file_collection.rb | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/BrainPortal/app/controllers/userfiles_controller.rb b/BrainPortal/app/controllers/userfiles_controller.rb index 105ca5878..13bcdcd8c 100644 --- a/BrainPortal/app/controllers/userfiles_controller.rb +++ b/BrainPortal/app/controllers/userfiles_controller.rb @@ -1001,7 +1001,7 @@ def create_collection #:nodoc: return end - virtual_files = VirtualFileCollection.where(id: filelist).to_a | CivetVirtualStudy.where(id: filelist).to_a + virtual_files = FileCollection.where(id: filelist, type: ['CivetVirtualStudy', 'VirtualFileCollection']).to_a if virtual_files.present? virtual_files = Userfile.find_accessible_by_user(virtual_files.pluck(:id), current_user, :access_requested => :read) diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb index b6f0b7e8c..093978db8 100644 --- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb +++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/virtual_file_collection/virtual_file_collection.rb @@ -177,8 +177,7 @@ def get_userfiles #:nodoc: @files.each do |f| cb_error "Nested virtual file collections are not supported, remove file with id #{f.id}" if ( f.is_a?(VirtualFileCollection) || - f.is_a?(CivetVirtualStudy) || - f.type.downcase.include?('virtual') + f.type == CivetVirtualStudy ) end end @@ -194,7 +193,7 @@ def list_errors errors = [] errors << "Virtual file collection contains duplicate filenames #{dup_names.join(',')}" if dup_names.present? virtual_files_names = @files.select do |f| - f.is_a?(VirtualFileCollection) || f.is_a?(CivetVirtualStudy) || f.type.downcase.include?('virtual') + f.is_a?(VirtualFileCollection) || f.type == "CivetVirtualStudy" end.map(&:name) errors << "Nested virtual file collections are not supported, remove files #{virtual_files_names.join ", "}" if virtual_files_names.present? errors