infopark_reactor 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +165 -0
  4. data/README.md +142 -0
  5. data/Rakefile +25 -0
  6. data/app/controllers/reactor_controller.rb +53 -0
  7. data/config/routes.rb +6 -0
  8. data/infopark_reactor.gemspec +30 -0
  9. data/lib/engine.rb +35 -0
  10. data/lib/infopark_reactor.rb +38 -0
  11. data/lib/reactor/already_released.rb +3 -0
  12. data/lib/reactor/attributes.rb +365 -0
  13. data/lib/reactor/attributes/date_serializer.rb +35 -0
  14. data/lib/reactor/attributes/html_serializer.rb +31 -0
  15. data/lib/reactor/attributes/link_list_extender.rb +50 -0
  16. data/lib/reactor/attributes/link_list_serializer.rb +27 -0
  17. data/lib/reactor/cache/permission.rb +36 -0
  18. data/lib/reactor/cache/user.rb +37 -0
  19. data/lib/reactor/legacy.rb +60 -0
  20. data/lib/reactor/link/external.rb +19 -0
  21. data/lib/reactor/link/internal.rb +19 -0
  22. data/lib/reactor/link/temporary_link.rb +43 -0
  23. data/lib/reactor/no_working_version.rb +1 -0
  24. data/lib/reactor/not_permitted.rb +1 -0
  25. data/lib/reactor/permission.rb +262 -0
  26. data/lib/reactor/persistence.rb +473 -0
  27. data/lib/reactor/rc_independent.rb +13 -0
  28. data/lib/reactor/session.rb +48 -0
  29. data/lib/reactor/session/observers.rb +32 -0
  30. data/lib/reactor/session/user.rb +22 -0
  31. data/lib/reactor/streaming_upload.rb +22 -0
  32. data/lib/reactor/sudo.rb +11 -0
  33. data/lib/reactor/support/link_matcher.rb +44 -0
  34. data/lib/reactor/tools/workflow_generator.rb +195 -0
  35. data/lib/reactor/validations.rb +55 -0
  36. data/lib/reactor/version.rb +3 -0
  37. data/lib/reactor/workflow.rb +19 -0
  38. data/lib/reactor/workflow/empty.rb +27 -0
  39. data/lib/reactor/workflow/standard.rb +34 -0
  40. metadata +204 -0
