infopark_fiona7 1.1.0.0.10 → 1.2.0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/fiona7_ui.js +96 -50
  3. data/app/assets/javascripts/scrivito_patches/models/blob.js +21 -0
  4. data/app/assets/javascripts/scrivito_patches/models/obj.js +1 -1
  5. data/app/controllers/fiona7/release_controller.rb +6 -2
  6. data/app/controllers/fiona7/sessions_controller.rb +3 -0
  7. data/app/controllers/fiona7/workflow_controller.rb +46 -0
  8. data/app/helpers/fiona7_login_helper.rb +16 -0
  9. data/app/helpers/fiona7_override_helper.rb +1 -1
  10. data/config/delayed_routes.rb +1 -0
  11. data/config/precedence_routes.rb +92 -0
  12. data/infopark_fiona7.gemspec +2 -2
  13. data/lib/fiona7/blob_id_generator.rb +13 -0
  14. data/lib/fiona7/builder/indirect_blob_builder.rb +71 -0
  15. data/lib/fiona7/builder/lazy_blob_copier.rb +207 -0
  16. data/lib/fiona7/builder/obj_builder.rb +29 -50
  17. data/lib/fiona7/builder/obj_class_builder.rb +20 -0
  18. data/lib/fiona7/controllers/rest_api/blob_controller.rb +19 -0
  19. data/lib/fiona7/controllers/rest_api/obj_controller.rb +8 -4
  20. data/lib/fiona7/controllers/rest_api/workspace_controller.rb +2 -1
  21. data/lib/fiona7/engine.rb +56 -2
  22. data/lib/fiona7/json/obj_decorator.rb +4 -1
  23. data/lib/fiona7/link_converter/fiona_to_scrivito.rb +3 -1
  24. data/lib/fiona7/link_converter/scrivito_to_fiona.rb +12 -1
  25. data/lib/fiona7/mode_switch/cms_routes/scrivito_sdk.rb +1 -71
  26. data/lib/fiona7/mode_switch/cms_routes/scrivito_sdk_slave.rb +1 -71
  27. data/lib/fiona7/recursive_object_finder.rb +32 -8
  28. data/lib/fiona7/routers/rest_api.rb +6 -7
  29. data/lib/fiona7/scrivito_patches/attribute_content.rb +1 -1
  30. data/lib/fiona7/scrivito_patches/attribute_serializer.rb +22 -1
  31. data/lib/fiona7/scrivito_patches/basic_obj.rb +27 -5
  32. data/lib/fiona7/scrivito_patches/cms_backend.rb +3 -22
  33. data/lib/fiona7/scrivito_patches/cms_rest_api.rb +2 -1
  34. data/lib/fiona7/scrivito_patches/cms_routing.rb +2 -2
  35. data/lib/fiona7/scrivito_patches/model_library.rb +3 -3
  36. data/lib/fiona7/scrivito_patches/page_config.rb +21 -1
  37. data/lib/fiona7/scrivito_patches/preset_routes.rb +15 -2
  38. data/lib/fiona7/scrivito_patches/routing_extensions.rb +33 -20
  39. data/lib/fiona7/scrivito_patches/workspace.rb +3 -1
  40. data/lib/fiona7/super_object_finder.rb +63 -0
  41. data/lib/fiona7/type_register.rb +35 -9
  42. data/lib/fiona7/verity_search_engine.rb +1 -1
  43. data/lib/fiona7/version.rb +1 -1
  44. metadata +13 -7
  45. data/config/routes.rb +0 -13
@@ -17,11 +17,11 @@ Gem::Specification.new do |s|
17
17
  s.files = Dir["{app,config,db,lib}/**/*", "Rakefile", "README.md", "infopark_fiona7.gemspec"]
18
18
 
19
19
  s.add_dependency "rails", "~> 4.2.2"
20
- s.add_dependency "scrivito", "= 1.1.0"
20
+ s.add_dependency "scrivito", "= 1.2.0"
21
21
  s.add_dependency "scrivito_sdk"
22
22
  s.add_dependency "scrivito_editors"
23
23
  s.add_dependency "infopark_fiona_connector", "= 7.0.1.beta2"
24
- s.add_dependency "infopark_reactor", ">= 1.18.0"
24
+ s.add_dependency "infopark_reactor", ">= 1.22.3"
25
25
  s.add_dependency "mini_magick"
26
26
  #s.add_development_dependency "ruby-prof"
27
27
  end
