talia_core 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +41 -0
- data/bin/talia +33 -0
- data/lib/JXslt/jxslt.rb +60 -0
- data/lib/acts_as_roled.rb +11 -0
- data/lib/core_ext/platform.rb +9 -0
- data/lib/core_ext/string.rb +6 -0
- data/lib/core_ext.rb +1 -0
- data/lib/custom_template.rb +4 -0
- data/lib/loader_helper.rb +62 -0
- data/lib/mysql.rb +1214 -0
- data/lib/progressbar.rb +236 -0
- data/lib/role.rb +12 -0
- data/lib/talia_cl/command_line.rb +39 -0
- data/lib/talia_cl/commands/standalone/cl_options.rb +9 -0
- data/lib/talia_cl/commands/standalone/standalone_generate.rb +75 -0
- data/lib/talia_cl/commands/standalone.rb +25 -0
- data/lib/talia_cl/commands/talia_console/cl_options.rb +55 -0
- data/lib/talia_cl/commands/talia_console/console_commands.rb +37 -0
- data/lib/talia_cl/commands/talia_console/talia_commands.rb +131 -0
- data/lib/talia_cl/commands/talia_console.rb +47 -0
- data/lib/talia_cl/core_commands.rb +11 -0
- data/lib/talia_cl.rb +47 -0
- data/lib/talia_core/active_source.rb +372 -0
- data/lib/talia_core/active_source_parts/class_methods.rb +378 -0
- data/lib/talia_core/active_source_parts/predicate_handler.rb +89 -0
- data/lib/talia_core/active_source_parts/rdf.rb +131 -0
- data/lib/talia_core/active_source_parts/sql_helper.rb +36 -0
- data/lib/talia_core/active_source_parts/xml/base_builder.rb +47 -0
- data/lib/talia_core/active_source_parts/xml/generic_reader.rb +363 -0
- data/lib/talia_core/active_source_parts/xml/rdf_builder.rb +88 -0
- data/lib/talia_core/active_source_parts/xml/source_builder.rb +73 -0
- data/lib/talia_core/active_source_parts/xml/source_reader.rb +20 -0
- data/lib/talia_core/agent.rb +14 -0
- data/lib/talia_core/background_jobs/job.rb +82 -0
- data/lib/talia_core/background_jobs/progress_job.rb +68 -0
- data/lib/talia_core/collection.rb +13 -0
- data/lib/talia_core/data_types/data_loader.rb +92 -0
- data/lib/talia_core/data_types/data_record.rb +105 -0
- data/lib/talia_core/data_types/delayed_copier.rb +76 -0
- data/lib/talia_core/data_types/file_record.rb +59 -0
- data/lib/talia_core/data_types/file_store.rb +306 -0
- data/lib/talia_core/data_types/iip_data.rb +153 -0
- data/lib/talia_core/data_types/iip_loader.rb +127 -0
- data/lib/talia_core/data_types/image_data.rb +32 -0
- data/lib/talia_core/data_types/media_link.rb +19 -0
- data/lib/talia_core/data_types/mime_mapping.rb +45 -0
- data/lib/talia_core/data_types/path_helpers.rb +77 -0
- data/lib/talia_core/data_types/pdf_data.rb +42 -0
- data/lib/talia_core/data_types/simple_text.rb +36 -0
- data/lib/talia_core/data_types/temp_file_handling.rb +85 -0
- data/lib/talia_core/data_types/xml_data.rb +169 -0
- data/lib/talia_core/dc_resource.rb +20 -0
- data/lib/talia_core/dummy_handler.rb +34 -0
- data/lib/talia_core/dummy_source.rb +20 -0
- data/lib/talia_core/errors.rb +25 -0
- data/lib/talia_core/initializer.rb +427 -0
- data/lib/talia_core/ordered_source.rb +228 -0
- data/lib/talia_core/rails_ext/actionpack/action_controller/record_identifier.rb +13 -0
- data/lib/talia_core/rails_ext/actionpack/action_controller.rb +1 -0
- data/lib/talia_core/rails_ext/actionpack.rb +1 -0
- data/lib/talia_core/rails_ext.rb +1 -0
- data/lib/talia_core/rdf_import.rb +90 -0
- data/lib/talia_core/rdf_resource.rb +159 -0
- data/lib/talia_core/semantic_collection_item.rb +93 -0
- data/lib/talia_core/semantic_collection_wrapper.rb +324 -0
- data/lib/talia_core/semantic_property.rb +7 -0
- data/lib/talia_core/semantic_relation.rb +67 -0
- data/lib/talia_core/source.rb +323 -0
- data/lib/talia_core/source_transfer_object.rb +38 -0
- data/lib/talia_core/workflow/base.rb +15 -0
- data/lib/talia_core/workflow/publication_workflow.rb +62 -0
- data/lib/talia_core/workflow.rb +300 -0
- data/lib/talia_core.rb +9 -0
- data/lib/talia_dependencies.rb +12 -0
- data/lib/talia_util/bar_progressor.rb +15 -0
- data/lib/talia_util/configuration/config_file.rb +48 -0
- data/lib/talia_util/configuration/database_config.rb +40 -0
- data/lib/talia_util/configuration/mysql_database_setup.rb +104 -0
- data/lib/talia_util/data_import.rb +91 -0
- data/lib/talia_util/image_conversions.rb +82 -0
- data/lib/talia_util/import_job_helper.rb +132 -0
- data/lib/talia_util/io_helper.rb +54 -0
- data/lib/talia_util/progressable.rb +38 -0
- data/lib/talia_util/progressbar.rb +236 -0
- data/lib/talia_util/rdf_update.rb +80 -0
- data/lib/talia_util/some_sigla.xml +1960 -0
- data/lib/talia_util/test_helpers.rb +151 -0
- data/lib/talia_util/util.rb +226 -0
- data/lib/talia_util/yaml_import.rb +80 -0
- data/lib/talia_util.rb +13 -0
- data/lib/user.rb +116 -0
- data/lib/version.rb +15 -0
- data/test/core_ext/string_test.rb +11 -0
- data/test/custom_template_test.rb +8 -0
- data/test/talia_core/active_source_predicate_test.rb +54 -0
- data/test/talia_core/active_source_rdf_test.rb +89 -0
- data/test/talia_core/active_source_test.rb +631 -0
- data/test/talia_core/data_types/data_loader_test.rb +123 -0
- data/test/talia_core/data_types/data_record_test.rb +40 -0
- data/test/talia_core/data_types/file_record_test.rb +171 -0
- data/test/talia_core/data_types/iip_data_test.rb +130 -0
- data/test/talia_core/data_types/image_data_test.rb +88 -0
- data/test/talia_core/data_types/pdf_data_test.rb +68 -0
- data/test/talia_core/data_types/xml_data_test.rb +134 -0
- data/test/talia_core/generic_xml_test.rb +83 -0
- data/test/talia_core/initializer_test.rb +36 -0
- data/test/talia_core/ordered_source_test.rb +398 -0
- data/test/talia_core/rdf_resource_test.rb +115 -0
- data/test/talia_core/semantic_collection_item_test.rb +129 -0
- data/test/talia_core/source_reader_test.rb +33 -0
- data/test/talia_core/source_test.rb +484 -0
- data/test/talia_core/source_transfer_object_test.rb +24 -0
- data/test/talia_core/workflow/publication_workflow_test.rb +242 -0
- data/test/talia_core/workflow/user_class_for_workflow.rb +35 -0
- data/test/talia_core/workflow/workflow_base_test.rb +21 -0
- data/test/talia_core/workflow_test.rb +19 -0
- data/test/talia_util/import_job_helper_test.rb +46 -0
- data/test/test_helper.rb +68 -0
- metadata +262 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
|
|
3
|
+
module TaliaCore
|
|
4
|
+
module DataTypes
|
|
5
|
+
module FileStore
|
|
6
|
+
|
|
7
|
+
# the handle for the file
|
|
8
|
+
@file_handle = nil
|
|
9
|
+
# position of the reading cursors
|
|
10
|
+
@position = 0
|
|
11
|
+
|
|
12
|
+
# Class for data paths
|
|
13
|
+
class DataPath < String ; end
|
|
14
|
+
|
|
15
|
+
module ClassMethods
|
|
16
|
+
|
|
17
|
+
# Find or create a record for the given location and source_id, then it saves the given file.
|
|
18
|
+
def find_or_create_and_assign_file(params)
|
|
19
|
+
data_record = self.find_or_create_by_location_and_source_id(extract_filename(params[:file]), params[:source_id])
|
|
20
|
+
data_record.file = params[:file]
|
|
21
|
+
data_record.save # force attachment save and it also saves type attribute.
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# This will create the data object from a given file. This will simply move the
|
|
27
|
+
# given file to the correct location upon save. This will avoid multiple
|
|
28
|
+
# read/write operations during import.
|
|
29
|
+
#
|
|
30
|
+
# The original file must not be touched by external processes until the
|
|
31
|
+
# record is saved.
|
|
32
|
+
#
|
|
33
|
+
# If the delete_original flag is set, the original file will be removed
|
|
34
|
+
# on save
|
|
35
|
+
def create_from_file(location, file_path, delete_original = false)
|
|
36
|
+
close_file
|
|
37
|
+
self.location = location
|
|
38
|
+
@file_data_to_write = DataPath.new(file_path)
|
|
39
|
+
@delete_original_file = delete_original
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Add data as string into file
|
|
43
|
+
def create_from_data(file_location, data, options = {})
|
|
44
|
+
# close file if opened
|
|
45
|
+
close_file
|
|
46
|
+
|
|
47
|
+
# Set the location for the record
|
|
48
|
+
self.location = file_location
|
|
49
|
+
|
|
50
|
+
if(data.respond_to?(:read))
|
|
51
|
+
@file_data_to_write = data.read
|
|
52
|
+
else
|
|
53
|
+
@file_data_to_write = data
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# returns the complete text
|
|
59
|
+
def all_text
|
|
60
|
+
if(!is_file_open?)
|
|
61
|
+
open_file
|
|
62
|
+
end
|
|
63
|
+
@file_handle.read(self.size)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# This is a placeholder in case file is used in a form.
|
|
67
|
+
def file() nil; end
|
|
68
|
+
|
|
69
|
+
# Assign the file data (<tt>StringIO</tt> or <tt>File</tt>).
|
|
70
|
+
def file=(file_data)
|
|
71
|
+
return nil if file_data.nil? || file_data.size == 0
|
|
72
|
+
self.assign_type file_data.content_type
|
|
73
|
+
self.location = file_data.original_filename
|
|
74
|
+
if file_data.is_a?(StringIO)
|
|
75
|
+
file_data.rewind
|
|
76
|
+
self.temp_data = file_data.read
|
|
77
|
+
else
|
|
78
|
+
self.temp_path = file_data.path
|
|
79
|
+
end
|
|
80
|
+
@save_attachment = true
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def write_file_after_save
|
|
84
|
+
# check if there are data to write
|
|
85
|
+
return unless(@file_data_to_write)
|
|
86
|
+
|
|
87
|
+
# check if file already exists
|
|
88
|
+
# raise(RuntimeError, "File already exists: #{file_path}") if(File.exists?(file_path))
|
|
89
|
+
|
|
90
|
+
begin
|
|
91
|
+
self.class.benchmark("\033[36m\033[1m\033[4mFileStore\033[0m Saving file for #{self.id}") do
|
|
92
|
+
# create data directory path
|
|
93
|
+
FileUtils.mkdir_p(data_directory)
|
|
94
|
+
|
|
95
|
+
if(@file_data_to_write.is_a?(DataPath))
|
|
96
|
+
copy_data_file
|
|
97
|
+
else
|
|
98
|
+
save_cached_data
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
@file_data_to_write = nil
|
|
102
|
+
end
|
|
103
|
+
rescue Exception => e
|
|
104
|
+
assit_fail("Exception on writing file #{self.location}: #{e}")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Return true if the specified data file is open, false otherwise
|
|
110
|
+
def is_file_open?
|
|
111
|
+
(@file_handle != nil)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# Assign the STI subclass, perfoming a mime-type lookup.
|
|
116
|
+
def assign_type(content_type)
|
|
117
|
+
self.type = self.class.class_type_from(content_type).name
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# private methods ==================================================================
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
# This saves the cached data from the file creation
|
|
125
|
+
def save_cached_data
|
|
126
|
+
# open file for writing
|
|
127
|
+
@file_handle = File.open(file_path, 'w')
|
|
128
|
+
|
|
129
|
+
# write data string into file
|
|
130
|
+
@file_handle << (@file_data_to_write.respond_to?(:read) ? @file_data_to_write.read : @file_data_to_write)
|
|
131
|
+
|
|
132
|
+
# close file
|
|
133
|
+
close_file
|
|
134
|
+
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# This copies the data file with which this object was created to the
|
|
138
|
+
# actual storage lcoation
|
|
139
|
+
def copy_data_file
|
|
140
|
+
copy_or_move(@file_data_to_write, file_path)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Open a specified file name and return a file handle.
|
|
144
|
+
# If the file is already opened, return the file handle
|
|
145
|
+
def open_file(file_name = file_path, options = 'rb')
|
|
146
|
+
# chek if the file name really exists, otherwise raise an exception
|
|
147
|
+
if !File.exists?(file_name)
|
|
148
|
+
raise(IOError, "File #{file_name} could not be opened.", caller)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# try to open the specified file if is not already open
|
|
152
|
+
if @file_handle == nil
|
|
153
|
+
@file_handle = File.open(file_name, options)
|
|
154
|
+
|
|
155
|
+
# check and set the initial position of the reading cursors.
|
|
156
|
+
# It's necessary to do this thing because we don't know if the user
|
|
157
|
+
# has specified the initial reading cursors befort starting working on file
|
|
158
|
+
@position ||= @file_handle.pos
|
|
159
|
+
|
|
160
|
+
@file_handle.pos = @position
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Close an already opened file
|
|
165
|
+
def close_file
|
|
166
|
+
if is_file_open?
|
|
167
|
+
@file_handle.close
|
|
168
|
+
|
|
169
|
+
# reset 'flags' variables and position
|
|
170
|
+
@file_handle = nil
|
|
171
|
+
@position = 0
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Read all bytes from a file
|
|
176
|
+
def read_all_bytes
|
|
177
|
+
# 1. Open file with option "r" (reading) and "b" (binary, useful for window system)
|
|
178
|
+
open_file
|
|
179
|
+
|
|
180
|
+
# 2. Read all bytes
|
|
181
|
+
begin
|
|
182
|
+
bytes = @file_handle.read(self.size).unpack("C*")
|
|
183
|
+
return bytes
|
|
184
|
+
rescue
|
|
185
|
+
# re-raise system the excepiton
|
|
186
|
+
raise
|
|
187
|
+
return nil
|
|
188
|
+
ensure
|
|
189
|
+
# 3. Close the file
|
|
190
|
+
close_file
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# return the next_byte
|
|
195
|
+
def next_byte(close)
|
|
196
|
+
if !is_file_open?
|
|
197
|
+
open_file
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
begin
|
|
201
|
+
current_byte = @file_handle.getc
|
|
202
|
+
|
|
203
|
+
if current_byte == nil or close
|
|
204
|
+
close_file
|
|
205
|
+
else
|
|
206
|
+
@position += 1
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
return current_byte
|
|
210
|
+
rescue
|
|
211
|
+
# re-raise system the excepiton
|
|
212
|
+
raise
|
|
213
|
+
close_file
|
|
214
|
+
return nil
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Copy or move the source file to the target. Working around all the
|
|
220
|
+
# things that suck in JRuby. This will honour two environment settings:
|
|
221
|
+
#
|
|
222
|
+
# * delay_file_copies - will not copy the files, but create a batch file
|
|
223
|
+
# so that the copy can be done later. Uses the DelayedCopier class.
|
|
224
|
+
# * fast_copies - will use the "normal" copy method from FileUtils that
|
|
225
|
+
# is faster. Since it crashed the system for us, the default is to
|
|
226
|
+
# use a "safe" workaround. The workaround is probably necessary for
|
|
227
|
+
# JRuby only.
|
|
228
|
+
def copy_or_move(original, target)
|
|
229
|
+
if(@delete_original_file)
|
|
230
|
+
FileUtils.move(original, target)
|
|
231
|
+
else
|
|
232
|
+
# Delay can be enabled through enviroment
|
|
233
|
+
if(delay_copies)
|
|
234
|
+
DelayedCopier.cp(original, target)
|
|
235
|
+
elsif(fast_copies)
|
|
236
|
+
FileUtils.copy(original, target)
|
|
237
|
+
else
|
|
238
|
+
# Call the copy as an external command. This is to work around the
|
|
239
|
+
# crashes that occurred using the builtin copy
|
|
240
|
+
from_file = File.expand_path(original)
|
|
241
|
+
to_file = File.expand_path(target)
|
|
242
|
+
system_success = system("cp '#{from_file}' '#{to_file}'")
|
|
243
|
+
raise(IOError, "copy error '#{from_file}' '#{to_file}'") unless system_success
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
# Returns true if the 'delayed write' is enabled in the environment
|
|
250
|
+
def delay_copies
|
|
251
|
+
ENV['delay_file_copies'] == 'true' || ENV['delay_file_copies'] == 'yes'
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Returns true if the 'fast copy' is enabled in the environment.
|
|
255
|
+
# Otherwise the class will use a workaround that is less likely to
|
|
256
|
+
# crash the whole system using JRuby.
|
|
257
|
+
def fast_copies
|
|
258
|
+
ENV['fast_copies'] == 'true' || ENV['fast_copies'] == 'yes'
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Return the data size
|
|
262
|
+
def data_size
|
|
263
|
+
File.size(file_path)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# set the position of the reading cursor
|
|
267
|
+
def set_position(position)
|
|
268
|
+
if (position != nil and position =~ /\A\d+\Z/)
|
|
269
|
+
if (position < size)
|
|
270
|
+
set_position(position)
|
|
271
|
+
else
|
|
272
|
+
raise(IOError, 'Position out of range', caller)
|
|
273
|
+
end
|
|
274
|
+
else
|
|
275
|
+
raise(IOError, 'Position not valid. It must be an integer')
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Check if the attachment should be saved.
|
|
280
|
+
def save_attachment?
|
|
281
|
+
@save_attachment
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Save the attachment, copying the file from the temp_path to the data_path.
|
|
285
|
+
def save_attachment
|
|
286
|
+
return unless save_attachment?
|
|
287
|
+
save_file
|
|
288
|
+
@save_attachment = false
|
|
289
|
+
true
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Destroy the attachment
|
|
293
|
+
def destroy_attachment
|
|
294
|
+
FileUtils.rm(full_filename) if File.exists?(full_filename)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Save the attachment on the data_path directory.
|
|
298
|
+
def save_file
|
|
299
|
+
FileUtils.mkdir_p(File.dirname(full_filename))
|
|
300
|
+
FileUtils.cp(temp_path, full_filename)
|
|
301
|
+
FileUtils.chmod(0644, full_filename)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
module TaliaCore
|
|
2
|
+
module DataTypes
|
|
3
|
+
|
|
4
|
+
# Class to manage IIP Image data type
|
|
5
|
+
class IipData < FileRecord
|
|
6
|
+
|
|
7
|
+
# Returns the IIP server configured for the application
|
|
8
|
+
def self.iip_server_uri
|
|
9
|
+
TaliaCore::CONFIG['iip_server_uri'] ||= 'http://localhost/fcgi-bin/iipsrv.fcgi'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# This is the mime type for the thumbnail - always tiff
|
|
13
|
+
def set_mime_type
|
|
14
|
+
self.mime = 'image/gif'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
alias :get_thumbnail :all_bytes
|
|
18
|
+
|
|
19
|
+
# Create from existing thumb and pyramid images
|
|
20
|
+
def create_from_existing(thumb, pyramid, delete_originals = false)
|
|
21
|
+
@file_data_to_write = [thumb, pyramid]
|
|
22
|
+
@delete_original_file = delete_originals
|
|
23
|
+
self.location = ''
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# return IIP Server Path
|
|
27
|
+
def iip_server_path
|
|
28
|
+
self.location
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def write_file_after_save
|
|
32
|
+
return unless(@file_data_to_write)
|
|
33
|
+
|
|
34
|
+
# Check if we have the images already given, in this case we prepare
|
|
35
|
+
# them and call the super method
|
|
36
|
+
return super if(direct_write!)
|
|
37
|
+
|
|
38
|
+
# create name for orginal temp file and destination temp file
|
|
39
|
+
original_file_path, orig_is_temp = prepare_original_file
|
|
40
|
+
will_delete_source = orig_is_temp || @delete_original_file
|
|
41
|
+
destination_thumbnail_file_path = File.join(Dir.tmpdir, "thumbnail_#{random_tempfile_filename}.gif")
|
|
42
|
+
|
|
43
|
+
begin # Begin the file creation operation
|
|
44
|
+
self.class.benchmark("\033[36mIipData\033[0m Making thumb and pyramid for #{self.id}", Logger::INFO) do
|
|
45
|
+
|
|
46
|
+
TaliaUtil::ImageConversions::create_thumb(original_file_path, destination_thumbnail_file_path)
|
|
47
|
+
create_pyramid(original_file_path)
|
|
48
|
+
|
|
49
|
+
# Run the super implementation for the thumbnail
|
|
50
|
+
# We will simply tell the system that we have to move the newly create
|
|
51
|
+
# thumb file
|
|
52
|
+
@file_data_to_write = DataPath.new(destination_thumbnail_file_path)
|
|
53
|
+
@delete_original_file = true
|
|
54
|
+
|
|
55
|
+
end # end benchmarking
|
|
56
|
+
super
|
|
57
|
+
|
|
58
|
+
ensure
|
|
59
|
+
# delete teƒmp files
|
|
60
|
+
File.delete original_file_path if(File.exists?(original_file_path) && will_delete_source)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# Checks if we have file paths given to directly copy thum and image file.
|
|
66
|
+
# Will always return true if such paths were given.
|
|
67
|
+
def direct_write!
|
|
68
|
+
return false unless(@file_data_to_write.kind_of?(Array))
|
|
69
|
+
|
|
70
|
+
thumb, pyramid = @file_data_to_write
|
|
71
|
+
self.class.benchmark("\033[36mIipData\033[0m Direct write for #{self.id}", Logger::INFO) do
|
|
72
|
+
prepare_for_pyramid
|
|
73
|
+
|
|
74
|
+
copy_or_move(pyramid, get_iip_root_file_path)
|
|
75
|
+
|
|
76
|
+
end # end benchmark
|
|
77
|
+
|
|
78
|
+
@file_data_to_write = DataPath.new(thumb)
|
|
79
|
+
|
|
80
|
+
true
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# This prepares the original file that needs to be converted. This will
|
|
84
|
+
# see if the data to be written is binary data or a file path. If this
|
|
85
|
+
# is binary data, it will create a temporary file on the disk.
|
|
86
|
+
#
|
|
87
|
+
# This returns an array with two elements: The name of the file to
|
|
88
|
+
# be used (a file system path) and a flag indicating if the file is
|
|
89
|
+
# a temporary file or not.
|
|
90
|
+
def prepare_original_file
|
|
91
|
+
if(@file_data_to_write.is_a?(DataPath))
|
|
92
|
+
[@file_data_to_write, false]
|
|
93
|
+
else
|
|
94
|
+
temp_file = File.join(Dir.tmpdir, "original_#{random_tempfile_filename}")
|
|
95
|
+
# write the original file
|
|
96
|
+
File.open(temp_file, 'w') do |original_file|
|
|
97
|
+
if(@file_data_to_write.respond_to?(:read))
|
|
98
|
+
original_file << @file_data_to_write.read
|
|
99
|
+
else
|
|
100
|
+
original_file << @file_data_to_write
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
[temp_file, true]
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Prepare for copying or creating the pyramid image
|
|
109
|
+
def prepare_for_pyramid
|
|
110
|
+
# set location
|
|
111
|
+
self.location = get_iip_root_file_path(true)
|
|
112
|
+
|
|
113
|
+
# create data directory path
|
|
114
|
+
FileUtils.mkdir_p(iip_root_directory)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Creates the pyramid image for IIP by running the configured system
|
|
118
|
+
# command. This automatically creates the file in the correct location
|
|
119
|
+
# (IIP root)
|
|
120
|
+
def create_pyramid(source)
|
|
121
|
+
# check if file already exists
|
|
122
|
+
raise(IOError, "File already exists: #{get_iip_root_file_path}") if(File.exists?(get_iip_root_file_path))
|
|
123
|
+
|
|
124
|
+
prepare_for_pyramid
|
|
125
|
+
|
|
126
|
+
TaliaUtil::ImageConversions::create_pyramid(source, get_iip_root_file_path)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Return the iip root directory for a specific iip image file
|
|
130
|
+
def iip_root_directory(relative = false)
|
|
131
|
+
if relative == false
|
|
132
|
+
File.join(TaliaCore::CONFIG["iip_root_directory_location"], ("00" + self.id.to_s)[-3..-1])
|
|
133
|
+
else
|
|
134
|
+
File.join(("00" + self.id.to_s)[-3..-1])
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Return the full file path related to the data directory
|
|
139
|
+
def get_iip_root_file_path(relative = false)
|
|
140
|
+
File.join(iip_root_directory(relative), self.id.to_s + '.tif')
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
private
|
|
144
|
+
|
|
145
|
+
# Generates a unique filename for a Tempfile.
|
|
146
|
+
def random_tempfile_filename
|
|
147
|
+
"#{rand 10E16}"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
module TaliaCore
|
|
2
|
+
module DataTypes
|
|
3
|
+
|
|
4
|
+
# Special module that contains the DataLoader methods to create IIP image data
|
|
5
|
+
module IipLoader
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Loads an image for the given file. This is a tad more complex than loading
|
|
9
|
+
# the data into a data record: It will create both an IIP data object and
|
|
10
|
+
# an Image data object.
|
|
11
|
+
def create_iip(mime_type, location, source, is_file)
|
|
12
|
+
iip_record = TaliaCore::DataTypes::IipData.new
|
|
13
|
+
image_record = TaliaCore::DataTypes::ImageData.new
|
|
14
|
+
records = [iip_record, image_record]
|
|
15
|
+
return records if(is_file && prepare_image_from_existing!(iip_record, image_record, source, location))
|
|
16
|
+
|
|
17
|
+
if(convert_original?(mime_type))
|
|
18
|
+
# We have an image that needs to be converted
|
|
19
|
+
open_original_image(source, is_file, mime_type) do |io|
|
|
20
|
+
create_from_stream(location, io, records)
|
|
21
|
+
image_record.location = orig_location(location)
|
|
22
|
+
end
|
|
23
|
+
else
|
|
24
|
+
if(is_file)
|
|
25
|
+
create_from_files(location, source, records)
|
|
26
|
+
else
|
|
27
|
+
create_from_stream(location, source, records)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
# IipRecord is always a (multi-leve) tiff
|
|
31
|
+
iip_record.mime = 'image/tiff'
|
|
32
|
+
|
|
33
|
+
records
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Rewrite the file location for original image files (to .png)
|
|
37
|
+
def orig_location(location)
|
|
38
|
+
File.basename(location, File.extname(location)) + '.png'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Indicates if the given mime type requires a conversion for the
|
|
42
|
+
# original image
|
|
43
|
+
def convert_original?(mime_type)
|
|
44
|
+
!([:jpeg, :png].include?(mime_type.to_sym))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Create the elements from a file
|
|
48
|
+
def create_from_files(location, file, records)
|
|
49
|
+
records.each { |rec| rec.create_from_file(location, file) }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Create the elements from a stream
|
|
53
|
+
def create_from_stream(location, io, records)
|
|
54
|
+
data = io.read
|
|
55
|
+
records.each { |rec| rec.create_from_data(location, data)}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Attempts to create an IipData object with pre-prepared images if possible
|
|
59
|
+
# Returns true if (and only if) the object has been created with existing
|
|
60
|
+
# data. Always fals if the data_record is not an IipData object or the
|
|
61
|
+
# :prepared_images option is not set.
|
|
62
|
+
def prepare_image_from_existing!(iip_record, image_record, url, location)
|
|
63
|
+
return false unless(iip_record.is_a?(TaliaCore::DataTypes::IipData) && image_record.is_a?(TaliaCore::DataTypes::ImageData))
|
|
64
|
+
return false unless((prep = ENV['prepared_images']) && prep.to_s.downcase != 'no' && prep.to_s.downcase != 'false')
|
|
65
|
+
|
|
66
|
+
file_ext = File.extname(url)
|
|
67
|
+
file_base = File.basename(url, file_ext)
|
|
68
|
+
|
|
69
|
+
thumb_file = File.join( ENV['prepared_images'], 'thumbs', "#{file_base}.gif")
|
|
70
|
+
pyramid_file = File.join( ENV['prepared_images'], 'pyramids', "#{file_base}.tif")
|
|
71
|
+
orig_file_pattern = File.join(ENV['prepared_images'], 'originals', "#{file_base}.*")
|
|
72
|
+
# We need to fix the pattern, also the Dir[] doesn't like unescaped brackets
|
|
73
|
+
orig_file_pattern.gsub!(/\[/, '\\[')
|
|
74
|
+
orig_file_pattern.gsub!(/\]/, '\\]')
|
|
75
|
+
orig_file_l = Dir[orig_file_pattern]
|
|
76
|
+
raise(ArgumentError, 'Original find not found for ' + url) unless(orig_file_l.size > 0)
|
|
77
|
+
orig_file = orig_file_l.first
|
|
78
|
+
assit_block { %w(.jpg .jpeg .png).include?(File.extname(orig_file).downcase) }
|
|
79
|
+
|
|
80
|
+
iip_record.create_from_existing(thumb_file, pyramid_file)
|
|
81
|
+
image_record.create_from_file(location, orig_file)
|
|
82
|
+
|
|
83
|
+
true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Little helper to decide how to open the original image
|
|
87
|
+
def open_original_image(thing, is_file, current_type, &block)
|
|
88
|
+
if(is_file)
|
|
89
|
+
open_original_image_file(thing, &block)
|
|
90
|
+
else
|
|
91
|
+
open_original_image_stream(thing, current_type, &block)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Same as open_original_image_file, but getting the data from a stream.
|
|
96
|
+
def open_original_image_stream(io, type, &block)
|
|
97
|
+
# First load this from the web to a temp file
|
|
98
|
+
tempfile = File.join(Dir.tmpdir, "talia_down_#{rand 10E16}.#{type.to_sym}")
|
|
99
|
+
begin
|
|
100
|
+
File.open(tempfile, 'w') do |fio|
|
|
101
|
+
fio << io.read # Download the file
|
|
102
|
+
end
|
|
103
|
+
assit(File.exist?(tempfile))
|
|
104
|
+
open_original_image_file(tempfile, &block)
|
|
105
|
+
ensure
|
|
106
|
+
FileUtils.rm(tempfile) if(File.exist?(tempfile))
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Opens the "original" image for the given file. This will convert the
|
|
111
|
+
# image to PNG image and the yield the io object for the PNG.
|
|
112
|
+
def open_original_image_file(filename)
|
|
113
|
+
converted_file = File.join(Dir.tmpdir, "talia_convert_#{rand 10E16}.png")
|
|
114
|
+
begin
|
|
115
|
+
TaliaUtil::ImageConversions.to_png(filename, converted_file)
|
|
116
|
+
File.open(converted_file) do |io|
|
|
117
|
+
yield(io)
|
|
118
|
+
end
|
|
119
|
+
ensure
|
|
120
|
+
FileUtils.rm(converted_file) if(File.exist?(converted_file))
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
end # Ending modules and such
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module TaliaCore
|
|
2
|
+
module DataTypes
|
|
3
|
+
|
|
4
|
+
# Class to manage image data type
|
|
5
|
+
class ImageData < FileRecord
|
|
6
|
+
|
|
7
|
+
# return the mime_type for a file
|
|
8
|
+
def extract_mime_type(location)
|
|
9
|
+
case File.extname(location).downcase
|
|
10
|
+
when '.bmp'
|
|
11
|
+
'image/bmp'
|
|
12
|
+
when '.cgm'
|
|
13
|
+
'image/cgm'
|
|
14
|
+
when '.fit', '.fits'
|
|
15
|
+
'image/fits'
|
|
16
|
+
when '.g3'
|
|
17
|
+
'image/g3fax'
|
|
18
|
+
when '.gif'
|
|
19
|
+
'image/gif'
|
|
20
|
+
when '.jpg', '.jpeg', '.jpe'
|
|
21
|
+
'image/jpeg'
|
|
22
|
+
when '.png'
|
|
23
|
+
'image/png'
|
|
24
|
+
when '.tif', '.tiff'
|
|
25
|
+
'image/tiff'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module TaliaCore
|
|
2
|
+
module DataTypes
|
|
3
|
+
|
|
4
|
+
# Data class that contains just a link to a remote data element (and no
|
|
5
|
+
# locally stored data at all). The uri for the link is stored in the location
|
|
6
|
+
# field
|
|
7
|
+
class MediaLink < DataRecord
|
|
8
|
+
|
|
9
|
+
def all_bytes
|
|
10
|
+
raise(RuntimeError, "Media Links have no data")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_byte(close_after_single_read=false)
|
|
14
|
+
raise(RuntimeRror, "Media Links have no data")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module TaliaCore
|
|
2
|
+
module DataTypes
|
|
3
|
+
|
|
4
|
+
# Mapping from Mime types to data classes and importing methods. Currently uses a fixed
|
|
5
|
+
# default mapping
|
|
6
|
+
module MimeMapping
|
|
7
|
+
|
|
8
|
+
def mapping_for(mime_type)
|
|
9
|
+
mime_type = Mime::Type.lookup(mime_type) if(mime_type.is_a?(String))
|
|
10
|
+
mapping = mapping_hash[mime_type.to_sym]
|
|
11
|
+
raise(ArgumentError, "No data class registered for type #{mime_type.inspect}") unless(mapping)
|
|
12
|
+
mapping
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Gets the data class for the given mime type
|
|
16
|
+
def class_type_from(mime_type)
|
|
17
|
+
mapping_for(mime_type)[:type]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def loader_type_from(mime_type)
|
|
21
|
+
map = mapping_for(mime_type)
|
|
22
|
+
map[:loader] || map[:type]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Currently this is only the default mapping
|
|
26
|
+
def mapping_hash
|
|
27
|
+
@mapping ||= {
|
|
28
|
+
:xml => { :type => XmlData },
|
|
29
|
+
:html =>{ :type => XmlData },
|
|
30
|
+
:tei => { :type => XmlData },
|
|
31
|
+
:hnml => { :type => XmlData },
|
|
32
|
+
:jpeg => { :type => ImageData, :loader => :create_iip },
|
|
33
|
+
:tiff => { :type => ImageData, :loader => :create_iip },
|
|
34
|
+
:png => { :type => ImageData, :loader => :create_iip },
|
|
35
|
+
:gif => { :type => ImageData, :loader => :create_iip },
|
|
36
|
+
:pdf => { :type => PdfData },
|
|
37
|
+
:text => { :type => SimpleText }
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
end
|