Merge branch '8784-dir-listings'
[arvados.git] / apps / workbench / app / models / collection.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 require "arvados/keep"
6
7 class Collection < ArvadosBase
8   MD5_EMPTY = 'd41d8cd98f00b204e9800998ecf8427e'
9
10   def default_name
11     if Collection.is_empty_blob_locator? self.uuid
12       "Empty Collection"
13     else
14       super
15     end
16   end
17
18   # Return true if the given string is the locator of a zero-length blob
19   def self.is_empty_blob_locator? locator
20     !!locator.to_s.match("^#{MD5_EMPTY}(\\+.*)?\$")
21   end
22
23   def self.goes_in_projects?
24     true
25   end
26
27   def manifest
28     if @manifest.nil? or manifest_text_changed?
29       @manifest = Keep::Manifest.new(manifest_text || "")
30     end
31     @manifest
32   end
33
34   def files
35     # This method provides backwards compatibility for code that relied on
36     # the old files field in API results.  New code should use manifest
37     # methods directly.
38     manifest.files
39   end
40
41   def content_summary
42     if total_bytes > 0
43       ApplicationController.helpers.human_readable_bytes_html(total_bytes) + " " + super
44     else
45       super + " modified at " + modified_at.to_s
46     end
47   end
48
49   def total_bytes
50     manifest.files.inject(0) { |sum, filespec| sum + filespec.last }
51   end
52
53   def files_tree
54     tree = manifest.files.group_by do |file_spec|
55       File.split(file_spec.first)
56     end
57     return [] if tree.empty?
58     # Fill in entries for empty directories.
59     tree.keys.map { |basedir, _| File.split(basedir) }.each do |splitdir|
60       until tree.include?(splitdir)
61         tree[splitdir] = []
62         splitdir = File.split(splitdir.first)
63       end
64     end
65     dir_to_tree = lambda do |dirname|
66       # First list subdirectories, with their files inside.
67       subnodes = tree.keys.select { |bd, td| (bd == dirname) and (td != '.') }
68         .sort.flat_map do |parts|
69         [parts + [nil]] + dir_to_tree.call(File.join(parts))
70       end
71       # Then extend that list with files in this directory.
72       subnodes + tree[File.split(dirname)]
73     end
74     dir_to_tree.call('.')
75   end
76
77   def editable_attributes
78     %w(name description manifest_text filename)
79   end
80
81   def provenance
82     arvados_api_client.api "collections/#{self.uuid}/", "provenance"
83   end
84
85   def used_by
86     arvados_api_client.api "collections/#{self.uuid}/", "used_by"
87   end
88
89   def uuid
90     if self[:uuid].nil?
91       return self[:portable_data_hash]
92     else
93       super
94     end
95   end
96
97   def friendly_link_name lookup=nil
98     name || portable_data_hash
99   end
100
101   def textile_attributes
102     [ 'description' ]
103   end
104
105   def untrash
106     arvados_api_client.api(self.class, "/#{self.uuid}/untrash", {"ensure_unique_name" => true})
107   end
108 end