talia_core 0.5.4 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/VERSION.yml +2 -2
  2. data/config/talia_core.yml.example +37 -35
  3. data/generators/talia_admin/templates/app/models/fake_source.rb +93 -0
  4. data/generators/talia_admin/templates/app/models/talia_collection.rb +13 -37
  5. data/generators/talia_base/talia_base_generator.rb +0 -1
  6. data/generators/talia_base/templates/app/controllers/custom_templates_controller.rb +2 -1
  7. data/generators/talia_base/templates/app/controllers/sources_controller.rb +1 -1
  8. data/generators/talia_base/templates/script/configure_talia +56 -73
  9. data/generators/talia_swicky/talia_swicky_generator.rb +18 -0
  10. data/generators/talia_swicky/templates/app/controllers/swicky_notebooks_controller.rb +111 -0
  11. data/generators/talia_swicky/templates/app/helpers/swicky_notebooks_helper.rb +29 -0
  12. data/generators/talia_swicky/templates/app/views/swicky_notebooks/index.builder +6 -0
  13. data/generators/talia_swicky/templates/app/views/swicky_notebooks/index.html.erb +10 -0
  14. data/generators/talia_swicky/templates/app/views/swicky_notebooks/show.html.erb +11 -0
  15. data/generators/talia_swicky/templates/test/fixtures/notebook.rdf +862 -0
  16. data/generators/talia_swicky/templates/test/functional/swicky_notebooks_controller_test.rb +44 -0
  17. data/lib/core_ext/boolean.rb +23 -0
  18. data/lib/core_ext/jdbc_rake_monkeypatch.rb +22 -0
  19. data/lib/core_ext/nil_class.rb +11 -0
  20. data/lib/core_ext/object.rb +34 -0
  21. data/lib/core_ext/string.rb +15 -0
  22. data/lib/custom_template.rb +3 -1
  23. data/lib/loader_helper.rb +16 -3
  24. data/lib/mysql.rb +7 -7
  25. data/lib/progressbar.rb +2 -2
  26. data/lib/swicky/exhibit_json/item.rb +129 -0
  27. data/lib/swicky/exhibit_json/item_collection.rb +129 -0
  28. data/lib/swicky/fragment.rb +0 -0
  29. data/lib/swicky/note.rb +7 -0
  30. data/lib/swicky/notebook.rb +78 -12
  31. data/lib/talia_core/active_source.rb +45 -13
  32. data/lib/talia_core/active_source_parts/class_methods.rb +154 -26
  33. data/lib/talia_core/active_source_parts/finders.rb +49 -26
  34. data/lib/talia_core/active_source_parts/predicate_handler.rb +71 -23
  35. data/lib/talia_core/active_source_parts/rdf/ntriples_reader.rb +13 -0
  36. data/lib/talia_core/active_source_parts/rdf/rdf_reader.rb +99 -0
  37. data/lib/talia_core/active_source_parts/rdf/rdfxml_reader.rb +12 -0
  38. data/lib/talia_core/active_source_parts/{rdf.rb → rdf_handler.rb} +52 -19
  39. data/lib/talia_core/active_source_parts/xml/generic_reader.rb +151 -260
  40. data/lib/talia_core/active_source_parts/xml/generic_reader_add_statements.rb +97 -0
  41. data/lib/talia_core/active_source_parts/xml/generic_reader_helpers.rb +88 -0
  42. data/lib/talia_core/active_source_parts/xml/generic_reader_import_statements.rb +239 -0
  43. data/lib/talia_core/active_source_parts/xml/rdf_builder.rb +14 -7
  44. data/lib/talia_core/active_source_parts/xml/source_builder.rb +7 -3
  45. data/lib/talia_core/active_source_parts/xml/source_reader.rb +17 -2
  46. data/lib/talia_core/collection.rb +192 -1
  47. data/lib/talia_core/data_types/data_loader.rb +88 -18
  48. data/lib/talia_core/data_types/data_record.rb +24 -2
  49. data/lib/talia_core/data_types/delayed_copier.rb +13 -3
  50. data/lib/talia_core/data_types/file_record.rb +24 -13
  51. data/lib/talia_core/data_types/file_store.rb +111 -94
  52. data/lib/talia_core/data_types/iip_data.rb +104 -23
  53. data/lib/talia_core/data_types/iip_loader.rb +102 -56
  54. data/lib/talia_core/data_types/image_data.rb +3 -1
  55. data/lib/talia_core/data_types/media_link.rb +4 -1
  56. data/lib/talia_core/data_types/mime_mapping.rb +65 -38
  57. data/lib/talia_core/data_types/path_helpers.rb +23 -17
  58. data/lib/talia_core/data_types/pdf_data.rb +9 -6
  59. data/lib/talia_core/data_types/simple_text.rb +5 -4
  60. data/lib/talia_core/data_types/xml_data.rb +53 -25
  61. data/lib/talia_core/dummy_handler.rb +3 -2
  62. data/lib/talia_core/errors.rb +13 -27
  63. data/lib/talia_core/initializer.rb +44 -4
  64. data/lib/talia_core/oai/active_source_model.rb +13 -6
  65. data/lib/talia_core/oai/active_source_oai_adapter.rb +13 -12
  66. data/lib/talia_core/rdf_import.rb +1 -1
  67. data/lib/talia_core/rdf_resource.rb +2 -1
  68. data/lib/talia_core/semantic_collection_wrapper.rb +143 -151
  69. data/lib/talia_core/semantic_property.rb +4 -0
  70. data/lib/talia_core/semantic_relation.rb +84 -33
  71. data/lib/talia_core/source.rb +45 -25
  72. data/lib/talia_core/source_fragment.rb +7 -0
  73. data/lib/talia_core/source_transfer_object.rb +3 -1
  74. data/lib/talia_core/source_types/agent.rb +16 -0
  75. data/lib/talia_core/source_types/dc_resource.rb +3 -3
  76. data/lib/talia_core/source_types/marcont_resource.rb +15 -0
  77. data/lib/talia_core/source_types/skos_concept.rb +17 -0
  78. data/lib/talia_dependencies.rb +1 -1
  79. data/lib/talia_util.rb +1 -1
  80. data/lib/talia_util/bar_progressor.rb +1 -1
  81. data/lib/talia_util/image_conversions.rb +8 -2
  82. data/lib/talia_util/import_job_helper.rb +40 -3
  83. data/lib/talia_util/io_helper.rb +15 -4
  84. data/lib/talia_util/progressable.rb +50 -1
  85. data/lib/talia_util/rake_tasks.rb +3 -21
  86. data/lib/talia_util/test_helpers.rb +6 -1
  87. data/lib/talia_util/util.rb +108 -27
  88. data/lib/talia_util/xml/base_builder.rb +28 -1
  89. data/lib/talia_util/xml/rdf_builder.rb +81 -5
  90. data/lib/tasks/talia_core_tasks.rake +2 -0
  91. data/test/core_ext/boolean_test.rb +26 -0
  92. data/test/core_ext/nil_class_test.rb +14 -0
  93. data/test/core_ext/object_test.rb +26 -0
  94. data/test/core_ext/string_test.rb +11 -0
  95. data/test/swicky/json_encoder_test.rb +51 -42
  96. data/test/swicky/notebook_test.rb +13 -6
  97. data/test/talia_core/active_source_finder_interface_test.rb +30 -0
  98. data/test/talia_core/active_source_test.rb +445 -34
  99. data/test/talia_core/collection_test.rb +332 -0
  100. data/test/talia_core/data_types/file_record_test.rb +2 -23
  101. data/test/talia_core/ntriples_reader_test.rb +49 -0
  102. data/test/talia_core/rdfxml_reader_test.rb +51 -0
  103. data/test/talia_core/source_test.rb +12 -0
  104. data/test/talia_util/import_job_helper_test.rb +19 -12
  105. metadata +190 -90
  106. data/config/database.yml +0 -19
  107. data/config/rdfstore.yml +0 -13
  108. data/config/talia_core.yml +0 -24
  109. data/generators/talia_base/templates/migrations/bj_migration.rb +0 -10
  110. data/lib/JXslt/jxslt.rb +0 -60
  111. data/lib/swicky/json_encoder.rb +0 -179
  112. data/lib/talia_core/agent.rb +0 -14
  113. data/lib/talia_core/background_jobs/job.rb +0 -82
  114. data/lib/talia_core/background_jobs/progress_job.rb +0 -68
  115. data/lib/talia_core/data_types/temp_file_handling.rb +0 -85
  116. data/lib/talia_core/ordered_source.rb +0 -228
  117. data/lib/talia_core/semantic_collection_item.rb +0 -94
  118. data/lib/talia_core/source_types/collection.rb +0 -15
  119. data/lib/talia_util/progressbar.rb +0 -236
  120. data/tasks/talia_core_tasks.rake +0 -2
  121. data/test/talia_core/ordered_source_test.rb +0 -394
  122. data/test/talia_core/semantic_collection_item_test.rb +0 -125
