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,13 @@
1
+ module Reactor
2
+ module RcIndependent
3
+ module Base
4
+ def really_edited?
5
+ crul_obj.edited?
6
+ end
7
+
8
+ def really_released?
9
+ crul_obj.released?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,48 @@
1
+ require 'singleton'
2
+ require 'observer'
3
+
4
+ require 'reactor/cm/bridge'
5
+ require 'reactor/cache/user'
6
+
7
+ class Reactor::Session
8
+ attr_reader :user_name, :session_id
9
+ include Singleton
10
+ include Observable
11
+
12
+ def login(session_id)
13
+ if !logged_in?(session_id)
14
+ self.user_name = authenticate(session_id)
15
+ end
16
+ end
17
+
18
+ def destroy
19
+ self.session_id = self.user_name = nil
20
+ end
21
+
22
+ def logged_in?(session_id)
23
+ self.session_id.present? && self.user? && self.session_id == session_id
24
+ end
25
+
26
+ def user?
27
+ self.user_name.present?
28
+ end
29
+
30
+ def user
31
+ Reactor::Cache::User.instance.get(self.user_name)
32
+ end
33
+
34
+ def user_name=(new_user_name)
35
+ @user_name = new_user_name
36
+ changed(true) # I will find and burn your house to the ground if you remove this line
37
+ notify_observers(@user_name)
38
+ end
39
+
40
+ protected
41
+ attr_writer :session_id
42
+
43
+ def authenticate(session_id)
44
+ self.session_id = session_id
45
+ Reactor::Cm::Bridge.login_for(session_id)
46
+ end
47
+
48
+ end
@@ -0,0 +1,32 @@
1
+ require 'reactor/session'
2
+
3
+ module Reactor::Session::Observers
4
+ class PermissionCacheInvalidator
5
+ def update(user_name)
6
+ #puts '%%% invalidating permission cache'
7
+ Reactor::Cache::Permission.instance.invalidate(user_name)
8
+ end
9
+ end
10
+
11
+ class UserCacheInvalidator
12
+ def update(user_name)
13
+ #puts '%%% invalidating user cache'
14
+ Reactor::Cache::User.instance.invalidate(user_name)
15
+ end
16
+ end
17
+
18
+ class CmsAccessDataPropagator
19
+ def update(user_name)
20
+ #puts '%%% propagating user'
21
+ Reactor::Configuration.xml_access[:username] = user_name
22
+ end
23
+ end
24
+
25
+ self.constants.each do |possible_observer_name|
26
+ possible_observer = self.const_get(possible_observer_name)
27
+ if possible_observer.method_defined?(:update)
28
+ Reactor::Session.instance.add_observer(possible_observer.new)
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,22 @@
1
+ require 'reactor/cm/user'
2
+
3
+ module Reactor
4
+ class Session
5
+ class User
6
+ def initialize(user_name)
7
+ # Rails.logger.debug "Reading user #{user_name} from CM"
8
+ user = Reactor::Cm::User.new(user_name)
9
+ @user_name= user_name
10
+ @groups = user.groups
11
+ @language = user.language
12
+ @superuser= user.is_root?
13
+ end
14
+
15
+ attr_reader :user_name, :groups, :language
16
+
17
+ def superuser?
18
+ @superuser == true
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module Reactor
2
+ module StreamingUpload
3
+ module Base
4
+ # Uploads a file/string into a CM. Requires call to save afterwards(!)
5
+ # @param [String, IO] data_or_io
6
+ # @param [String] extension file extension
7
+ # @raise [Reactor::UploadError] raised when CM does not respond with
8
+ # streaming ticket (i.e. has not accepted the file)
9
+ # @raise [Timeout::Error] if upload to CM takes more than:
10
+ # 60 seconds for IO streaming IO or 30 seconds for memory
11
+ #
12
+ # NOTE: behavior of this method is slightly different, than the
13
+ # traditional method: this method opens a TCP connection to the CM,
14
+ # transfers the data and stores the reference (so called streaming
15
+ # ticket). You still need to call save! afterwards.
16
+ def upload(data_or_io, extension)
17
+ self.uploaded = true
18
+ Reactor::Tools::Uploader.new(crul_obj).upload(data_or_io, extension)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ module Reactor
2
+ module Sudo
3
+ def self.su(other_user_name, &block)
4
+ current_user_name = Reactor::Session.instance.user_name
5
+ Reactor::Session.instance.user_name = other_user_name
6
+ yield
7
+ ensure
8
+ Reactor::Session.instance.user_name = current_user_name || 'root'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,44 @@
1
+ module Reactor
2
+ module Support
3
+ class LinkMatcher
4
+ def initialize(url)
5
+ @url = url
6
+ end
7
+
8
+ def recognized?
9
+ match = match_url
10
+ (match[:action] == "index") &&
11
+ (match[:controller] == "rails_connector/cms_dispatch") &&
12
+ ((match[:id].present? && Obj.exists?(match[:id].to_i)) ||
13
+ (match[:permalink].present? && Obj.exists?(:permalink => match[:permalink])))
14
+ rescue ActionController::RoutingError
15
+ return false
16
+ end
17
+
18
+ def rewrite_url
19
+ match = match_url
20
+
21
+ if match[:permalink].present?
22
+ append_fragment_and_query Obj.find_by_permalink(match[:permalink]).path
23
+ elsif match[:id].present?
24
+ append_fragment_and_query Obj.find(match[:id].to_i).path
25
+ end
26
+ end
27
+
28
+ private
29
+ def match_url
30
+ relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
31
+ url = @url.clone
32
+ url.gsub!(/^#{Regexp.escape(relative_url_root)}/, '') if relative_url_root.present?
33
+ Rails.application.routes.recognize_path(url)
34
+ end
35
+
36
+ def append_fragment_and_query(obj_path)
37
+ uri = URI.parse(@url)
38
+ obj_path += "?#{uri.query}" if uri.query
39
+ obj_path += "##{uri.fragment}" if uri.fragment
40
+ obj_path
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,195 @@
1
+ module Reactor
2
+ module Tools
3
+ class WorkflowGenerator
4
+ attr_reader :obj, :users, :editors, :correctors, :groups, :workflow_name
5
+
6
+ def initialize(options)
7
+ @editors = options[:editors]
8
+ @correctors = options[:correctors]
9
+ @users = @editors + @correctors
10
+ @groups = personal_groups(@users)
11
+ @class_name = options[:obj_class] || generate_obj_class_name
12
+ @workflow_name = options[:workflow_name] || generate_workflow_name
13
+ @obj_name = options[:obj_name] || generate_obj_name
14
+ end
15
+
16
+ def generate!
17
+ create_groups
18
+ create_users
19
+ create_signatures
20
+ create_workflow
21
+ create_obj_class
22
+ create_obj
23
+ set_permissions
24
+ start_workflow
25
+ end
26
+
27
+ def destroy!
28
+ destroy_objs
29
+ destroy_obj_class
30
+ destroy_workflow
31
+ destroy_users
32
+ destroy_groups
33
+ destroy_signatures
34
+ end
35
+
36
+ def personal_group(user)
37
+ "#{user}_group"
38
+ end
39
+
40
+ protected
41
+
42
+ def personal_signature(user)
43
+ "#{user}_signature"
44
+ end
45
+
46
+ def personal_groups(users)
47
+ users.map {|user| personal_group(user) }
48
+ end
49
+
50
+ def create_groups
51
+ @groups.each do |group|
52
+ if Reactor::Cm::Group.exists?(group)
53
+ Reactor::Cm::Group.get(group)
54
+ else
55
+ Reactor::Cm::Group.create(:name => group)
56
+ end
57
+ end
58
+ end
59
+
60
+ def create_users
61
+ @users.each do |user|
62
+ if Reactor::Cm::User::Internal.exists?(user)
63
+ internal_user = Reactor::Cm::User::Internal.get(user)
64
+ else
65
+ internal_user = Reactor::Cm::User::Internal.create(user, personal_group(user))
66
+ end
67
+
68
+ internal_user.change_password('thepasswordispassword')
69
+ end
70
+ end
71
+
72
+ def create_signatures
73
+ # hash storing users as keys and attribute names as values
74
+ @signatures = {}
75
+ @users.each do |user|
76
+ signature = personal_signature(user)
77
+ if Reactor::Cm::Attribute.exists?(signature)
78
+ #Reactor::Cm::Attribute.get(signature)
79
+ @signatures[user] = signature
80
+ else
81
+ Reactor::Cm::Attribute.create(signature, 'signature')
82
+ @signatures[user] = signature
83
+ end
84
+ end
85
+ end
86
+
87
+ def create_workflow
88
+ if Reactor::Cm::Workflow.exists?(@workflow_name)
89
+ @workflow = Reactor::Cm::Workflow.get(@workflow_name)
90
+ else
91
+ @workflow = Reactor::Cm::Workflow.create(@workflow_name)
92
+ end
93
+
94
+ # set up workflow steps
95
+ @workflow.edit_groups = personal_groups(@editors)
96
+
97
+ serialized_signatures = []
98
+ @correctors.each do |corrector|
99
+ signature = {
100
+ :group => personal_group(corrector),
101
+ :attribute => @signatures[corrector]
102
+ }
103
+ serialized_signatures << signature
104
+ end
105
+ @workflow.signatures = serialized_signatures
106
+
107
+ @workflow.save!
108
+ @workflow.reload
109
+ end
110
+
111
+ def create_obj_class
112
+ @obj_class = if Reactor::Cm::ObjClass.exists?(@class_name)
113
+ Reactor::Cm::ObjClass.get(@class_name)
114
+ else
115
+ Reactor::Cm::ObjClass.create(@class_name, 'publication')
116
+ end
117
+ end
118
+
119
+ def create_obj
120
+ @obj = Reactor::Cm::Obj.create(@obj_name, '/', @class_name)
121
+ @obj.set(:workflowName, @workflow_name)
122
+ @obj.save!
123
+ end
124
+
125
+ def set_permissions
126
+ # get RC object
127
+ @rc_obj = RailsConnector::Obj.find(@obj.obj_id)
128
+
129
+ # use nice API to set permissions
130
+ @groups.each do |group|
131
+ @rc_obj.permission.grant(:read, group)
132
+ @rc_obj.permission.grant(:write, group)
133
+ end
134
+
135
+ # Allow Dirk to release the object
136
+ @rc_obj.permission.grant(:root, personal_group(@correctors.last))
137
+ end
138
+
139
+ def start_workflow
140
+ @obj.release!
141
+ Reactor::Sudo.su(@editors.first) do
142
+ @obj.edit!
143
+ end
144
+ end
145
+
146
+ def destroy_objs
147
+ RailsConnector::Obj.find_all_by_obj_class(@class_name).each(&:destroy)
148
+ end
149
+
150
+ def destroy_obj_class
151
+ @obj_class.delete!
152
+ end
153
+
154
+ def destroy_workflow
155
+ @workflow.delete!
156
+ end
157
+
158
+ def destroy_users
159
+ @users.each do |user|
160
+ Reactor::Cm::User::Internal.get(user).delete!
161
+ end
162
+ end
163
+
164
+ def destroy_groups
165
+ @groups.each do |group|
166
+ Reactor::Cm::Group.get(group).delete!
167
+ end
168
+ end
169
+
170
+ def destroy_signatures
171
+ @signatures.values.each do |attribute|
172
+ Reactor::Cm::Attribute.get(attribute).delete!
173
+ end
174
+ end
175
+
176
+ def generate_workflow_name
177
+ "GeneratedWorkflow#{generate_token}"
178
+ end
179
+
180
+ def generate_obj_name
181
+ "generated_obj#{generate_token}"
182
+ end
183
+
184
+ def generate_obj_class_name
185
+ "GeneratedObjClass#{generate_token}"
186
+ end
187
+
188
+ def generate_token
189
+ characters = ('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a
190
+ token = 8.times.map { characters[rand(characters.length)] }.join
191
+ end
192
+
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,55 @@
1
+ module Reactor
2
+ # This module provides standard validations for Objs (:permalink, :parent_obj_id, :name, :obj_class),
3
+ # and presence validations for all mandatory fields.
4
+ # It also creates new validation context :release
5
+ #
6
+ # @todo :in validations for (multi)enums
7
+ # @todo link validations?
8
+ module Validations
9
+ module Base
10
+ def self.included(base)
11
+ base.extend(ClassMethods)
12
+ # Common validations for all Objs
13
+ base.class_eval do
14
+ validates :permalink, :format => { :with => /^[-_$.\/a-zA-Z0-9]*$/ }
15
+ validates :parent_obj_id, :numericality => { :only_integer => true }, :on => :create
16
+ validates :name, :presence => true, :on => :create
17
+ validates :obj_class, :presence => true, :on => :create
18
+ end
19
+ end
20
+
21
+ # Wraps around Reactor::Persistence::Base#release! and validates object
22
+ # in :release context before release. Raises exception when invalid.
23
+ # @raise [ActiveRecord::RecordInvalid] validations registered for :release failed
24
+ def release!
25
+ raise(ActiveRecord::RecordInvalid.new(self)) unless valid?(:release)
26
+ return super
27
+ end
28
+ end
29
+
30
+ module ClassMethods
31
+ def inherited(subclass)
32
+ super(subclass) # if you remove this line, y'll get TypeError: can't dup NilClass at some point
33
+
34
+ # Add validation for each mandatory attribute
35
+ mandatory_attrs = __mandatory_cms_attributes(subclass.name)
36
+ mandatory_attrs.each do |attr|
37
+ subclass.send(:validates_presence_of, attr.to_sym, :on => :release)
38
+ end if mandatory_attrs
39
+
40
+ cms_attributes = __cms_attributes(subclass).values
41
+ # Add validation for linklist & multienum [minSize/maxSize]
42
+ array_attributes= cms_attributes.select {|attr| ["linklist", "multienum"].include?(attr.attribute_type) }
43
+ array_attributes.each do |attr|
44
+ length_hash = {}
45
+ length_hash[:minimum] = attr.min_size if attr.min_size && "linklist" != attr.attribute_type # CMS ignores minimum for linklists.
46
+ length_hash[:maximum] = attr.max_size if attr.max_size
47
+
48
+ subclass.send(:validates, attr.attribute_name.to_sym, :length => length_hash, :on => :release) unless length_hash.empty?
49
+ end
50
+
51
+ subclass
52
+ end
53
+ end
54
+ end
55
+ end