talia_core 0.4.3 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/README.rdoc +6 -27
  2. data/VERSION.yml +3 -2
  3. data/config/database.yml +11 -11
  4. data/config/talia_core.yml +11 -6
  5. data/config/talia_core.yml.example +11 -6
  6. data/generators/talia_base/talia_base_generator.rb +3 -0
  7. data/generators/talia_base/templates/README +1 -1
  8. data/generators/talia_base/templates/app/controllers/source_data_controller.rb +1 -1
  9. data/generators/talia_base/templates/app/controllers/sources_controller.rb +31 -12
  10. data/generators/talia_base/templates/app/helpers/sources_helper.rb +77 -1
  11. data/generators/talia_base/templates/app/views/layouts/sources.html.erb +22 -0
  12. data/generators/talia_base/templates/app/views/sources/_data_list.html.erb +17 -0
  13. data/generators/talia_base/templates/app/views/sources/_property_item.html.erb +10 -0
  14. data/generators/talia_base/templates/app/views/sources/_property_list.html.erb +13 -0
  15. data/generators/talia_base/templates/app/views/sources/index.html.erb +16 -11
  16. data/generators/talia_base/templates/app/views/sources/semantic_templates/default/default.html.erb +8 -19
  17. data/generators/talia_base/templates/config/routes.rb +11 -0
  18. data/generators/talia_base/templates/migrations/create_semantic_relations.rb +2 -1
  19. data/generators/talia_base/templates/public/images/core/arrow.png +0 -0
  20. data/generators/talia_base/templates/public/images/core/building.png +0 -0
  21. data/generators/talia_base/templates/public/images/core/contents_top_left.gif +0 -0
  22. data/generators/talia_base/templates/public/images/core/document-horizontal-text.png +0 -0
  23. data/generators/talia_base/templates/public/images/core/document.png +0 -0
  24. data/generators/talia_base/templates/public/images/core/gear.png +0 -0
  25. data/generators/talia_base/templates/public/images/core/group.png +0 -0
  26. data/generators/talia_base/templates/public/images/core/header_bg.gif +0 -0
  27. data/generators/talia_base/templates/public/images/core/image.png +0 -0
  28. data/generators/talia_base/templates/public/images/core/imagebig.png +0 -0
  29. data/generators/talia_base/templates/public/images/core/left_edge.gif +0 -0
  30. data/generators/talia_base/templates/public/images/core/letter.png +0 -0
  31. data/generators/talia_base/templates/public/images/core/line.png +0 -0
  32. data/generators/talia_base/templates/public/images/core/logo.gif +0 -0
  33. data/generators/talia_base/templates/public/images/core/map.png +0 -0
  34. data/generators/talia_base/templates/public/images/core/period.png +0 -0
  35. data/generators/talia_base/templates/public/images/core/person.png +0 -0
  36. data/generators/talia_base/templates/public/images/core/person_default.png +0 -0
  37. data/generators/talia_base/templates/public/images/core/place.png +0 -0
  38. data/generators/talia_base/templates/public/images/core/source.png +0 -0
  39. data/generators/talia_base/templates/public/images/core/television.png +0 -0
  40. data/generators/talia_base/templates/public/images/core/text.png +0 -0
  41. data/generators/talia_base/templates/public/images/core/type.png +0 -0
  42. data/generators/talia_base/templates/public/images/core/video.png +0 -0
  43. data/generators/talia_base/templates/public/stylesheets/img/arrow.png +0 -0
  44. data/generators/talia_base/templates/public/stylesheets/main.css +276 -0
  45. data/generators/talia_base/templates/script/configure_talia +1 -1
  46. data/generators/talia_base/templates/script/setup_talia_backend +2 -0
  47. data/lib/core_ext/platform.rb +1 -0
  48. data/lib/core_ext/string.rb +6 -0
  49. data/lib/talia_core/active_source.rb +62 -3
  50. data/lib/talia_core/active_source_parts/class_methods.rb +36 -122
  51. data/lib/talia_core/active_source_parts/finders.rb +158 -0
  52. data/lib/talia_core/active_source_parts/predicate_handler.rb +7 -8
  53. data/lib/talia_core/active_source_parts/xml/generic_reader.rb +95 -11
  54. data/lib/talia_core/active_source_parts/xml/rdf_builder.rb +6 -13
  55. data/lib/talia_core/active_source_parts/xml/source_reader.rb +8 -3
  56. data/lib/talia_core/data_types/data_loader.rb +14 -6
  57. data/lib/talia_core/data_types/data_record.rb +5 -1
  58. data/lib/talia_core/data_types/iip_data.rb +1 -1
  59. data/lib/talia_core/data_types/mime_mapping.rb +8 -3
  60. data/lib/talia_core/errors.rb +4 -0
  61. data/lib/talia_core/initializer.rb +1 -8
  62. data/lib/talia_core/property_string.rb +58 -0
  63. data/lib/talia_core/semantic_collection_item.rb +3 -2
  64. data/lib/talia_core/semantic_collection_wrapper.rb +236 -198
  65. data/lib/talia_core/source.rb +130 -178
  66. data/lib/talia_core/source_types/collection.rb +15 -0
  67. data/lib/talia_core/source_types/dc_resource.rb +22 -0
  68. data/lib/talia_core/source_types/dummy_source.rb +22 -0
  69. data/lib/talia_core.rb +0 -1
  70. data/lib/talia_util/import_job_helper.rb +44 -16
  71. data/lib/talia_util/io_helper.rb +21 -1
  72. data/lib/talia_util/rake_tasks.rb +48 -72
  73. data/lib/talia_util/rdf_update.rb +22 -13
  74. data/lib/talia_util/test_helpers.rb +1 -1
  75. data/lib/talia_util.rb +0 -2
  76. data/test/core_ext/string_test.rb +5 -0
  77. data/test/talia_core/active_source_test.rb +151 -14
  78. data/test/talia_core/generic_xml_test.rb +46 -2
  79. data/test/talia_core/initializer_test.rb +0 -1
  80. data/test/talia_core/property_string_test.rb +78 -0
  81. data/test/talia_core/source_reader_test.rb +5 -1
  82. data/test/talia_core/source_test.rb +23 -32
  83. data/test/talia_util/import_job_helper_test.rb +1 -1
  84. data/test/talia_util/io_helper_test.rb +44 -0
  85. metadata +399 -373
  86. data/generators/talia_base/templates/app/views/sources/semantic_templates/default/province.html.erb +0 -19
  87. data/lib/acts_as_roled.rb +0 -11
  88. data/lib/talia_core/collection.rb +0 -13
  89. data/lib/talia_core/dc_resource.rb +0 -20
  90. data/lib/talia_core/dummy_source.rb +0 -20
  91. data/lib/talia_core/rails_ext/actionpack/action_controller/record_identifier.rb +0 -13
  92. data/lib/talia_core/rails_ext/actionpack/action_controller.rb +0 -1
  93. data/lib/talia_core/rails_ext/actionpack.rb +0 -1
  94. data/lib/talia_core/rails_ext.rb +0 -1
  95. data/lib/talia_util/data_import.rb +0 -91
  96. data/lib/talia_util/yaml_import.rb +0 -80
