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,50 @@
1
+ require 'reactor/link/temporary_link'
2
+
3
+ module Reactor
4
+ module Attributes
5
+ module LinkListExtender
6
+ def self.extend_linklist!
7
+ # this will trigger rails autoload
8
+ extend_given_linklist!(RailsConnector::LinkList)
9
+ end
10
+
11
+ def self.extend_given_linklist!(klass)
12
+ unless klass.instance_methods(false).include?("changed?")
13
+ klass.class_eval do
14
+ def <<(link_data)
15
+ super(transform_into_link(link_data))
16
+ end
17
+
18
+ # install #size_changed callback
19
+ Array.instance_methods(false).each do |meth|
20
+ old = instance_method(meth)
21
+ define_method(meth) do |*args, &block|
22
+ old_size = size
23
+ ret = old.bind(self).call(*args, &block)
24
+ size_changed(old_size, size) if old_size != size
25
+ ret
26
+ end if meth.to_sym != :size
27
+ end
28
+
29
+ def changed?
30
+ @changed == true
31
+ end
32
+
33
+ protected
34
+ def size_changed(old_size, size)
35
+ @changed = true
36
+ end
37
+
38
+ def transform_into_link(link_data)
39
+ if (link_data.respond_to?(:external?) && link_data.respond_to?(:internal?))
40
+ link_data
41
+ else
42
+ Reactor::Link::TemporaryLink.new(link_data)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,27 @@
1
+ require 'reactor/link/temporary_link'
2
+ require 'infopark_rails_connector'
3
+
4
+ module Reactor
5
+ module Attributes
6
+ class LinkListSerializer
7
+ def initialize(attr, value)
8
+ @attr, @value = attr, value
9
+ end
10
+
11
+ def serialize
12
+ linklist = RailsConnector::LinkList.new([])
13
+ enumerate(@value).each do |link_data|
14
+ linklist << link_data
15
+ end
16
+ linklist
17
+ end
18
+
19
+ private
20
+ def enumerate(value)
21
+ return [] if value.nil? || value.blank?
22
+ return [value] unless value.kind_of?(Array)
23
+ return value
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ require 'singleton'
2
+
3
+ module Reactor
4
+ module Cache
5
+ class Permission
6
+ include Singleton
7
+
8
+ def lookup(user, key, &block)
9
+ set(user, key, yield) unless set?(user, key)
10
+ get(user, key)
11
+ end
12
+
13
+ def initialize
14
+ @cache = {}
15
+ end
16
+
17
+ def invalidate(user)
18
+ @cache[user] = {}
19
+ end
20
+
21
+ protected
22
+ def set?(user, key)
23
+ @cache.key?(user) && @cache[user].include?(key)
24
+ end
25
+
26
+ def set(user, key, value)
27
+ @cache[user] ||= {}
28
+ @cache[user][key] = value
29
+ end
30
+
31
+ def get(user, key)
32
+ @cache[user][key]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,37 @@
1
+ require 'singleton'
2
+
3
+ module Reactor
4
+ module Cache
5
+ class User
6
+ include Singleton
7
+
8
+ def get(user_name)
9
+ @cache ||= {}
10
+ # Rails.logger.debug "User:Cache hit: #{hit?(user_name.to_s)} [#{@cache[user_name.to_s].inspect}]"
11
+
12
+ key = user_name.to_s
13
+ if hit?(key)
14
+ @cache[key]
15
+ else
16
+ @cache[key] = Reactor::Session::User.new(key)
17
+ end
18
+ end
19
+
20
+ def set(user_name, user)
21
+ @cache ||= {}
22
+ @cache[user_name.to_s] = user
23
+ end
24
+
25
+ def invalidate(user_name)
26
+ @cache ||= {}
27
+ @cache[user_name.to_s] = nil
28
+ end
29
+
30
+ private
31
+ def hit?(user_name)
32
+ key = user_name.to_s
33
+ @cache.key?(key) && !@cache[key].nil?
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,60 @@
1
+ module Reactor
2
+ # This is a collection of legacy method,s that have not found any better place to live.
3
+ module Legacy
4
+ module Base
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ # removes CMS objects underneath current object
10
+ # @option options :no_children if true prevents deletion unless there are no children
11
+ # @option options :img_children_only if true prevents deletion unless there are exclusively img children
12
+ # @option options [Array] :children if true prevents deletion if there are other children besides the specified ones (array of names)
13
+ def delete_children!(options={})
14
+ # TODO: provide better discrimination mechanisms (blocks?)
15
+ f_nochild = options.delete(:no_children)
16
+ f_imgchild = options.delete(:img_children_only)
17
+ f_children = options.delete(:children)
18
+
19
+ mychildren = self.children
20
+ image_children, other_children = mychildren.partition {|obj| obj.obj_class == 'Image'}
21
+
22
+ # are there any links pointing to this container
23
+ return false if has_super_links?
24
+ # is the flag set and are there any children
25
+ return false if f_nochild && !mychildren.empty?
26
+ # is the flag set and are there any children besides images
27
+ return false if f_imgchild && !other_children.empty?
28
+ # is the flag set and are there any children besides the specified ones
29
+ return false if f_children && !((mychildren.map(&:name) - f_children).empty?)
30
+
31
+ # check children for any links pointing to them
32
+ return false if mychildren.detect(&:has_super_links?)
33
+ # check if there are any grandchildren
34
+ return false if mychildren.detect {|child| !child.children.empty?}
35
+
36
+ # delete children
37
+ mychildren.each(&:destroy)
38
+ return true
39
+ end
40
+ end
41
+ module ClassMethods
42
+ def path_from_anything(anything)
43
+ obj_from_anything(anything).try(:path)
44
+ end
45
+
46
+ def obj_from_anything(anything)
47
+ case anything
48
+ when Fixnum then Obj.find(anything)
49
+ when String then Obj.find_by_path(anything)
50
+ when Obj then anything
51
+ else raise ArgumentError, "Link target must Fixnum, String or Obj, but was #{anything.class}."
52
+ end
53
+ end
54
+
55
+ def obj_id_from_anything(anything)
56
+ obj_from_anything(anything).try(:obj_id)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,19 @@
1
+ module Reactor
2
+ module Link
3
+ class External
4
+ attr_accessor :url
5
+
6
+ def external? ; true ; end
7
+ def internal? ; false ; end
8
+
9
+ def initialize(url)
10
+ raise TypeError, "#{self.class.name} is deprecated!"
11
+ self.url = url
12
+ end
13
+
14
+ def id
15
+ nil
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module Reactor
2
+ module Link
3
+ class Internal
4
+ attr_accessor :destination_object
5
+
6
+ def external? ; false ; end
7
+ def internal? ; true ; end
8
+
9
+ def initialize(anything)
10
+ raise TypeError, "#{self.class.name} is deprecated!"
11
+ self.destination_object = Obj.obj_from_anything(anything)
12
+ end
13
+
14
+ def id
15
+ nil
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ module Reactor
2
+ module Link
3
+ class TemporaryLink
4
+ attr_reader :url
5
+ attr_accessor :title
6
+
7
+ def external? ; raise TypeError, "This link needs to be persisted to gain any meaningful information" ; end
8
+ def internal? ; false ; end
9
+
10
+ def initialize(anything)
11
+ link_data = {}
12
+
13
+ case anything
14
+ when Hash
15
+ link_data = anything
16
+ else
17
+ link_data[:target] = anything
18
+ end
19
+
20
+ self.url = link_data[:target] || link_data[:url] || link_data[:destination_object]
21
+ self.title = link_data[:title] if link_data.key?(:title)
22
+ end
23
+
24
+ def url=(some_target)
25
+ @url = case some_target
26
+ when Obj
27
+ @destination_object = some_target
28
+ some_target.path
29
+ else
30
+ some_target
31
+ end
32
+ end
33
+
34
+ def destination_object
35
+ @destination_object ||= Obj.find_by_path(url)
36
+ end
37
+
38
+ def id
39
+ nil
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1 @@
1
+ class Reactor::NoWorkingVersion < StandardError ; end
@@ -0,0 +1 @@
1
+ class Reactor::NotPermitted < StandardError ; end
@@ -0,0 +1,262 @@
1
+ require 'reactor/session'
2
+ require 'reactor/cache/permission'
3
+ require 'reactor/cache/user'
4
+
5
+ module Reactor
6
+
7
+ # This module adds #permission method to obj that act as a gateway for permission checking,
8
+ # see documentation for [Permission::PermissionProxy] for more details.
9
+ # @see [Permission::PermissionProxy]
10
+ #
11
+ # Including this module also alters typical ActiveRecord call chain, so that it becomes:
12
+ # 1. permission checking
13
+ # 2. validations (if [Reactor::Validations] is included)
14
+ # 3. callbacks (before_*, around_*, after_*)
15
+ #
16
+ # Therefore if the user lacks permissions no futher actions are executed.
17
+ module Permission
18
+
19
+ module Base
20
+
21
+ # @see [PermissionProxy]
22
+ def permission
23
+ @permission ||= PermissionProxy.new(self)
24
+ end
25
+
26
+ # Wraps around Reactor::Persistence::Base#release! and ensures
27
+ # that current user has required permissions to release the object
28
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
29
+ def release!
30
+ ensure_permission_granted(:release)
31
+ return super
32
+ end
33
+
34
+ # Wraps around Reactor::Persistence::Base#take! and ensures
35
+ # that current user has required permissions to take the object
36
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
37
+ def take!
38
+ ensure_permission_granted(:take)
39
+ return super
40
+ end
41
+
42
+ # Wraps around Reactor::Persistence::Base#edit! and ensures
43
+ # that current user has required permissions to edit the object
44
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
45
+ def edit!
46
+ ensure_permission_granted(:edit)
47
+ return super
48
+ end
49
+
50
+ # Wraps around ActiveRecord::Persistence#save and ensures
51
+ # that current user has required permissions to save the object
52
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
53
+ def save
54
+ if persisted?
55
+ ensure_permission_granted(:write)
56
+ else
57
+ ensure_create_permission_granted(self.parent_obj_id)
58
+ end
59
+ return super
60
+ rescue Reactor::NotPermitted
61
+ return false
62
+ end
63
+
64
+ # Wraps around ActiveRecord::Persistence#save! and ensures
65
+ # that current user has required permissions to save the object
66
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
67
+ def save!
68
+ if persisted?
69
+ ensure_permission_granted(:write)
70
+ else
71
+ ensure_create_permission_granted(self.parent_obj_id)
72
+ end
73
+ return super
74
+ end
75
+
76
+ # Wraps around Reactor::Persistence::Base#resolve_refs! and ensures
77
+ # that current user has required permissions to call resolve refs on the object
78
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
79
+ def resolve_refs!
80
+ ensure_permission_granted(:write)
81
+ return super
82
+ end
83
+
84
+ private
85
+
86
+ def ensure_permission_granted(type)
87
+ raise Reactor::NotPermitted, "#{self.path} lacks neccessary permissions for #{type}" unless self.permission.send("#{type}?")
88
+ return true
89
+ end
90
+
91
+ def ensure_create_permission_granted(obj_id)
92
+ raise Reactor::NotPermitted, 'Obj lacks neccessary permissions for creation' unless Obj.find(obj_id).permission.create_children?
93
+ return true
94
+ end
95
+
96
+ end
97
+
98
+ # This class acts as a proxy to underlying permission checking classes.
99
+ # There are three possible cases for each permission type (live, read, write, root, create_children):
100
+ # 1. Given user is SuperUser - all permissions granted
101
+ # 2. Given user has the permission
102
+ # 3. Given user doesn't have the permission
103
+ class PermissionProxy
104
+
105
+ def initialize(obj) #:nodoc:
106
+ @obj = obj
107
+ @cache = Reactor::Cache::Permission.instance
108
+ @lookup = PermissionLookup.new(obj)
109
+ end
110
+
111
+ # Returns true if given user (or current user, if none given) has 'live' permission
112
+ def live?(user=nil)
113
+ granted?(user, :live)
114
+ end
115
+
116
+ # Returns true if given user (or current user, if none given) has 'read' permission
117
+ def read?(user = nil)
118
+ granted?(user, :read)
119
+ end
120
+
121
+ # Returns true if given user (or current user, if none given) has 'write' permission
122
+ def write?(user = nil)
123
+ granted?(user, :write)
124
+ end
125
+
126
+ # Returns true if given user (or current user, if none given) has 'root' permission
127
+ def root?(user = nil)
128
+ granted?(user, :root)
129
+ end
130
+
131
+ # Returns true if given user (or current user, if none given) has 'create_children' permission
132
+ def create_children?(user = nil)
133
+ granted?(user, :create_children)
134
+ end
135
+
136
+ # @see #root?
137
+ def delete?(user = nil)
138
+ root?(user)
139
+ end
140
+
141
+ # @see #write?
142
+ def take?(user = nil)
143
+ write?(user)
144
+ end
145
+
146
+ # @see #write?
147
+ def edit?(user = nil)
148
+ write?(user)
149
+ end
150
+
151
+ # Returns true if given user has permissions required to release an object (the exact
152
+ # permissions depend on the state of the object)
153
+ def release?(user = nil)
154
+ (has_workflow? && root?(user)) || (!has_workflow? && write?(user))
155
+ end
156
+
157
+ # Setter to overwrite the current groups for the given +permission+ with the
158
+ # given +groups+.
159
+ def set(permission, groups)
160
+ identifier = identifier(permission)
161
+
162
+ crul_obj.permission_set(identifier, groups)
163
+ end
164
+
165
+ # Grants the given +groups+ the given +permission+, without effecting
166
+ # already existing groups.
167
+ def grant(permission, groups)
168
+ identifier = identifier(permission)
169
+
170
+ crul_obj.permission_grant(identifier, groups)
171
+ end
172
+
173
+ # Takes away the given +permission+ from the given +groups+, without effecting
174
+ # already existing groups.
175
+ def revoke(permission, groups)
176
+ identifier = identifier(permission)
177
+
178
+ crul_obj.permission_revoke(identifier, groups)
179
+ end
180
+
181
+ # Takes away the given +permission+ from all groups currently set.
182
+ def clear(permission)
183
+ identifier = identifier(permission)
184
+
185
+ crul_obj.permission_clear(identifier)
186
+ end
187
+
188
+ protected
189
+
190
+ attr_reader :cache, :obj, :lookup
191
+
192
+ def crul_obj
193
+ obj.send(:crul_obj)
194
+ end
195
+
196
+ def default_user
197
+ Reactor::Session.instance.user_name || Reactor::Configuration.xml_access[:username]
198
+ end
199
+
200
+ def granted?(user, permission)
201
+ user ||= default_user
202
+ cache.lookup(user, "#{obj.path}:#{permission}") do
203
+ lookup.superuser?(user) || lookup.send("#{permission}?", user)
204
+ end
205
+ end
206
+
207
+ # A table with all available permissions and their identifier.
208
+ def self.permissions
209
+ @permissions ||= {
210
+ :read => 'permissionRead',
211
+ :root => 'permissionRoot',
212
+ :live => 'permissionLiveServerRead',
213
+ :write => 'permissionWrite',
214
+ :create_children => 'permissionCreateChildren',
215
+ }
216
+ end
217
+
218
+ def identifier(permission)
219
+ self.class.permissions[permission]
220
+ end
221
+
222
+ def has_workflow?
223
+ cache.lookup(:any, "#{obj.path}:workflow") do
224
+ RailsConnector::ObjectWithMetaData.find_by_object_id(obj.id).try(:workflow_name).present?
225
+ end
226
+ end
227
+ end
228
+
229
+ class PermissionLookup
230
+
231
+ def initialize(obj)
232
+ @obj = obj
233
+ @cache = Reactor::Cache::User.instance
234
+ end
235
+
236
+ Reactor::Permission::PermissionProxy.permissions.keys.each do |permission|
237
+ define_method "#{permission}?" do |user|
238
+ user_in_groups(user, obj.permissions.send(permission))
239
+ end
240
+ end
241
+
242
+ def superuser?(user)
243
+ cache.get(user).superuser?
244
+ end
245
+
246
+ def groups(user)
247
+ cache.get(user).groups
248
+ end
249
+
250
+ protected
251
+
252
+ attr_reader :cache, :obj
253
+
254
+ def user_in_groups(user, groups)
255
+ groups(user).detect { |user_group| groups.include?(user_group) } != nil
256
+ end
257
+
258
+ end
259
+
260
+ end
261
+
262
+ end