@@ -21,7 +21,15 @@ module TaliaCore
21
21
  end
22
22
  end
23
23
 
24
+ # This writes the "cp" command to the output script. It will also
25
+ # add a "mkdir" command to create the directory for the target file,
26
+ # if necessary.
27
+ #
28
+ # At the moment,
29
+ # this will always use UNIX-style "cp" and "mkdir" commands.
24
30
  def self.cp(source, target)
31
+ # We use the << in-place string concenation, 'cause if there
32
+ # are a lot of files, it really makes a speed difference
25
33
  unless(dir_seen?(File.expand_path(target)))
26
34
  mkdir_string = 'mkdir -vp "'
27
35
  mkdir_string << File.dirname(File.expand_path(target))
@@ -47,7 +55,8 @@ module TaliaCore
47
55
 
48
56
  private
49
57
 
50
-
58
+ # Returns true if the directory has already been seen by
59
+ # the copier before.
51
60
  def self.dir_seen?(directory)
52
61
  @seen_dirs = {}
53
62
  return true if(@seen_dirs[directory])
@@ -55,12 +64,13 @@ module TaliaCore
55
64
  false
56
65
  end
57
66
 
58
- # The file name for the delayed copy
67
+ # The file name for the delayed copy (the file where the
68
+ # commands are written out)
59
69
  def self.delay_file_name