@@ -0,0 +1,13 @@
1
+ module Fiona7
2
+ class BlobIdGenerator
3
+ def initialize(obj_id, time=nil)
4
+ @obj_id = obj_id.to_i
5
+ @time = time || Time.now
6
+ @stamp = @time.to_i.to_s(16)
7
+ end
8
+
9
+ def call
10
+ ("%022d" % @obj_id) + "bb" + @stamp
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,71 @@
1
+ module Fiona7
2
+ module Builder
3
+ class IndirectBlobBuilder
4
+ def initialize(obj, filename, file=nil)
5
+ @obj = obj
6
+ @filename = filename
7
+ @file = file
8
+ end
9
+
10
+ def call
11
+ # only publication type can contain children (code = "5")
12
+ if @obj.obj_type_code != "5" || @obj.id == 2001
13
+ parent_path = "/_uploads/#{@obj.id}"
14
+ else
15
+ parent_path = "#{@obj.path}/_uploads"
16
+ end
17
+
18
+ parent = ensure_parent_exists(parent_path)
19
+ ext = ::File.extname(@filename).to_s[1..-1]
20
+ name = ::File.basename(@filename, '.' + ext.to_s)
21
+ obj_class = if ['jpg', 'jpeg', 'gif', 'png', 'tif', 'tiff'].include?(ext)
22
+ 'X_Image'
23
+ else
24
+ 'X_Generic'
25
+ end
26
+
27
+ if @file
28
+ upload = WriteObj.upload(@file, ext, {name: name, parent: parent, obj_class: obj_class}).tap(&:release!)
29
+ else
30
+ upload = WriteObj.create({name: name, parent: parent, obj_class: obj_class}).tap(&:release!)
31
+ end
32
+ end
33
+
34
+ protected
35
+ # TODO: extract this code
36
+ def ensure_parent_exists(path)
37
+ remaining = path.split("/")
38
+ current = []
39
+ paths = []
40
+ original = path
41
+
42
+ while !remaining.empty?
43
+ current.push(remaining.shift)
44
+
45
+ path = current.join('/').presence || '/'
46
+ paths.push(path)
47
+ end
48
+
49
+ # TODO: add an optimization for exiting parents
50
+
51
+ paths.each do |path|
52
+ if !WriteObj.exists?(path: path)
53
+ name, parent_path = name_and_parent_path_from_path(path)
54
+ WriteObj.create!(name: name, parent_obj_id: WriteObj.find_by_path(parent_path).id, obj_class: 'X_Container')
55
+ end
56
+ end
57
+
58
+ WriteObj.find_by_path(original) || (raise "Tried to make sure that the parent under '#{original}' exist, but it does not :(")
59
+ end
60
+
61
+ def name_and_parent_path_from_path(path)
62
+ components = path.split('/')
63
+ name = components.pop.presence
64
+ parent_path= components.join('/').presence || '/'
65
+
66
+ return name, parent_path
67
+ end
68
+ end
69
+ end
70
+ end
71
+
@@ -0,0 +1,207 @@
1
+ require 'fiona7/super_object_finder'
2
+ require 'fiona7/blob_id_generator'
3
+ require 'fiona7/builder/indirect_blob_builder'
4
+
5
+ module Fiona7
6
+ module Builder
7
+ # This class implements a lazy blob copy/rename operation.
8
+ #
9
+ # Blob copy is trivial: an existing blob is taken
10
+ # and copied to the destination object.
11
+ #
12
+ # Blob rename occurs, when an apparent copy is executed,
13
+ # that is the blob that is copied is already associated
14
+ # with this object (and only with this object)
15
+ #
16
+ # The lazy part of this class is the fact that you
17
+ # need to call .save! on the passed destination obj
18
+ # to actually persist the changes in the obj
19
+ #
20
+ # NOTE: there is an exception to the above:
21
+ # renaming a blob in standalone mode executes immediately
22
+ class LazyBlobCopier
23
+ def initialize(attrs={})
24
+ @destination_obj = attrs[:destination_obj]
25
+ @attribute = attrs[:attr_name] || 'blob'
26
+ @source_blob_id = attrs[:source_blob_id]
27
+ @filename = attrs[:filename]
28
+ @content_type = attrs[:content_type]
29
+
30
+ @basename, @ext = *split_filename(@filename)
31
+ end
32
+
33
+ def call
34
+ validate_input!
35
+
36
+ @blob_obj = Fiona7::WriteObj.find(@source_blob_id.to_i)
37
+ @source_obj = find_unique_source_obj
38
+
39
+ if @destination_obj == @source_obj
40
+ rename
41
+ else
42
+ copy
43
+ end
44
+
45
+ return {id: @destination_blob_id}
46
+ end
47
+
48
+ protected
49
+
50
+ def rename
51
+ return unless @basename.present? && @ext.present?
52
+
53
+ if Fiona7.mode == :legacy
54
+ direct_rename
55
+ else
56
+ indirect_rename
57
+ end
58
+ end
59
+
60
+ def copy
61
+ if Fiona7.mode == :legacy
62
+ direct_copy
63
+ else
64
+ indirect_copy
65
+ end
66
+ end
67
+
68
+ def direct_rename
69
+ @destination_obj.set(:name, @basename)
70
+ # CM stores the file extension as :contentType and not the mimetype
71
+ @destination_obj.set(:content_type, @ext)
72
+
73
+ @destination_blob_id = Fiona7::BlobIdGenerator.new(@destination_obj.id).call
74
+ end
75
+
76
+ def indirect_rename
77
+ # This is kind of silly, but as far as I can tell there is no way
78
+ # around it: whenever you rename a blob, you will also rename
79
+ # it in the published workspace.
80
+ # Basically the same design flaw as in the "classical" CM
81
+ #
82
+ # NOTE: this applies the change directly, whether a "save"
83
+ # happens afterwards on not, so it breaks the "lazy" part
84
+ # of the contract
85
+ @blob_obj.set(:name, @basename)
86
+ @blob_obj.set(:content_type, @ext)
87
+ @blob_obj.save!
88
+ # Just for good measure, since contentType is a content attribute
89
+ @blob_obj.release
90
+ # And now for the "lazy" part, we have to update the linklist with
91
+ # new "fake" blob id to fool SDK into thinking we actually copied
92
+ # the blob
93
+ @destination_blob_id = Fiona7::BlobIdGenerator.new(@blob_obj.id, @blob_obj.last_changed).call
94
+ @destination_obj.set(@attribute, {
95
+ title: @destination_blob_id,
96
+ destination_object: @blob_obj
97
+ })
98
+ end
99
+
100
+ def direct_copy
101
+ @destination_blob_id = Fiona7::BlobIdGenerator.new(@destination_obj.id).call
102
+
103
+ content = @blob_obj.edited? ? 'edited' : 'released'
104
+
105
+ begin
106
+ blob_ticket_id = @blob_obj.send(:crul_obj).blob_ticket_id(content)
107
+ target = @destination_obj.send(:crul_obj)
108
+
109
+ target.set(:blob, {blob_ticket_id => {encoding: 'stream'}})
110
+ rescue Reactor::Cm::BlobTooSmallError
111
+ # Reading blob_ticket_id failed,
112
+ # an alternative strategy is required:
113
+ # reading into memory
114
+ @destination_obj.set(:blob, @blob_obj.body)
115
+ end
116
+
117
+ if @basename.present? && @ext.present?
118
+ direct_rename
119
+ end
120
+
121
+ # NOTE: calling @destination_obj.save! will trigger target.save!
122
+ end
123
+
124
+ def indirect_copy
125
+ destination_blob = construct_destination_blob
126
+
127
+ # NOTE: this should under normal circumstances
128
+ # never return 'edited'. But this is on the safe side
129
+ content = @blob_obj.edited? ? 'edited' : 'released'
130
+
131
+ begin
132
+ blob_ticket_id = @blob_obj.send(:crul_obj).blob_ticket_id(content)
133
+ target = destination_blob.send(:crul_obj)
134
+
135
+ target.set(:blob, {blob_ticket_id => {encoding: 'stream'}})
136
+ rescue Reactor::Cm::BlobTooSmallError
137
+ # Reading blob_ticket_id failed,
138
+ # an alternative strategy is required:
139
+ # reading into memory
140
+ destination_blob.set(:blob, @blob_obj.body)
141
+ end
142
+
143
+ # NOTE: this will also call target.save!
144
+ destination_blob.save!
145
+ destination_blob.release
146
+
147
+ @destination_blob_id = Fiona7::BlobIdGenerator.new(destination_blob.id, destination_blob.last_changed).call
148
+
149
+ @destination_obj.set(@attribute, {
150
+ title: @destination_blob_id, destination_object: destination_blob
151
+ })
152
+ end
153
+
154
+ def split_filename(filename)
155
+ return unless filename
156
+
157
+ ext = ::File.extname(filename)[1..-1]
158
+ base = ::File.basename(filename, ::File.extname(filename))
159
+
160
+ return base, ext
161
+ end
162
+
163
+ def validate_input!
164
+ if Fiona7.mode == :legacy && @attribute != 'blob'
165
+ raise Scrivito::ClientError.new("Blob attributes other than :blob are unsupported in :legacy mode")
166
+ end
167
+ if !extension_and_content_type_match?
168
+ Rails.logger.error("BLOB COPY ERROR: content_type passed to the blob copy operation ignored, because it does not match the file extension")
169
+ end
170
+ if !@destination_obj.kind_of?(Fiona7::WriteObj)
171
+ raise ArgumentError, "Wrong input for LazyBlobCopier"
172
+ end
173
+ end
174
+
175
+ def extension_and_content_type_match?
176
+ return true if @content_type.blank?
177
+ return false if @ext.blank?
178
+
179
+ return MIME::Types.type_for(@ext).any? {|m| m.content_type == @content_type }
180
+ #rescue
181
+ return false
182
+ end
183
+
184
+ def find_unique_source_obj
185
+ if Fiona7.mode == :legacy
186
+ return @blob_obj
187
+ end
188
+
189
+ linking = Fiona7::SuperObjectFinder.new.find(@blob_obj)
190
+
191
+ if linking.count == 1
192
+ return linking.first
193
+ else
194
+ return nil
195
+ end
196
+ rescue ActiveRecord::RecordNotFound
197
+ return nil
198
+ end
199
+
200
+ def construct_destination_blob
201
+ filename = @filename || (@source_obj.name + "." + @source_obj.file_extension)
202
+ Fiona7::Builder::IndirectBlobBuilder.new(@destination_obj, filename).call
203
+
204
+ end
205
+ end
206
+ end
207
+ end
@@ -13,6 +13,9 @@ require 'fiona7/obj_class_name_demangler'
13
13
  require 'fiona7/builder/batch_widget_writer'
