talia_core 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|