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