@@ -0,0 +1,158 @@
1
+ module TaliaCore
2
+ module ActiveSourceParts
3
+
4
+ # All class methods that are used for finding sources.
5
+ module Finders
6
+
7
+
8
+ # Finder also accepts uris as "ids". There are also some additional options
9
+ # that are accepted:
10
+ #
11
+ # [*:find_through*] accepts and array with an predicate name and an object
12
+ # value/uri, to search for predicates that match the given predicate/value
13
+ # combination
14
+ # [*:type*] specifically looks for sources with the given type.
15
+ # [*:find_through_inv*] like :find_through, but for the "inverse" lookup
16
+ # [*:prefetch_relations*] if set to "true", this will pre-load all semantic
17
+ # relations for the sources (experimental, not fully implemented yet)
18
+ def find(*args)
19
+ prefetching = false
20
+ if(args.last.is_a?(Hash))
21
+ options = args.last
22
+ options.to_options!
23
+ prefetching = options.delete(:prefetch_relations)
24
+ if(options.empty?) # If empty we remove the args hash, so that the 1-param uri search works
25
+ args.pop
26
+ else
27
+ prepare_options!(options)
28
+ end
29
+ end
30
+
31
+ result = if(args.size == 1 && (uri_s = uri_string_for(args[0])))
32
+ src = super(:first, :conditions => { :uri => uri_s })
33
+ raise(ActiveRecord::RecordNotFound, "Not found: #{uri_s}") unless(src)
34
+ src
35
+ else
36
+ super
37
+ end
38
+
39
+ prefetch_relations_for(result) if(prefetching)
40
+
41
+ result
42
+ end
43
+
44
+ # Modify the count helper so that it can use the advanced options of the
45
+ # ActiveSource #find routine
46
+ def count(*args)
47
+ if((options = args.last).is_a?(Hash))
48
+ options.to_options!
49
+ options.delete(:prefetch_relations) # This is not relevant for counting
50
+ prepare_options!(options)
51
+ end
52
+ super
53
+ end
54
+
55
+ # Find a list of sources which contains the given token inside the local name.
56
+ # This means that the namespace it will be excluded.
57
+ #
58
+ # Sources in system:
59
+ # * http://talia.org/one
60
+ # * http://talia.org/two
61
+ #
62
+ # Source.find_by_uri_token('a') # => [ ]
63
+ # Source.find_by_uri_token('o') # => [ 'http://talia.org/one', 'http://talia.org/two' ]
64
+ #
65
+ # NOTE: It internally use a MySQL function, as sql condition, to find the local name of the uri.
66
+ def find_by_uri_token(token, options = {})
67
+ find(:all, {
68
+ :conditions => [ "LOWER(SUBSTRING_INDEX(uri, '/', -1)) LIKE ?", '%' + token.downcase + '%' ],
69
+ :order => "uri ASC" }.merge!(options))
70
+ end
71
+
72
+ # Find the Sources within the given namespace by a partial local name
73
+ def find_by_partial_local(namespace, local_part, options = {})
74
+ namesp = N::URI[namespace]
75
+ return [] unless(namesp)
76
+ find(:all, {
77
+ :conditions => [ 'uri LIKE ?', "#{namesp.uri}#{local_part}%" ],
78
+ :order => "uri ASC"}.merge!(options))
79
+ end
80
+
81
+ # Find the fist Source that matches the given URI.
82
+ # It's useful for admin pane, because users visit:
83
+ # /admin/sources/<source_id>/edit
84
+ # but that information is not enough, since we store
85
+ # into the database the whole reference as URI:
86
+ # http://localnode.org/av_media_sources/source_id
87
+ def find_by_partial_uri(id, options = {})
88
+ find(:all, { :conditions => ["uri LIKE ?", '%' + id + '%'] }.merge!(options))
89
+ end
90
+
91
+ private
92
+
93
+ # Checks if the :find_through option is set. If so, this expects the
94
+ # option to have 2 values: The first representing the URL of the predicate
95
+ # and the second the URL or value that should be matched.
96
+ #
97
+ # An optional third parameter can be used to force an object search on the
98
+ # semantic_properties table (instead of active_sources) - if not present
99
+ # this will be auto-guessed from the "object value", checking if it appears
100
+ # to be an URL or not.
101
+ #
102
+ # ...find(:find_through => [N::RDF::something, 'value', true]
103
+ def check_for_find_through!(options)
104
+ if(f_through = options.delete(:find_through))
105
+ assit_kind_of(Array, f_through)
106
+ raise(ArgumentError, "Passed non-hash conditions with :find_through") if(options.has_key?(:conditions) && !options[:conditions].is_a?(Hash))
107
+ raise(ArgumentError, "Cannot pass custom join conditions with :find_through") if(options.has_key?(:joins))
108
+ predicate = f_through[0]
109
+ obj_val = f_through[1]
110
+ search_prop = (f_through.size > 2) ? f_through[2] : !(obj_val.to_s =~ /:/)
111
+ options[:joins] = default_joins(!search_prop, search_prop)
112
+ options[:conditions] ||= {}
113
+ options[:conditions]['semantic_relations.predicate_uri'] = predicate.to_s
114
+ if(search_prop)
115
+ options[:conditions]['obj_props.value'] = obj_val.to_s
116
+ else
117
+ options[:conditions]['obj_sources.uri'] = obj_val.to_s
118
+ end
119
+ end
120
+ end
121
+
122
+ # Check for the :find_through_inv option. This expects the 2 basic values
123
+ # in the same way as :find_through.
124
+ #
125
+ # find(:find_through_inv => [N::RDF::to_me, my_uri]
126
+ def check_for_find_through_inv!(options)
127
+ if(f_through = options.delete(:find_through_inv))
128
+ assit_kind_of(Array, f_through)
129
+ raise(ArgumentError, "Passed non-hash conditions with :find_through") if(options.has_key?(:conditions) && !options[:conditions].is_a?(Hash))
130
+ raise(ArgumentError, "Cannot pass custom join conditions with :find_through") if(options.has_key?(:joins))
131
+ options[:joins] = default_inv_joins
132
+ options[:conditions] ||= {}
133
+ options[:conditions]['semantic_relations.predicate_uri'] = f_through[0].to_s
134
+ options[:conditions]['sub_sources.uri'] = f_through[1].to_s
135
+ end
136
+ end
137
+
138
+
139
+ # Checks for the :type option in the find options. This is the same as
140
+ # doing a :find_through on the rdf type
141
+ def check_for_type_find!(options)
142
+ if(f_type = options.delete(:type))
143
+ options[:find_through] = [N::RDF::type, f_type.to_s, false]
144
+ check_for_find_through!(options)
145
+ end
146
+ end
147
+
148
+ # Takes the "advanced" options that can be passed to the find method and
149
+ # converts them into "standard" find options.
150
+ def prepare_options!(options)
151
+ check_for_find_through!(options)
152
+ check_for_type_find!(options)
153
+ check_for_find_through_inv!(options)
154
+ end
155
+
156
+ end
157
+ end
158
+ end
@@ -17,7 +17,7 @@ module TaliaCore
17
17
  raise(RangeError, "Too many sources for prefetching.") if(sources.size > limit)