60
70
  File.join(RAILS_ROOT, 'delayed_copy.sh')
61
71
  end
62
72
 
63
- # Backs up an existing file if necessary
73
+ # Backs up an existing file with delayed commands, if necessary
64
74
  def self.backup_file
65
75
  round = 1
66
76
  file_name = 'nil'
@@ -1,54 +1,65 @@
1
1
  module TaliaCore
2
2
  module DataTypes
3
3
 
4
- # Base class for all data records that use a plain file for data storage
4
+ # Base class for all data records that use a plain file for data storage. This
5
+ # implements the DataRecord API so that all byte methods work on a file in the
6
+ # File system.
7
+ #
8
+ # Most of the operations are defined in the FileStore module, see there on how
9
+ # to create and work with file records.
10
+ #
11
+ # See the DataLoader and MimeMapping modules to see new file records are
12
+ # created automatically, depending on the MIME type.
13
+ #
14
+ # The data paths are set automatically by the class, see PathHelpers
15
+ #
16
+ # There is also an IipLoader module, that contains the loader mechanism for
17
+ # creating Iip images - you can also use that as an example to create
18
+ # new loaders for other file types.
5
19
  class FileRecord < DataRecord
6
20
  include FileStore
7
- extend FileStore::ClassMethods
8
21
 
9
22
  include PathHelpers
10
23
  extend PathHelpers::ClassMethods
11
24
 
12
- include TempFileHandling
13
- extend TempFileHandling::ClassMethods
14
-
15
25
  include DataLoader
16
26
  extend DataLoader::ClassMethods
17
27
  extend IipLoader
18
28
  extend TaliaUtil::IoHelper # Data IO for class methods
19
29
 
20
- after_save :save_attachment, :write_file_after_save
30
+ after_save :write_file_after_save
21
31
 
22
- before_destroy :destroy_attachment
32
+ before_destroy :destroy_file
23
33
 
24
34
  # Returns and, if necessary, creates the file for "delayed" copy operations
25
35
 
26
- # returns all bytes in the object as an array
36
+ # Return all bytes from the file as a byte array.
27
37
  def all_bytes
28
38
  read_all_bytes
29
39
  end
30
40
 
31
- # returns the next byte from the object, or nil at EOS
41
+ # Returns the next byte from the file (at the position of the
42
+ # read cursor), or EOS if the end of the file has been reached.
32
43
  def get_byte(close_after_single_read=false)
33
44
  next_byte(close_after_single_read)
34
45
  end
35
46
 
