infopark_reactor 1.5.1

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