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.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE +165 -0
- data/README.md +142 -0
- data/Rakefile +25 -0
- data/app/controllers/reactor_controller.rb +53 -0
- data/config/routes.rb +6 -0
- data/infopark_reactor.gemspec +30 -0
- data/lib/engine.rb +35 -0
- data/lib/infopark_reactor.rb +38 -0
- data/lib/reactor/already_released.rb +3 -0
- data/lib/reactor/attributes.rb +365 -0
- data/lib/reactor/attributes/date_serializer.rb +35 -0
- data/lib/reactor/attributes/html_serializer.rb +31 -0
- data/lib/reactor/attributes/link_list_extender.rb +50 -0
- data/lib/reactor/attributes/link_list_serializer.rb +27 -0
- data/lib/reactor/cache/permission.rb +36 -0
- data/lib/reactor/cache/user.rb +37 -0
- data/lib/reactor/legacy.rb +60 -0
- data/lib/reactor/link/external.rb +19 -0
- data/lib/reactor/link/internal.rb +19 -0
- data/lib/reactor/link/temporary_link.rb +43 -0
- data/lib/reactor/no_working_version.rb +1 -0
- data/lib/reactor/not_permitted.rb +1 -0
- data/lib/reactor/permission.rb +262 -0
- data/lib/reactor/persistence.rb +473 -0
- data/lib/reactor/rc_independent.rb +13 -0
- data/lib/reactor/session.rb +48 -0
- data/lib/reactor/session/observers.rb +32 -0
- data/lib/reactor/session/user.rb +22 -0
- data/lib/reactor/streaming_upload.rb +22 -0
- data/lib/reactor/sudo.rb +11 -0
- data/lib/reactor/support/link_matcher.rb +44 -0
- data/lib/reactor/tools/workflow_generator.rb +195 -0
- data/lib/reactor/validations.rb +55 -0
- data/lib/reactor/version.rb +3 -0
- data/lib/reactor/workflow.rb +19 -0
- data/lib/reactor/workflow/empty.rb +27 -0
- data/lib/reactor/workflow/standard.rb +34 -0
- 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,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
|