36
- # returns the current position of the read cursor (binary access)
47
+ # Returns the current position of the read cursor
37
48
  def position
38
49
  return (@position != nil) ? @position : 0
39
50
  end
40
51
 
41
- # reset the cursor to the initial state
52
+ # Reset the cursor to the beginning of the file
42
53
  def reset
43
54
  set_position(0)
44
55
  end
45
56
 
46
- # set the new position of the reding cursors
57
+ # Set a new position for the read cursor
47
58
  def seek(new_position)
48
59
  set_position(new_position)
49
60
  end
50
61
 
51
- # returns the size of the object in bytes
62
+ # Returns the file size in bytes
52
63
  def size
53
64
  data_size
54
65
  end
@@ -2,36 +2,72 @@ require 'fileutils'
2
2
 
3
3
  module TaliaCore
4
4
  module DataTypes
5
+
6
+ # The "hevy lifting" for FileRecords, handling the actual creation and
7
+ # manipulation of the files.
8
+ #
9
+ # A record can be created with create_from_file (passing a filename) or
10
+ # with create_from_data, passing a byte array.
11
+ #
12
+ # While a "location" can be passed to the record (which will be save
13
+ # in the database), the actual file name will be determined by the system.
14
+ # Also see the PathHelpers for info
15
+ #
16
+ # The base directory for file storage can be configured in the
17
+ # talia_core.yml file as "data_directory_location". Otherwise,
18
+ # RAILS_ROOT/data. Inside, each record will be stored at
19
+ # "<data class of the record>/<last three numbers of the id>/<record id>"
20
+ #
21
+ # So, for example, a "XmlData" record with the id "123456" would be stored
22
+ # as "DATA_ROOT/XmlData/456/123456"
23
+ #
24
+ # = Creating new records
25
+ #
26
+ # When creating a new record using create_from_data, the data will be cached
27
+ # inside the object and only be saved when the record itself is saved. In
28
+ # this case, the file data is simply written to file during the save operation.
29
+ #
30
+ # When the record is created using create_from_file, the behaviour depens on
31
+ # the parameters passed and the system settings.
32
+ #
33
+ # * In case the delete_original flag is set, the system will try to move the
34
+ # file to the new location. If both are on the same file system, this will
35
+ # be quicker than a copy operation.
36
+ # * Currently, the move operation uses the "mv" command. This is does not work
37
+ # on windows, but is a workaround to stability problems with the file handling
38
+ # in JRuby
39
+ # * If the delete_original flag is not set, the system will attempt to copy the
40
+ # files:
41
+ # * If the "delay_file_copies" options is set in the _environment_, no copy
42
+ # operation will be done. Instead, the system will create a "delayed_copies.sh"
43
+ # script that can be executed from a UNIX shell to do the actual copying.
44
+ # This is extremely fast and stable, as no actual copying is done.
45
+ # * Otherwise Talia will attempt to copy the file by itself. If the "fast_copies"
46
+ # flag is set in _environment_, it will use the internal copy routine
47
+ # which will work on any system. Otherwise, it will call the system's "cp"
48
+ # command, which can sometimes be more stable with jruby.
49
+ #
50
+ # Also see the DataLoader module to see how the creation of records automatically
51
+ # selects the record type and loader, depending on the MIME type of the data.
52
+ #
53
+ # *Note*: The above behaviour means that for files that are treated through "copy"
54
+ # or "move", the original file must not be touched by external processes until
55
+ # the record is saved
5
56
  module FileStore
6
57
 
7
- # the handle for the file
58
+ # The file handle of the current record
8
59
  @file_handle = nil
9
- # position of the reading cursors
60
+ # Read cursor within the current record
10
61
  @position = 0
11
62
 
12
- # Class for data paths
63
+ # Class used to represent data paths in file records. This type is used for
64
+ # strings that contain a path to a file, to distinguish them from "normal"
65
+ # string, which may contain plain data.
13
66
  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
67
 
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
68
+ # Creates a new record from an existing file on the file system.
69
+ # The file will be copied or moved when the new record is saved - see
70
+ # the module documentation to see how this works in detail.
35
71
  def create_from_file(location, file_path, delete_original = false)
36
72
  close_file
37
73
  self.location = location
@@ -39,13 +75,15 @@ module TaliaCore
39
75
  @delete_original_file = delete_original
40
76
  end
41
77
 
