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,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
|