@@ -0,0 +1,38 @@
1
+ # Runtime Rails version detection
2
+ module Reactor
3
+ def self.rails3_0?
4
+ ::Rails::VERSION::MAJOR == 3 && ::Rails::VERSION::MINOR == 0
5
+ end
6
+
7
+ def self.rails3_1?
8
+ ::Rails::VERSION::MAJOR == 3 && ::Rails::VERSION::MINOR == 1
9
+ end
10
+
11
+ def self.rails3_2?
12
+ ::Rails::VERSION::MAJOR == 3 && ::Rails::VERSION::MINOR == 2
13
+ end
14
+ end
15
+
16
+ # require exceptions
17
+ require 'reactor/already_released'
18
+ require 'reactor/no_working_version'
19
+ require 'reactor/not_permitted'
20
+
21
+ # require components
22
+ require 'reactor/attributes'
23
+ require 'reactor/persistence'
24
+ require 'reactor/validations'
25
+ require 'reactor/permission'
26
+ require 'reactor/legacy'
27
+ require 'reactor/rc_independent'
28
+ require 'reactor/sudo'
29
+ require 'reactor/workflow'
30
+ require 'reactor/streaming_upload'
31
+
32
+ # require rails integration
33
+ require 'reactor/session'
34
+ require 'reactor/session/observers'
35
+ require 'reactor/session/user'
36
+
37
+ # require engine
38
+ require File.expand_path('../engine', __FILE__) if defined?(Rails)
@@ -0,0 +1,3 @@
1
+ module Reactor
2
+ class AlreadyReleased < StandardError ; end
3
+ end
@@ -0,0 +1,365 @@
1
+ require 'reactor/attributes/date_serializer'
2
+ require 'reactor/attributes/html_serializer'
3
+ require 'reactor/attributes/link_list_serializer'
4
+
5
+ require 'reactor/attributes/link_list_extender'
6
+
7
+ require 'singleton'
8
+
9
+ module Reactor
10
+ class AttributeHandlers
11
+ include Singleton
12
+
13
+ def initialize
14
+ # t1 = Time.now
15
+ self.generate_attribute_handlers
16
+ # Rails.logger.debug "Reactor::AttributeHandlers: generate_attribute_handlers took #{Time.now - t1}"
17
+ end
18
+
19
+ # Use this method to install attributes into class
20
+ def self.install_attributes(klass)
21
+ Reactor::AttributeHandlers.instance.install(klass, obj_class(klass))
22
+ end
23
+
24
+ # Use this method if attributes changed and you wish to reinstall them
25
+ def self.reinstall_attributes(klass, obj_class)
26
+ Reactor::AttributeHandlers.instance.tap do |handler|
27
+ handler.regenerate_attribute_handler(obj_class)
28
+ handler.install(klass, obj_class)
29
+ end
30
+ end
31
+
32
+ def self.obj_class(klass)
33
+ klass.name.split('::').last
34
+ end
35
+
36
+ def install(klass, obj_class)
37
+ if obj_class_known?(obj_class)
38
+ klass.send(:include, handler_module(obj_class))
39
+ end
40
+ end
41
+
42
+ def regenerate_attribute_handler(obj_class_name)
43
+ generate_attribute_handler(RailsConnector::ObjClass.find_by_name(obj_class_name))
44
+ end
45
+
46
+ protected
47
+
48
+ def handler_module(obj_class)
49
+ Reactor::AttributeHandlers.const_get('Handler__' + obj_class.to_s)
50
+ end
51
+
52
+ def obj_class_known?(obj_class)
53
+ Reactor::AttributeHandlers.const_defined?('Handler__' + obj_class.to_s)
54
+ end
55
+
56
+ def generate_attribute_handlers
57
+ RailsConnector::ObjClass.all.each do |obj_class|
58
+ # Rails.logger.debug "Reactor::AttributeHandlers: preparing obj class #{obj_class.name}"
59
+ generate_attribute_handler(obj_class) if obj_class.name =~ /^[A-Z]/
60
+ end
61
+ end
62
+
63
+ def generate_attribute_handler(obj_class)
64
+ # Rails.logger.debug "Reactor::AttributeHandlers: generating handler for #{obj_class.name}"
65
+ attribute_methods = []
66
+ writers = []
67
+
68
+ obj_class.custom_attributes.each do |attribute, attribute_data|
69
+ writers << attribute.to_sym
70
+ writers << attribute.to_s.underscore.to_sym
71
+
72
+ # Custom attribute readers: prevent unwanted nils
73
+ case attribute_data.attribute_type.to_sym
74
+ when :html
75
+ attribute_methods << <<-EOC
76
+ def #{attribute}
77
+ self[:#{attribute}] || ''.html_safe
78
+ end
79
+ EOC
80
+ when :date, :enum
81
+ attribute_methods << <<-EOC
82
+ def #{attribute}
83
+ self[:#{attribute}]
84
+ end
85
+ EOC
86
+ when :linklist
87
+ attribute_methods << <<-EOC
88
+ def #{attribute}
89
+ self[:#{attribute}] || RailsConnector::LinkList.new([])
90
+ end
91
+ EOC
92
+ when :multienum
93
+ attribute_methods << <<-EOC
94
+ def #{attribute}
95
+ self[:#{attribute}] || []
96
+ end
97
+ EOC
98
+ else
99
+ attribute_methods << <<-EOC
100
+ def #{attribute}
101
+ self[:#{attribute}] || ''
102
+ end
103
+ EOC
104
+ end
105
+ end
106
+
107
+ [:main_content, :contentType].each do |attribute|
108
+ writers << attribute.to_sym
109
+ writers << attribute.to_s.underscore.to_sym
110
+ end
111
+
112
+ Reactor::Cm::Obj::OBJ_ATTRS.each do |attribute|
113
+ writers << attribute.to_sym
114
+ writers << attribute.to_s.underscore.to_sym
115
+ end
116
+
117
+ writers.uniq!
118
+
119
+ writers.each do |attribute|
120
+ attribute_methods << <<-EOC
121
+ def #{attribute}=(value)
122
+ set(:#{attribute},value)
123
+ end
124
+ EOC
125
+ end
126
+
127
+ Reactor.class_eval <<-EOC
128
+ class AttributeHandlers
129
+ module Handler__#{obj_class.name}
130
+ def self.included(base)
131
+ # store allowed attributes
132
+ allowed_attrs = %w|#{writers * ' '}|.map(&:to_sym)
133
+ base.send(:instance_variable_set, '@_o_allowed_attrs', allowed_attrs)
134
+ end
135
+
136
+ # attribute readers and writers
137
+ #{attribute_methods}
138
+
139
+ # parent-setting handling
140
+ def parent=(parent_something)
141
+ set_parent(parent_something)
142
+ end
143
+ end
144
+ end
145
+ EOC
146
+
147
+ handler_module(obj_class.name)
148
+ # "Reactor::AttributeHandlers::Handler__#{obj_class.name}"
149
+ end
150
+ end
151
+ # This module provides support for ActiveRecord like attribute setting, plus additional
152
+ # #set method, which is equivalent to the setters.
153
+ #
154
+ # Date attributes are converted to correct format, when passed as Time-like objects.
155
+ # Links within HTML attributes are scanned and are converted if they point to local objects,
156
+ # so that the CM stores them as internal links.
157
+ #
158
+ # @note date attributes accept strings as values, and tries to parse them with Time.parse (unless they are in ISO format)
159
+ # @note link recognition works only on relative urls. All absolute urls are recognized as external links
160
+ module Attributes
161
+ module Base
162
+ def self.included(base)
163
+ base.extend(ClassMethods)
164
+ Reactor::Attributes::LinkListExtender.extend_linklist!
165
+ end
166
+
167
+ def valid_from=(value)
168
+ set(:valid_from, value)
169
+ end
170
+
171
+ def valid_until=(value)
172
+ set(:valid_until, value)
173
+ end
174
+
175
+ def obj_class=(value)
176
+ set(:obj_class, value)
177
+ end
178
+
179
+ def permalink=(value)
180
+ set(:permalink, value)
181
+ end
182
+
183
+ def name=(value)
184
+ set(:name, value)
185
+ end
186
+
187
+ def body=(value)
188
+ set(:body, value)
189
+ end
190
+
191
+ def blob=(value)
192
+ set(:blob, value)
193
+ end
194
+
195
+ def title=(value)
196
+ set(:title, value)
197
+ end
198
+
199
+ # Sets given attribute, to given value. Converts values if neccessary
200
+ # @see [Reactor::Attributes]
201
+ # @note options are passed to underlying xml interface, but as of now have no effect
202
+ def set(key, value, options={})
203
+ key = key.to_sym
204
+ raise TypeError, "can't modify frozen object" if frozen?
205
+ key = resolve_attribute_alias(key)
206
+ raise ArgumentError, "Unknown attribute #{key.to_s} for #{self.class.to_s} #{self.path}" unless allowed_attr?(key)
207
+ attr = key_to_attr(key)
208
+
209
+ not_formated_value = value
210
+ formated_value = serialize_value(key, value)
211
+ crul_set(attr, formated_value, options)
212
+ active_record_set(key, formated_value) if builtin_attr?(key)
213
+ rails_connector_set(key, formated_value)
214
+ send(key)
215
+ end
216
+
217
+
218
+ # Uploads a file/string into a CM. Requires call to save afterwards(!)
219
+ # @param [String, IO] data_or_io
220
+ # @param [String] extension file extension
221
+ # @note Uploaded file is loaded into memory, so try not to do anything silly (like uploading 1GB of data)
222
+ def upload(data_or_io, extension)
223
+ self.uploaded = true
224
+ crul_obj.upload(data_or_io, extension)
225
+ end
226
+
227
+ def uploaded?
228
+ self.uploaded == true
229
+ end
230
+
231
+ # @deprecated
232
+ def set_link(key, id_or_path_or_cms_obj)
233
+ target_path = case id_or_path_or_cms_obj
234
+ when Fixnum then Obj.find(id_or_path_or_cms_obj).path
235
+ when String then id_or_path_or_cms_obj
236
+ when Obj then id_or_path_or_cms_obj.path
237
+ else raise ArgumentError.new("Link target must Fixnum, String or Obj, but was #{id_or_path_or_cms_obj.class}.")
238
+ end
239
+
240
+ edit!
241
+ @force_resolve_refs = true
242
+ crul_obj.set_link(key, target_path.to_s)
243
+ end
244
+
245
+
246
+ protected
247
+ attr_accessor :uploaded
248
+
249
+ def reload_attributes(new_obj_class=nil)
250
+ Reactor::AttributeHandlers.reinstall_attributes(self.class, new_obj_class || self.obj_class)
251
+ end
252
+
253
+ def builtin_attr?(attr)
254
+ [:valid_from, :valid_until, :name, :obj_class, :content_type, :body, :blob, :permalink, :title].include?(attr)
255
+ end
256
+
257
+ def allowed_attr?(attr)
258
+ builtin_attr?(attr) || (self.class.send(:instance_variable_get,'@_o_allowed_attrs') || []).include?(key_to_attr(attr))
259
+ end
260
+
261
+ def resolve_attribute_alias(key)
262
+ key = :body if key == :main_content
263
+ key
264
+ end
265
+
266
+ def key_to_attr(key)
267
+ @__attribute_map ||= {
268
+ :main_content => :blob,
269
+ :body => :blob,
270
+ :valid_until => :validUntil,
271
+ :valid_from => :validFrom,
272
+ :content_type => :contentType,
273
+ :obj_class => :objClass
274
+ }
275
+
276
+ key = key.to_sym
277
+ key = @__attribute_map[key] if @__attribute_map.key?(key)
278
+ key
279
+ end
280
+
281
+ def serialize_value(attr, value)
282
+ case attribute_type(attr)
283
+ when :html
284
+ HTMLSerializer.new(attr, value).serialize
285
+ when :date
286
+ DateSerializer.new(attr, value).serialize
287
+ when :linklist
288
+ LinkListSerializer.new(attr, value).serialize
289
+ else
290
+ value
291
+ end
292
+ end
293
+
294
+ def rails_connector_set(field, value)
295
+ field = :blob if field.to_sym == :body
296
+ field = field.to_sym
297
+ # invalidate RC attribute cache
298
+ # send(:attr_dict).instance_variable_get('@attr_cache')[field] = nil
299
+ # # set new value for attr_dict
300
+ # send(:attr_dict).send(:blob_dict)[field] = value
301
+ if cached_value?(field, value)
302
+ send(:attr_dict).instance_variable_get('@attr_cache')[field] = value
303
+ send(:attr_dict).send(:blob_dict)[field] = :dirt_hack
304
+ else
305
+ send(:attr_dict).instance_variable_get('@attr_cache')[field] = nil
306
+ send(:attr_dict).send(:blob_dict)[field] = value
307
+ end
308
+ end
309
+
310
+ def cached_value?(attr, value)
311
+ attribute_type(attr) == :linklist
312
+ end
313
+
314
+ def active_record_set(field, value)
315
+ #method = :"#{field}="
316
+ #send(method, value) if self.respond_to? method
317
+ #value
318
+ @attributes[field.to_s] = value
319
+ end
320
+
321
+ # Lazily sets values for crul interface. May be removed in later versions
322
+ def crul_set(field, value, options)
323
+ @__crul_attributes ||= {}
324
+ @__crul_attributes[field.to_sym] = [value, options]
325
+ end
326
+
327
+ private
328
+ def path=(*args) ; super ; end
329
+
330
+ def attribute_type(attr)
331
+ return :html if [:body, :blob, :main_content].include?(attr.to_sym)
332
+ return :date if [:valid_from, :valid_until, :last_changed].include?(attr.to_sym)
333
+ return :string if [:name, :title, :obj_class, :permalink].include?(attr.to_sym)
334
+
335
+ custom_attr = self.obj_class_def.try(:custom_attributes).try(:[],attr.to_s)
336
+ raise TypeError, "obj_class_def is nil for: #{obj_class}" if self.obj_class_def.nil?
337
+
338
+ # FIXME: this should blow up on error
339
+ # raise TypeError, "Unable to determine type of attribute: #{attr}" if custom_attr.nil?
340
+ custom_attr ||= {"attribute_type"=>:string}
341
+ return custom_attr["attribute_type"].to_sym
342
+ end
343
+ end
344
+ module ClassMethods
345
+ def inherited(subclass)
346
+ super(subclass) # if you remove this line, y'll get TypeError: can't dup NilClass at some point
347
+
348
+ # t2 = Time.now
349
+ Reactor::AttributeHandlers.install_attributes(subclass)
350
+ # Rails.logger.debug "Installing dynamic module for #{subclass.name} took #{Time.now - t2}"
351
+ subclass
352
+ end
353
+
354
+ def __cms_attributes(obj_class)
355
+ obj_class_def = RailsConnector::Meta::EagerLoader.instance.obj_class(obj_class) #RailsConnector::ObjClass.where(:obj_class_name => obj_class).first
356
+ obj_class_def ? obj_class_def.custom_attributes : {}
357
+ end
358
+
359
+ def __mandatory_cms_attributes(obj_class)
360
+ obj_class_def = RailsConnector::Meta::EagerLoader.instance.obj_class(obj_class) #RailsConnector::ObjClass.where(:obj_class_name => obj_class).first
361
+ obj_class_def ? obj_class_def.mandatory_attribute_names(:only_custom_attributes => true) : []
362
+ end
363
+ end
364
+ end
365
+ end
@@ -0,0 +1,35 @@
1
+ module Reactor
2
+ module Attributes
3
+ class DateSerializer
4
+ def initialize(attr, value)
5
+ @attr, @value = attr, value
6
+ end
7
+
8
+ def serialize
9
+ @serialized ||= serialize_date
10
+ end
11
+
12
+ private
13
+ def serialize_date
14
+ if @value.is_a?(Time)
15
+ @value.utc.to_iso
16
+ elsif @value.is_a?(String)
17
+ if iso_format?(@value)
18
+ @value
19
+ elsif !@value.empty?
20
+ Time.zone.parse(@value).utc.to_iso
21
+ else
22
+ # empty string <=> clear date
23
+ nil
24
+ end
25
+ else
26
+ @value
27
+ end
28
+ end
29
+
30
+ def iso_format?(val)
31
+ val =~ /^[0-9]{14}$/
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ require 'reactor/support/link_matcher'
2
+
3
+ module Reactor
4
+ module Attributes
5
+ class HTMLSerializer
6
+ def initialize(attr, value)
7
+ @attr, @value = attr, value.to_str
8
+ end
9
+
10
+ def serialize
11
+ serialize_html
12
+ end
13
+
14
+ private
15
+ def serialize_html
16
+ link_expressions = [/(href|src)\s*=\s*"([^"]*)"/, /(href|src)\s*=\s*'([^']*)'/]
17
+ link_expressions.each do |expr|
18
+ @value.gsub!(expr) do |string|
19
+ link = Reactor::Support::LinkMatcher.new($2)
20
+ if link.recognized?
21
+ "#{$1}=\"#{link.rewrite_url}\""
22
+ else
23
+ string
24
+ end
25
+ end
26
+ end
27
+ @value
28
+ end
29
+ end
30
+ end
31
+ end