14
14
  require 'fiona7/link_converter/scrivito_to_fiona'
15
15
  require 'fiona7/widget_resolver'
16
+ require 'fiona7/builder/lazy_blob_copier'
17
+ require 'fiona7/builder/indirect_blob_builder'
18
+ require 'fiona7/blob_id_generator'
16
19
 
17
20
 
18
21
  module Fiona7
@@ -25,7 +28,6 @@ module Fiona7
25
28
  @values.delete(:_modification)
26
29
  # revert command sends this info. which is silly.
27
30
  @values.delete(:_last_changed)
28
- #puts "CREATING/UPDATING: #{@values.inspect}"
29
31
  end
30
32
 
31
33
  def build
@@ -218,6 +220,8 @@ module Fiona7
218
220
  #if value.nil? && value != @obj[attribute_name]
219
221
  @obj.set(attribute_name.to_s, value)
220
222
  #end
223
+ when :number
224
+ @obj.set(attribute_name.to_s, value.to_s)
221
225
  when :stringlist
222
226
  if Fiona7.mode == :legacy && attribute_name.to_s == "channels"
223
227
  @obj.set(:channels, value || [])
@@ -247,9 +251,25 @@ module Fiona7
247
251
  elsif value.nil?
248
252
  @obj.set(attribute_name.to_s, [])