18
18
  src_hash = {}
19
19
  sources.each { |src| src_hash[src.id] = src }
20
- conditions = ['subject_id in (?)', src_hash.keys.join(', ')]
20
+ conditions = { :subject_id => src_hash.keys }
21
21
  joins = ActiveSource.sources_join
22
22
  joins << ActiveSource.props_join
23
23
  relations = SemanticRelation.find(:all, :conditions => conditions,
@@ -51,20 +51,19 @@ module TaliaCore
51
51
  # so that the object will always be the same as long as the parent source
52
52
  # lives.
53
53
  def get_objects_on(predicate)
54
- TaliaCore::logger.warn "GETTING OBJECTS (#{predicate}) #{@type_cache.inspect}"
55
54
  @type_cache ||= {}
56
55
  active_wrapper = @type_cache[predicate.to_s]
57
56
 
58
-
59
57
  if(active_wrapper.nil?)
60
- # If this is a prefetched source we have everything - no use looking further
61
- # TODO: Returns an Array instead of a wrapper
62
- return [] if(@prefetched)
63
-
64
58
  active_wrapper = SemanticCollectionWrapper.new(self, predicate)
59
+
60
+ # If this is a prefetched source we have everything, so we can
61
+ # initialize the wrapper without loading anything
62
+ active_wrapper.init_as_empty! if(@prefetched)
63
+
65
64
  @type_cache[predicate.to_s] = active_wrapper
66
65
  end
67
-
66
+
68
67
  active_wrapper
69
68
  end
70
69
 
@@ -1,4 +1,5 @@
1
1
  require 'hpricot'
2
+ require 'pathname'
2
3
 
3
4
  module TaliaCore
4
5
  module ActiveSourceParts
@@ -16,6 +17,7 @@ module TaliaCore
16
17
  class GenericReader
17
18
 
18
19
  extend TaliaUtil::IoHelper
20
+ include TaliaUtil::IoHelper
19
21
  include TaliaUtil::Progressable
20
22
 
21
23
  # Helper class for state
@@ -28,11 +30,14 @@ module TaliaCore
28
30
  # See the IoHelper class for help on the options. A progressor may
29
31
  # be supplied on which the importer will report it's progress.
30
32
  def sources_from_url(url, options = nil, progressor = nil)
31
- open_generic(url, options) { |io| sources_from(io, progressor) }
33
+ open_generic(url, options) { |io| sources_from(io, progressor, url) }
32
34
  end
33
35
 
34
- def sources_from(source, progressor = nil)
36
+ # Reader the sources from the given IO stream. You may specify a base
37
+ # url to help the reader to decide from where files should be opened.
38
+ def sources_from(source, progressor = nil, base_url=nil)
35
39
  reader = self.new(source)
40
+ reader.base_file_url = base_url if(base_url)
36
41
  reader.progressor = progressor
37
42
  reader.sources
38
43
  end
@@ -91,6 +96,17 @@ module TaliaCore
91
96
  end
92
97
  @sources.values
93
98
  end
99
+
100
+ # This is the "base" for resolving file URLs. If a file URL is found
101
+ # to be relative, it will be relative to this URL
102
+ def base_file_url
103
+ @base_file_url ||= TALIA_ROOT
104
+ end
105
+
106
+ # Assign a new base url
107
+ def base_file_url=(new_base_url)
108
+ @base_file_url = base_for(new_base_url)
109
+ end
94
110
 
95
111
  def add_source_with_check(source_attribs)
96
112
  assit_kind_of(Hash, source_attribs)
@@ -103,10 +119,10 @@ module TaliaCore
103
119
  @sources[uri].each do |key, value|
104
120
  next unless(new_value = source_attribs.delete(key))
105
121
 
106
- assit(!((key.to_sym == :type) && (value != 'TaliaCore::DummySource') && (value != new_value)), "Type should not change during import, may be a format problem. (From #{value} to #{new_value})")
122
+ assit(!((key.to_sym == :type) && (value != 'TaliaCore::SourceTypes::DummySource') && (value != new_value)), "Type should not change during import, may be a format problem. (From #{value} to #{new_value})")
107
123
  if(new_value.is_a?(Array) && value.is_a?(Array))
108
124
  # If both are Array-types, the new elements will be appended
109
- # and duplicates nwill be removed
125
+ # and duplicates will be removed
110
126
  @sources[uri][key] = (value + new_value).uniq
111
127
  else
112
128
  # Otherwise just replace
@@ -184,18 +200,27 @@ module TaliaCore
184
200
 
185
201
  # Adds a value for the given predicate (may also be a database field)
186
202
  def add(predicate, object, required = false)
203
+ # We need to check if the object elements are already strings -
204
+ # otherwise we would *.to_s the PropertyString objects, which would
205
+ # destroy the metadata in them.
187
206
  if(object.kind_of?(Array))
188
- object.each { |obj| set_element(predicate, obj.to_s, required) }
207
+ object.each { |obj| set_element(predicate, obj.is_a?(String) ? obj : obj.to_s, required) }
189
208
  else
190
- set_element(predicate, object.to_s, required)
209
+ set_element(predicate, object.is_a?(String) ? object : object.to_s, required)
191
210
  end
192
211
  end
193
212
 
213
+ # Adds a value with the given prediate and language/type information
214
+ def add_i18n(predicate, object, lang, type=nil)
215
+ object = object.blank? ? nil : TaliaCore::PropertyString.new(object, lang, type)
216
+ add(predicate, object)
217
+ end
218
+
194
219
  # Adds a date field. This will attempt to parse the original string
195
220
  # and write the result as an ISO 8061 compliant date string. Note
196
221
  # that this won't be able to parse everything you throw at it, though.
197
222
  def add_date(predicate, date, required = false, fmt = nil)
198
- add(predicate, parse_date(date, fmt), required)
223
+ add(predicate, to_iso8601(parse_date(date, fmt)), required)
199
224
  end
200
225
 
201
226
  # Adds a date interval as an ISO 8061 compliant date string. See
@@ -208,7 +233,7 @@ module TaliaCore
208
233
  elsif(end_date.blank?)
209
234
  add_date(predicate, end_date, true, fmt)
210
235
  else
211
- add(predicate, "#{parse_date(start_date, fmt)}/#{parse_date(end_date, fmt)}", required)
236
+ add(predicate, "#{to_iso8601(parse_date(start_date, fmt))}/#{to_iso8601(parse_date(end_date, fmt))}", required)
212
237
  end
213
238
  end
214
239
 
@@ -221,7 +246,7 @@ module TaliaCore
221
246
  end
222
247
  if(object.kind_of?(Array))
223
248
  object.each do |obj|
224
- raise(ArgumentError, "Cannot add relation on database field") if(ActiveSource.db_attr?(predicate))
249
+ raise(ArgumentError, "Cannot add relation on database field <#{predicate}> - <#{object.inspect}>") if(ActiveSource.db_attr?(predicate))
225
250
  set_element(predicate, "<#{irify(obj)}>", required)
226
251
  end
227
252
  else
@@ -230,14 +255,63 @@ module TaliaCore
230
255
  end
231
256
  end
232
257
 
233
- # Add a file to the source being imported
258
+ # Add a file to the source being imported. See the DataLoader module for a description of
259
+ # the possible options
234
260
  def add_file(urls, options = {})
235
261
  return if(urls.blank?)
236
262
  urls = [ urls ] unless(urls.is_a?(Array))
237
- files = urls.collect { |url| { :url => url.to_s, :options => options } }
263
+ files = urls.collect { |url| { :url => get_absolute_file_url(url), :options => options } }
238
264
  @current.attributes[:files] = files if(files.size > 0)
239
265
  end
240
266
 
267
+ # Gets an absolute path to the given file url, using the base_file_url
268
+ def get_absolute_file_url(url)
269
+ orig_url = url.to_s.strip
270
+
271
+ url = file_url(orig_url)
272
+ # If a file:// was stripped from the url, this means it will always point
273
+ # to a file
274
+ force_file = (orig_url != url)
275
+ # Indicates wether the base url is a network url or a file/directory
276
+ base_is_net = !base_file_url.is_a?(String)
277
+ # Try to find if we have a "net" URL if we aren't sure if this is a file. In
278
+ # case the base url is a network url, we'll always assume that the
279
+ # url is also a net thing. Otherwise we only have a net url if it contains a
280
+ # '://' string
281
+ is_net_url = !force_file && (base_is_net || url.include?('://'))
282
+ # The url is absolute if there is a : character to be found
283
+
284
+
285
+ if(is_net_url)
286
+ base_is_net ? join_url(base_file_url, url) : url
287
+ else
288
+ base_is_net ? url : join_files(base_file_url, url)
289
+ end
290
+ end
291
+
292
+ # Joins the two files. If the path is an absolute path,
293
+ # the base_dir is ignored
294
+ def join_files(base_dir, path)
295
+ if(Pathname.new(path).relative?)
296
+ File.join(base_dir, path)
297
+ else
298
+ path
299
+ end
300
+ end
301
+
302
+ # Joins the two url parts. If the path is an absolute URL,
303
+ # the base_url is ignored.
304
+ def join_url(base_url, path)
305
+ return path if(path.include?(':')) # Absolute URL contains ':'
306
+ if(path[0..0] == '/')
307
+ new_url = base_url.clone
308
+ new_url.path = path
309
+ new_url.to_s
310
+ else
311
+ (base_file_url + path).to_s
312
+ end
313
+ end
314
+
241
315
  # Returns true if the given source was already imported. This can return false
242
316
  # if you call this for the currently importing source.
243
317
  def source_exists?(uri)
@@ -337,7 +411,10 @@ module TaliaCore
337
411
 
338
412
  # Get the content of exactly one child element of type "elem" of the
339
413
  # currently importing element.
414
+ #
415
+ # If elem is set to :self, this will give the content of the current element
340
416
  def from_element(elem)
417
+ return @current.element.inner_text.strip if(elem == :self)
341
418
  elements = all_elements(elem)
342
419
  elements = elements.uniq if(elements.size > 1) # Try to ignore dupes
343
420
  raise(ArgumentError, "More than one element of #{elem} in #{@current.element.inspect}") if(elements.size > 1)
@@ -352,6 +429,13 @@ module TaliaCore
352
429
  result
353
430
  end
354
431
 
432
+ # Get the iso8601 string for the date
433
+ def to_iso8601(date)
434
+ return nil unless(date)
435
+ date = DateTime.parse(date) unless(date.respond_to?(:strftime))
436
+ date.strftime('%Y-%m-%dT%H:%M:%SZ')
437
+ end
438
+
355
439
  # Parses the given string and returns it as a date object
356
440
  def parse_date(date, fmt = nil)
357
441
  return nil if(date.blank?)
@@ -66,22 +66,15 @@ module TaliaCore
66
66
  # Splits up the value, extracting encoded language codes and RDF data types. The
67
67
  # result will be returned as a hash, with the "true" value being "value"
68
68
  def extract_values(value)
69
+ prop_string = PropertyString.parse(value)
69
70
  result = {}
70
- # First split for the type
71
- type_split = value.split('^^')
72
- # Check if any of the elements contains a language string
73
- type_split = type_split.collect { |element| extract_lang(element, result) }
74
- result['rdf:datatype'] = type_split.last if(type_split.size > 1)
75
- result['value'] = (type_split.first || '')
71
+ result['value'] = prop_string
72
+ result['rdf:datatype'] = prop_string.type if(prop_string.type)
73
+ result['xml:lang'] = prop_string.lang if(prop_string.lang)
74
+
76
75
  result
77
76
  end
78
-
79
- # Helper to extract a language string. The lang value, if any, will be added to the hash
80
- def extract_lang(value, hash)
81
- lang_split = value.split('@')
82
- hash['xml:lang'] = lang_split.last if(lang_split.size > 1)
83
- lang_split.first || ''
84
- end
77
+
85
78
  end
86
79
  end
87
80
  end
@@ -6,9 +6,14 @@ module TaliaCore
6
6
  class SourceReader < GenericReader
7
7
 
8
8
  element :source do
9
- nested :attribute do
10
- add from_element(:predicate), all_elements(:value)
11
- add_rel from_element(:predicate), all_elements(:object)
9
+ nested :attribute do
10
+ predicate = from_element(:predicate)
11
+ # We need to treat each value separately, as the can have 'xml:lang'
12
+ # attributes
13
+ nested :value do
14
+ add_i18n predicate, from_element(:self), from_attribute('xml:lang')
15
+ end
16
+ add_rel predicate, all_elements(:object)
12
17
  end
13
18
  add_file all_elements(:file)
14
19
  end
@@ -26,13 +26,14 @@ module TaliaCore
26
26
  # will *always* attempt to determine the mime type through the location parameter, unless
27
27
  # an explicit mime type is given.
28
28
  def create_from_url(uri, options = {})
29
- mime_type = options[:mime_type] || options['mime_type']
30
- location = options[:location] || options['location']
29
+ options.to_options!
30
+ options.assert_valid_keys(:mime_type, :location, :http_credentials)
31
+
32
+ mime_type = options[:mime_type]
33
+ location = options[:location]
31
34
  # If a Mime type is given, use that.
32
35
  if(mime_type)
33
36
  mime_type = Mime::Type.lookup(mime_type) if(mime_type.is_a?(String))
34
- elsif(location)
35
- mime_type = Mime::Type.lookup_by_extension(File.extname(location)[1..-1])
36
37
  end
37
38
 
38
39
  data_records = []
@@ -49,13 +50,15 @@ module TaliaCore
49
50
  # If we have a "standard" uri, we cut off at the last slash (the
50
51
  # File.basename would use the system file separator)
51
52
  location ||= uri.rindex('/') ? uri[(uri.rindex('/') + 1)..-1] : uri
52
-
53
+
53
54
  if(is_file)
54
- mime_type ||= Mime::Type.lookup_by_extension(File.extname(location)[1..-1])
55
+ mime_type ||= mime_by_location(location)
55
56
  open_and_create(mime_type, location, uri, true)
56
57
  else
57
58
  open_from_url(uri, options[:http_credentials]) do |io|
58
59
  mime_type ||= Mime::Type.lookup(io.content_type)
60
+ # Just in case we didn't get any content type
61
+ mime_type ||= mime_by_location(location)
59
62
  open_and_create(mime_type, location, io, false)
60
63
  end
61
64
  end
@@ -63,6 +66,11 @@ module TaliaCore
63
66
  end
64
67
 
65
68
  private
69
+
70
+ # Get the mime type from the location
71
+ def mime_by_location(location)
72
+ Mime::Type.lookup_by_extension(File.extname(location)[1..-1].downcase)
73
+ end
66
74
 
67
75
  # The main loader. This will handle the lookup from the mapping and the creating of the
68
76
  # data objects. Depending on the setting of is_file, the source parameter will be interpreted
@@ -11,6 +11,8 @@ module TaliaCore
11
11
  belongs_to :source, :class_name => 'TaliaCore::ActiveSource'
12
12
  before_create :set_mime_type # Mime type must be saved before the record is written
13
13
 
14
+ # validates_presence_of :source
15
+
14
16
  extend MimeMapping
15
17
 
16
18
  # Declaration of main abstract methods ======================
@@ -74,7 +76,9 @@ module TaliaCore
74
76
  #
75
77
  # source_data = source_data_type.classify.constantize.find_by_location(location, :limit => 1)
76
78
  #
77
- source_data = self.find(:first, :conditions => ["type = ? AND location = ?", source_data_type.camelize, location])
79
+ data_type = "TaliaCore::DataTypes::#{source_data_type.camelize}"
80
+ source_data = self.find(:first, :conditions => ["type = ? AND location = ?", data_type, location])
81
+
78
82
  raise ActiveRecord::RecordNotFound if source_data.nil?
79
83
  source_data
80
84
  end
@@ -1,7 +1,7 @@
1
1
  module TaliaCore
2
2
  module DataTypes
3
3
 
4
- # Class to manage IIP Image data type
4
+ # Class to manage IIP Image data type. FIXME: Check if this correctly destroys existing files.
5
5
  class IipData < FileRecord
6
6
 
7
7
  # Returns the IIP server configured for the application
@@ -2,12 +2,15 @@ module TaliaCore
2
2
  module DataTypes
3
3
 
4
4
  # Mapping from Mime types to data classes and importing methods. Currently uses a fixed
5
- # default mapping
5
+ # default mapping. If the mime type is not known, it will use a fallback default handler.
6
6
  module MimeMapping
7
7
 
8
8
  def mapping_for(mime_type)
9
9
  mime_type = Mime::Type.lookup(mime_type) if(mime_type.is_a?(String))
10
10
  mapping = mapping_hash[mime_type.to_sym]
11
+ TaliaCore.logger.warn { "No data class registered for mime type #{mime_type.inspect}, trying default handler." } unless(mapping)
12
+ mapping ||= mapping_hash[:default]
13
+
11
14
  raise(ArgumentError, "No data class registered for type #{mime_type.inspect}") unless(mapping)
12
15
  mapping
13
16
  end
@@ -22,7 +25,7 @@ module TaliaCore
22
25
  map[:loader] || map[:type]
23
26
  end
24
27
 
25
- # Currently this is only the default mapping
28
+ # Currently there is only the default mapping
26
29
  def mapping_hash
27
30
  @mapping ||= {
28
31
  :xml => { :type => XmlData },
@@ -38,7 +41,9 @@ module TaliaCore
38
41
  :png => { :type => ImageData, :loader => :create_iip },
39
42
  :gif => { :type => ImageData, :loader => :create_iip },
40
43
  :pdf => { :type => PdfData },
41
- :text => { :type => SimpleText }
44
+ :text => { :type => SimpleText },
45
+ # Default fallback handler
46
+ :default => { :type => FileRecord }
42
47
  }
43
48
  end
44
49
 
@@ -22,4 +22,8 @@ end
22
22
 
23
23
  # Indicates an error in a query
24
24
  class QueryError < RuntimeError
25
+ end
26
+
27
+ # Indicates an error with import data
28
+ class ImportError < RuntimeError
25
29
  end
@@ -160,9 +160,6 @@ module TaliaCore
160
160
  # The name of the local node
161
161
  config["local_uri"] = "http://test.dummy/"
162
162
 
163
- # The "default" namespace
164
- config["default_namespace_uri"] = "http://default.dummy/"
165
-
166
163
  # Connect options for ActiveRDF
167
164
  # Defaults to in-memory RDFLite
168
165
  config["rdf_connection"] = {
@@ -315,10 +312,6 @@ module TaliaCore
315
312
  N::Namespace.shortcut(:local, @config["local_uri"])
316
313
  talia_logger.info("Local Domain: #{N::LOCAL}")
317
314
 
318
- # Register the default name
319
- N::Namespace.shortcut(:default, @config["default_namespace_uri"])
320
- talia_logger.debug("Default Domain: #{N::DEFAULT}")
321
-
322
315
  # Register namespace for database dupes
323
316
  N::Namespace.shortcut(:talia, "http://talia.discovery-project.eu/wiki/TaliaInternal#")
324
317
 
@@ -387,7 +380,7 @@ module TaliaCore
387
380
  end
388
381
 
389
382
  def load_core_ext
390
- path_to_core_ext = File.join(TALIA_ROOT, 'vendor', 'plugins', 'talia_core', 'lib', 'core_ext')
383
+ path_to_core_ext = File.join(TALIA_CODE_ROOT, 'lib', 'core_ext')
391
384
  Dir[path_to_core_ext + '/**/*.rb'].each { |f| require f}
392
385
  end
393
386
 
@@ -0,0 +1,58 @@
1
+ module TaliaCore
2
+
3
+ # Handling of RDF string values. These values can contain both locale (language)
4
+ # and type information. (E.g. "Some value^^string@en")
5
+ #
6
+ # This class will parse the value string an make all elements available as separate
7
+ # accessors.
8
+ class PropertyString < String
9
+
10
+ attr_accessor :type, :lang
11
+
12
+ # Create a new object by parsing the given property string
13
+ def self.parse(property_string)
14
+ self.new.parse(property_string)
15
+ end
16
+
17
+ # Create a new object from the given values. No parsing is done here.
18
+ def initialize(property_string = '', language = nil, type = nil)
19
+ @lang = language
20
+ @type = type
21
+ self.replace(property_string)
22
+ end
23
+
24
+ # Parses the given string into a PropertyString
25
+ def parse(property_string)
26
+ # First split for the type
27
+ type_split = property_string.split('^^')
28
+ # Check if any of the elements contains a language string
29
+ type_split = type_split.collect { |el| extract_lang(el) }
30
+ @type = (type_split.size > 1) ? type_split.last : nil
31
+ self.replace(type_split.first || '')
32
+ end
33
+
34
+ # Gives the "internal representation" - this should be equivalent
35
+ # to the string from which the object was created
36
+ def to_rdf
37
+ value = self.clone
38
+ value << '^^' << type if(type)
39
+ value << '@' << lang if(lang)
40
+ value
41
+ end
42
+
43
+ # Inspect shows the real content
44
+ def inspect
45
+ "\"#{self}\" <lang: #{lang.inspect} - type: #{type.inspect}>"
46
+ end
47
+
48
+ private
49
+
50
+ # Helper to extract a language string. The lang value, if any, will be added to the hash
51
+ def extract_lang(value)
52
+ lang_split = value.split('@')
53
+ @lang ||= (lang_split.size > 1) ? lang_split.last : nil
54
+ lang_split.first || ''
55
+ end
56
+
57
+ end
58
+ end
@@ -27,7 +27,8 @@ module TaliaCore
27
27
  @fat_relation || @plain_relation
28
28
  end
29
29
 
30
- # Return the "value" (Semantic relation value or the related ActiveSource)
30
+ # Return the "value" (Semantic relation value or the related ActiveSource).
31
+ # String values will actually be PropertyString strings
31
32
  def value
32
33
  semprop = object.is_a?(SemanticProperty)
33
34
  if(@object_type)
@@ -36,7 +37,7 @@ module TaliaCore
36
37
  @object_type.new(object.uri.to_s)
37
38
  else
38
39
  # Plain, return the object or the value for SemanticProperties
39
- semprop ? object.value : object
40
+ semprop ? PropertyString.parse(object.value) : object
40
41
  end
41
42
  end
42
43