42
- # Add data as string into file
43
- def create_from_data(file_location, data, options = {})
78
+ # Creates a new record from the given data (binary array). See
79
+ # the module documentation for the details; the data will be cached
80
+ # and written to disk once the new record is saved.
81
+ def create_from_data(location, data, options = {})
44
82
  # close file if opened
45
83
  close_file
46
84
 
47
85
  # Set the location for the record
48
- self.location = file_location
86
+ self.location = location
49
87
 
50
88
  if(data.respond_to?(:read))
51
89
  @file_data_to_write = data.read
@@ -55,38 +93,21 @@ module TaliaCore
55
93
 
56
94
  end
57
95
 
58
- # returns the complete text
96
+ # Returns the contents of the file as a text string.
59
97
  def all_text
60
98
  if(!is_file_open?)
61
99
  open_file
62
100
  end
63
101
  @file_handle.read(self.size)
64
102
  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
103
 
104
+ # Callback for writing the data from create_from_data or create_from_file. If there is
105
+ # a problem saving this file, only an internal assertion is thrown so that it won't crash
106
+ # production environments.
83
107
  def write_file_after_save
84
108
  # check if there are data to write
85
109
  return unless(@file_data_to_write)
86
110
 
87
- # check if file already exists
88
- # raise(RuntimeError, "File already exists: #{file_path}") if(File.exists?(file_path))
89
-
90
111
  begin
91
112
  self.class.benchmark("\033[36m\033[1m\033[4mFileStore\033[0m Saving file for #{self.id}") do
92
113
  # create data directory path
@@ -106,7 +127,7 @@ module TaliaCore
106
127
 
107
128
  end
108
129
 
109
- # Return true if the specified data file is open, false otherwise
130
+ # Return true if the data file is open
110
131
  def is_file_open?
111
132
  (@file_handle != nil)
112
133
  end
@@ -117,11 +138,10 @@ module TaliaCore
117
138
  self.type = MimeMapping.class_type_from(content_type).name
118
139
  end
119
140
 
120
-
121
- # private methods ==================================================================
122
141
  private
123
142
 
124
- # This saves the cached data from the file creation
143
+ # This saves the cached data from create_from_data. Simply writes the
144
+ # data to disk.
125
145
  def save_cached_data
126
146
  # open file for writing
127
147
  @file_handle = File.open(file_path, 'w')
@@ -135,7 +155,7 @@ module TaliaCore
135
155
  end
136
156
 
137
157
  # This copies the data file with which this object was created to the
138
- # actual storage lcoation
158
+ # actual storage lcoation. This is for records created with create_from_file
139
159
  def copy_data_file
140
160
  copy_or_move(@file_data_to_write, file_path)
141
161
  end
@@ -172,7 +192,7 @@ module TaliaCore
172
192
  end
173
193
  end
174
194
 
175
- # Read all bytes from a file
195
+ # Read all bytes from the file
176
196
  def read_all_bytes
177
197
  # 1. Open file with option "r" (reading) and "b" (binary, useful for window system)
178
198
  open_file
@@ -191,7 +211,7 @@ module TaliaCore
191
211
  end
192
212
  end
193
213
 
194
- # return the next_byte
214
+ # Gets the next byte of the file
195
215
  def next_byte(close)
196
216
  if !is_file_open?
197
217
  open_file
@@ -216,7 +236,9 @@ module TaliaCore
216
236
 
217
237
  end
218
238
 
219
- # Copy or move the source file to the target. Working around all the
239
+ # Copy or move the source file to the target depending on the
240
+ # <tt>delete_original</tt> setting in #create_from_file.
241
+ # Working around all the
220
242
  # things that suck in JRuby. This will honour two environment settings:
221
243
  #
222
244
  # * delay_file_copies - will not copy the files, but create a batch file
@@ -227,35 +249,50 @@ module TaliaCore
227
249
  # JRuby only.
228
250
  def copy_or_move(original, target)
229
251
  if(@delete_original_file)
230
- FileUtils.move(original, target)
252
+ begin
253
+ FileUtils.move(original, target)
254
+ rescue Errno::EACCES
255
+ # Workaround for File.rename bug with JRuby (jira.codehaus.org/browse/JRUBY-3381),
256
+ # based on the code from Lenny Marks 03/Jun/10.
257
+ safe_copy original, target
258
+ FileUtils.rm original
259
+ end
231
260
  else