249
253
  elsif value.kind_of?(Hash)
250
- #TODO: legacy mode!
251
- target = {title: value["id"], destination_object: InternalReleasedObj.find(value["id"].to_i)}
252
- @obj.set(attribute_name.to_s, target)
254
+ source_blob_id = value["id"] || value["id_to_copy"]
255
+ filename = value["filename"]
256
+ content_type = value["content_type"]
257
+
258
+ # There is a little bit of magic behind this.
259
+ # This will pass some data directly to the crul_obj
260
+ # bypassing @obj.set(), but calling @obj.save
261
+ # afterwards still persists the data correctly
262
+ #
263
+ # This works the same way as Reactor::Tools::Uploader
264
+ Fiona7::Builder::LazyBlobCopier.new({
265
+ destination_obj: @obj,
266
+ attr_name: attribute_name.to_s,
267
+ source_blob_id: source_blob_id,
268
+ filename: filename,
269
+ content_type: content_type
270
+ }).call
271
+
272
+ # NOTE: no @obj.set() required here
253
273
  else
254
274
  raise Scrivito::ClientError.new("Invalid input for binary field", 422)
255
275
  end
@@ -291,7 +311,7 @@ module Fiona7
291
311
  end
292
312
 
293
313
  def rewrite_full_text
294
- full_text = ::YAML.load(@obj.attr_values["X_full_text"])
314
+ full_text = ::YAML.load(@obj.attr_values["X_full_text"]) rescue {}
295
315
  full_text = {} unless full_text.kind_of?(Hash)
296
316
  full_text["_widget_pool"] ||= {}
297
317
  full_text["_widget_pool"].deep_merge!(@widget_pool)
@@ -302,55 +322,14 @@ module Fiona7
302
322
  end
303
323
 
