Files
codeocean/lib/file_tree.rb
2022-10-29 18:49:18 +02:00

130 lines
3.8 KiB
Ruby

# frozen_string_literal: true
class FileTree
def file_icon(file)
return 'fa-solid fa-lock' if file.missing_read_permissions?
if file.file_type.audio?
'fa-regular fa-file-audio'
elsif file.file_type.compressed?
'fa-regular fa-file-zipper'
elsif file.file_type.excel?
'fa-regular fa-file-excel'
elsif file.file_type.image?
'fa-regular fa-file-image'
elsif file.file_type.pdf?
'fa-regular fa-file-pdf'
elsif file.file_type.powerpoint?
'fa-regular fa-file-powerpoint'
elsif file.file_type.video?
'fa-regular fa-file-video'
elsif file.file_type.word?
'fa-regular fa-file-word'
elsif file.read_only?
'fa-solid fa-lock'
elsif file.file_type.executable?
'fa-regular fa-file-code'
elsif file.file_type.renderable? || file.file_type.csv?
'fa-regular fa-file-lines'
else
'fa-regular fa-file'
end
end
private :file_icon
def folder_icon
'fa-regular fa-folder'
end
private :folder_icon
# @param [CodeOcean::File] files The files to be displayed in the tree.
# @param [String] directories Additional directories to be displayed in the tree
# @param [Boolean] force_closed Specify whether the tree should be closed by default
def initialize(files = [], directories = [], force_closed: false)
# Our tree needs a root node, but we won't display it.
@root = Tree::TreeNode.new('ROOT')
@force_closed = force_closed
files.uniq(&:filepath).each do |file|
parent = @root
(file.path || '').split('/').each do |segment|
node = parent.children.detect {|child| child.name == segment } || parent.add(Tree::TreeNode.new(segment))
parent = node
end
parent.add(Tree::TreeNode.new(file.name_with_extension, file))
end
directories.uniq.each do |directory|
parent = @root
(directory || '').split('/').each do |segment|
node = parent.children.detect {|child| child.name == segment } || parent.add(Tree::TreeNode.new(segment))
parent = node
end
end
end
def map_to_js_tree(node)
{
children: children(node),
icon: node_icon(node),
id: node.content.try(:ancestor_id),
state: {
disabled: !(node.leaf? && node.content.is_a?(CodeOcean::File)),
opened: !(node.leaf? || @force_closed),
},
text: name(node),
download_path: node.content.try(:download_path),
path: node.content.try(:download_path) ? nil : path(node),
}
end
private :map_to_js_tree
def node_icon(node)
if node.leaf? && !node.root? && node.content.is_a?(CodeOcean::File)
file_icon(node.content)
else
folder_icon
end
end
private :node_icon
def children(node)
if node.children.present? || node.content.is_a?(CodeOcean::File)
node.children.sort_by {|n| n.name.downcase }.map {|child| map_to_js_tree(child) }
else
# Folders added manually should always be expandable and therefore might have children.
# This allows users to open the folder and get a refreshed view, even if it might be empty.
true
end
end
private :children
def name(node)
# We just need any information that is only present in files retrieved from the runner's file system.
# In our case, that is the presence of the `privileged_execution` attribute.
if node.content.is_a?(CodeOcean::File) && !node.content.privileged_execution.nil?
node.content.name_with_extension_and_size
else
node.name
end
end
private :name
def path(node)
"#{node.parentage&.reverse&.drop(1)&.map(&:name)&.join('/')}/#{node.name}"
end
private :path
def to_js_tree
{
core: {
data: @root.children.sort_by {|node| node.name.downcase }.map {|child| map_to_js_tree(child) },
},
}
end
def to_js_tree_in_json
to_js_tree.to_json
end
end