232
261
  # Delay can be enabled through enviroment
233
262
  if(delay_copies)
234
263
  DelayedCopier.cp(original, target)
235
- elsif(fast_copies)
236
- FileUtils.copy(original, target)
237
264
  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
265
+ safe_copy original, target
244
266
  end
245
267
  end
246
268
  end
269
+
270
+ # Copies the file using some workarounds for jruby if necessary.
271
+ # See also #copy_or_move.
272
+ def safe_copy(original, target)
273
+ if(fast_copies)
274
+ FileUtils.copy(original, target)
275
+ else
276
+ # Call the copy as an external command. This is to work around the
277
+ # crashes that occurred using the builtin copy
278
+ from_file = File.expand_path(original)
279
+ to_file = File.expand_path(target)
280
+ system_success = system("cp '#{from_file}' '#{to_file}'")
281
+ raise(IOError, "copy error '#{from_file}' '#{to_file}'") unless system_success
282
+ end
283
+ end
284
+
247
285
 
248
-
249
- # Returns true if the 'delayed write' is enabled in the environment
286
+ # Returns true if the 'delay_file_copies' option is set in the environment
250
287
  def delay_copies
251
- ENV['delay_file_copies'] == 'true' || ENV['delay_file_copies'] == 'yes'
288
+ ENV['delay_file_copies'].yes?
252
289
  end
253
290
 
254
- # Returns true if the 'fast copy' is enabled in the environment.
291
+ # Returns true if the 'fast_copies' option is enabled in the environment.
255
292
  # Otherwise the class will use a workaround that is less likely to
256
293
  # crash the whole system using JRuby.
257
294
  def fast_copies
258
- ENV['fast_copies'] == 'true' || ENV['fast_copies'] == 'yes'
295
+ ENV['fast_copies'].yes?
259
296
  end
260
297
 
261
298
  # Return the data size
@@ -263,7 +300,7 @@ module TaliaCore
263
300
  File.size(file_path)
264
301
  end
265
302
 
266
- # set the position of the reading cursor
303
+ # Sets the position of the reading cursor
267
304
  def set_position(position)
268
305
  if (position != nil and position =~ /\A\d+\Z/)
269
306
  if (position < size)
@@ -275,32 +312,12 @@ module TaliaCore
275
312
  raise(IOError, 'Position not valid. It must be an integer')
276
313
  end
277
314
  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
315
+
316
+ # Delete the file connected to this record
317
+ def destroy_file
294
318
  FileUtils.rm(full_filename) if File.exists?(full_filename)
295
319
  end
296
320
 
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
321
  end
305
322
  end
306
- end
323
+ end
@@ -1,87 +1,162 @@
1
1
  module TaliaCore
2
2
  module DataTypes
3
3
 
4
- # Class to manage IIP Image data type. FIXME: Check if this correctly destroys existing files.
4
+ # Class to manage IIP Image data type. This differs from the "normal"
5
+ # file record in various ways:
6
+ #
7
+ # * The record itself only contains a thumbnail version of the image as
8
+ # data (#get_thumbnail will return the file data for the thumbnail)
9
+ # * The location of the record contains the path to the image on the
10
+ # IIP server. This is also returned by #iip_server_path
11
+ # * The records should be created using the IipLoader - see DataLoader
12
+ # to find out how to configure Talia to do that
13
+ #
14
+ # = What is IIP?
15
+ #
16
+ # IIP is a protocol to use "pyramidal images" - which are images which
17
+ # contain a tiled version of the original image in various resolutions.
18
+ # It is used to serve high-resolution images in a way that a client can
19
+ # request only the portion of the image that is currently needed.
20
+ #
21
+ # = How does IIP work with Talia?
22
+ #
23
+ # To use IIP, an IIP server is needed.
24
+ # The IIP client will connect to the server and request an image from using
25
+ # HTTP requests.
26
+ #
27
+ # The IIP server is a separate piece of sofware, which is not part of
28
+ # Talia. The one commonly used is http://iipimage.sourceforge.net/ which
29
+ # runs as a fastcgi module in Apache (or another web server).
30
+ #
31
+ # For talia this means:
32
+ #
33
+ # * The pyramidal images have to be put in a place where the IIP server can
34
+ # access them - they do *not* go in the normal data directory.
35
+ # * The location of the directory for the pyramidal images can be configured
36
+ # as <tt>iip_root_directory_location</tt> in the <tt>talia_core.yml</tt>
37
+ # file.
38
+ # * The <tt>location</tt> field of each IipData record contains the _relative_
39
+ # path for accessing the file on the IIP server.
40
+ # * The URI of the server itself can be set in <tt>talia_core.yml</tt> as
41
+ # <tt>iip_server_uri</tt>
42
+ #
43
+ # = How can the IIP images be shown in Talia?
44
+ #
45
+ # There are several clients or "viewers" for IIP images available, and one
46
+ # is also included in the "muruca_widgets" gem that can be installed in
47
+ # addition to talia.
48
+ #
49
+ # Generally you'll have to include the client in your view templates, and
50
+ # use the #iip_server_uri and #iip_server_path to point it to the image
51
+ # on the server.
52
+ #
53
+ # Obviously you'll also need a running IIP server.
54
+ #
55
+ # = What are the requirements for IIP?
56
+ #
57
+ # Apart from the IIP server itself, you'll need some software to actually
58
+ # create the pyramidal images - see the TaliaUtil::ImageConversions class for details.
5
59
  class IipData < FileRecord