304
324
  def upload_file(file)
305
- # only publication type can contain children (code = "5")
306
- # also handle / this way
307
- if @obj.obj_type_code != "5" || @obj.id == 2001
308
- parent_path = "/_uploads/#{@obj.id}"
309
- else
310
- parent_path = "#{@obj.path}/_uploads"
311
- end
312
-
313
- parent = ensure_parent_exists(parent_path)
314
- ext = ::File.extname(file.path).to_s[1..-1]
315
- name = ::File.basename(file.path, '.' + ext.to_s)
316
- obj_class = if ['jpg', 'jpeg', 'gif', 'png', 'tif', 'tiff'].include?(ext)
317
- 'X_Image'
318
- else
319
- 'X_Generic'
320
- end
321
-
322
- raise Scrivito::ScrivitoError.new("File extension of uploaded file cannot be identified", 422) if ext.nil?
323
-
324
- upload = WriteObj.upload(file, ext, {name: name, parent: parent, obj_class: obj_class}).tap(&:release!)
325
- encoded_id = '%032d' % upload.id
325
+ upload = Fiona7::Builder::IndirectBlobBuilder.new(@obj, ::File.basename(file.path), file).call
326
+ encoded_id = Fiona7::BlobIdGenerator.new(upload.id, upload.last_changed).call
326
327
  {title: encoded_id, destination_object: upload}
327
328
  end
328
329
 
329
330
  def upload_uploaded_file(file)
330
- # FIXME: DRY
331
- # only publication type can contain children (code = "5")
332
- # also handle / this way
333
- if @obj.obj_type_code != "5" || @obj.id == 2001
334
- parent_path = "/_uploads/#{@obj.id}"
335
- else
336
- parent_path = "#{@obj.path}/_uploads"
337
- end
338
-
339
- parent = ensure_parent_exists(parent_path)
340
- # here the first difference
341
- ext = ::File.extname(file.original_filename).to_s[1..-1]
342
- name = ::File.basename(file.original_filename, '.' + ext.to_s)
343
- obj_class = if ['jpg', 'jpeg', 'gif', 'png', 'tif', 'tiff'].include?(ext)
344
- 'X_Image'
345
- else
346
- 'X_Generic'
347
- end
348
-
349
- raise Scrivito::ScrivitoError.new("File extension of uploaded file cannot be identified", 422) if ext.nil?
350
-
351
- # here the second difference
352
- upload = WriteObj.upload(file.open, ext, {name: name, parent: parent, obj_class: obj_class}).tap(&:release!)
353
- encoded_id = '%032d' % upload.id
331
+ upload = Fiona7::Builder::IndirectBlobBuilder.new(@obj, file.original_filename, file.open).call
332
+ encoded_id = Fiona7::BlobIdGenerator.new(upload.id, upload.last_changed).call
354
333
  {title: encoded_id, destination_object: upload}
355
334
  end
356
335
 
@@ -48,6 +48,26 @@ module Fiona7
48
48
  else
49
49
  Assert.constraint(false, "Type #{attribute[:type]} requested for #{attribute[:real_name]}, but is #{cms_attribute.attribute_type}")
50
50
  end
51
+ elsif cms_attribute.attribute_type == "multienum" && attribute[:type] == :stringlist
52
+ # This is a bit of hack. This allows a multienum to exist
53
+ # But be treated in the application like a stringlist
54
+ #
55
+ # The intention here is to allow dynamically restricting or allowing
56
+ # values in the application, while having a fixed superset of allowed
57
+ # values in the CMS
58
+ #
59
+ # Nothing to do.
60
+ Rails.logger.warn("TYPE SYNCHRONIZATION: Type #{attribute[:type]} requested for #{attribute[:real_name]}, but is #{cms_attribute.attribute_type}, this may cause errors.")
61
+ elsif cms_attribute.attribute_type == "enum" && attribute[:type] == :string
62
+ # This is a bit of hack. This allows an enum to exist
63
+ # But be treated in the application like a string
64
+ #
65
+ # The intention here is to allow dynamically restricting or allowing
66
+ # values in the application, while having a fixed superset of allowed
67
+ # values in the CMS
68
+ #
69
+ # Nothing to do.
70
+ Rails.logger.warn("TYPE SYNCHRONIZATION: Type #{attribute[:type]} requested for #{attribute[:real_name]}, but is #{cms_attribute.attribute_type}, this may cause errors.")
51
71
  elsif attribute[:type] == :enum || attribute[:type] == :multienum
52
72
  if cms_attribute.attribute_type == "multienum" || cms_attribute.attribute_type == "enum"
53
73
  if attribute[:values]