6
60
 
7
- # Returns the IIP server configured for the application
61
+ # Returns the IIP server configured for the application. This is usually the
62
+ # value configured as the <tt>iip_server_uri</tt> in the <tt>talia_core.yml</tt>
8
63
  def self.iip_server_uri
9
64
  TaliaCore::CONFIG['iip_server_uri'] ||= 'http://localhost/fcgi-bin/iipsrv.fcgi'
10
65
  end
11
66
 
12
- # This is the mime type for the thumbnail - always tiff
67
+ # This is the mime type for the thumbnail. Always gif.
13
68
  def set_mime_type
14
69
  self.mime = 'image/gif'
15
70
  end
16
71
 
17
72
  alias :get_thumbnail :all_bytes
18
73
 
19
- # Create from existing thumb and pyramid images
74
+ # Create a new record from an existing thumb and pyramidal image.
75
+ # This is used to make a record from pre-existing "prepared" images
76
+ # and does not do any image conversion.
20
77
  def create_from_existing(thumb, pyramid, delete_originals = false)
21
78
  @file_data_to_write = [thumb, pyramid]
22
79
  @delete_original_file = delete_originals
23
80
  self.location = ''
24
81
  end
25
82
 
26
- # return IIP Server Path
83
+ # The relative path to the image on the IIP server
27
84
  def iip_server_path
28
85
  self.location
29
86
  end
30
87
 
88
+ # Callback to write the data when the record is saved, which is more involved than
89
+ # the same method on the superclass.
90
+ #
91
+ # * If the thumb and pyramid file are already available, they are used directly
92
+ # with #direct_write!
93
+ # * Otherwise it will take the original file and pass it through the TaliaUtil::ImageConversions
94
+ # to create the thumbnail and pyramidal image.
95
+ # * Any temporary files created in the process are cleaned up afterwards
31
96
  def write_file_after_save
32
97
  return unless(@file_data_to_write)
33
98
 
34
- # Check if we have the images already given, in this case we prepare
35
- # them and call the super method
99
+ # Check if we have the converted images already. In this case we write
100
+ # them to the appropriate directories directly and call the super method
36
101
  return super if(direct_write!)
37
102
 
38
- # create name for orginal temp file and destination temp file
103
+ # "Prepare" the original file for conversion, indicate if a temp file is being used
39
104
  original_file_path, orig_is_temp = prepare_original_file
105
+ # Check if we need to delete the original/temp file
40
106
  will_delete_source = orig_is_temp || @delete_original_file
107
+ # Path to the temporary thumbnail file
41
108
  destination_thumbnail_file_path = File.join(Dir.tmpdir, "thumbnail_#{random_tempfile_filename}.gif")
42
109
 
43
- begin # Begin the file creation operation
110
+ begin
44
111
  self.class.benchmark("\033[36mIipData\033[0m Making thumb and pyramid for #{self.id}", Logger::INFO) do
45
112
 
113
+ # Create the thumbnail at the temporary location
46
114
  TaliaUtil::ImageConversions::create_thumb(original_file_path, destination_thumbnail_file_path)
115
+ # Create the pyramidal image from the original
47
116
  create_pyramid(original_file_path)
48
117
 
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
118
+ # Run the super implementation for the thumbnail, by using the temporary thumb file as the "data"
52
119
  @file_data_to_write = DataPath.new(destination_thumbnail_file_path)
120
+ # The temp thumb file needs to be deleted by the superclass
53
121
  @delete_original_file = true
54
122
 
55
123
  end # end benchmarking
56
124
  super
57
125
 
58
126
  ensure
59
- # delete teƒmp files
127
+ # Delete the temporary "original" file, if necessary
60
128
  File.delete original_file_path if(File.exists?(original_file_path) && will_delete_source)
61
129
  end
62
130
  end
63
131
 
64
132
 
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.
133
+ # Checks if there if the thumb and pyramidal file already exist and can simply be moved
134
+ # to the correct location.
135
+ #
136
+ # If yes, the method will move or copy the files to the correct locations, and return true
137
+ # Otherwise, the method will do nothing and return false.
67
138
  def direct_write!
139
+ # If we have an array of data files, we can assume that these are
140
+ # pre-prepared thumb and pyramid images
68
141
  return false unless(@file_data_to_write.kind_of?(Array))
69
142
 
70
143
  thumb, pyramid = @file_data_to_write
71
144
  self.class.benchmark("\033[36mIipData\033[0m Direct write for #{self.id}", Logger::INFO) do
72
- prepare_for_pyramid
73
-
145
+ prepare_for_pyramid # Setup the record for the new image
146
+ # Copy or move the pyramid file to the correct location
74
147
  copy_or_move(pyramid, get_iip_root_file_path)
75
148
 
76
149
  end # end benchmark
77
-
150
+
151
+ # Set the thumb file as the data file for the current FileRecord (which
152
+ # is automatically handled by the superclass)
78
153
  @file_data_to_write = DataPath.new(thumb)
79
154
 
80
155
  true
81
156
  end
82
157
 
83
158
  # 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
159
+ # see if the data to be written is binary data or a file path. If it
85
160
  # is binary data, it will create a temporary file on the disk.
86
161
  #
87
162
  # This returns an array with two elements: The name of the file to
@@ -105,7 +180,8 @@ module TaliaCore
105
180
  end
106
181
  end
107
182
 
108
- # Prepare for copying or creating the pyramid image
183
+ # Prepare for copying or creating the pyramid image. Sets the <tt>location</tt>
184
+ # of the record and creates the directory to store the IIP images
109
185
  def prepare_for_pyramid
110
186
  # set location
111
187
  self.location = get_iip_root_file_path(true)
@@ -116,7 +192,7 @@ module TaliaCore
116
192
 
117
193
  # Creates the pyramid image for IIP by running the configured system
118
194
  # command. This automatically creates the file in the correct location
119
- # (IIP root)
195
+ # (IIP root). The conversion is done using the TaliaUtil::ImageConversions
120
196
  def create_pyramid(source)
121
197
  # check if file already exists
122
198
  raise(IOError, "File already exists: #{get_iip_root_file_path}") if(File.exists?(get_iip_root_file_path))
@@ -126,8 +202,13 @@ module TaliaCore
126
202
  TaliaUtil::ImageConversions::create_pyramid(source, get_iip_root_file_path)
127
203
  end
128
204
 
129
- # Return the iip root directory for a specific iip image file
205
+ # Return the iip root directory for a specific iip image file. This is the
206
+ # directory where the pyramid file will be stored on disk.
207
+ #
208
+ # If the <tt>relative</tt> flag is set, it will return a relative path.
130
209
  def iip_root_directory(relative = false)
210
+ # TODO: The relative paths are (also?) used for web access, is it o.k. to
211
+ # use File.join ?
131
212
  if relative == false
132
213
  File.join(TaliaCore::CONFIG["iip_root_directory_location"], ("00" + self.id.to_s)[-3..-1])
133
214
  else
@@ -135,7 +216,7 @@ module TaliaCore
135
216
  end
136
217
  end
137
218
 
138
- # Return the full file path related to the data directory
219
+ # Return the full file path for the IIP image. See #iip_root_directory
139
220
  def get_iip_root_file_path(relative = false)
140
221
  File.join(iip_root_directory(relative), self.id.to_s + '.tif')
141
222
  end