Commit 32ea7f36badb289bbbb367a91ee537fa6fb4d25b
1 parent
06c13aae
Exists in
master
and in
28 other branches
Adding new hotspots
1. organization_members: allows plugins to extend the list of members of an organization. 2. has_permission?: allows plugins to add permission to some user to perform some action over a profile or environment. This hotspot only allows plugins to expand access, not to revoke access. 3. new_community_hidden_fields and enterprise_registration_hidden_fields: allows plugins to include new hidden fields in the following forms. * Also including a monkey patch to include some features from the gem fake_arel that requires activesupport-2.3.14 and activerecord-2.3.14. This them allows OR with scopes without loading them.
Showing
11 changed files
with
232 additions
and
3 deletions
Show diff stats
app/models/person.rb
... | ... | @@ -3,9 +3,19 @@ class Person < Profile |
3 | 3 | |
4 | 4 | acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)} |
5 | 5 | acts_as_accessor |
6 | + acts_as_having_hotspots | |
6 | 7 | |
7 | 8 | named_scope :members_of, lambda { |resource| { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.resource_type = ? AND role_assignments.resource_id = ?', resource.class.base_class.name, resource.id ] } } |
8 | 9 | |
10 | + def has_permission_with_plugins?(permission, profile) | |
11 | + permissions = [has_permission_without_plugins?(permission, profile)] | |
12 | + permissions += enabled_plugins.map do |plugin| | |
13 | + plugin.has_permission?(self, permission, profile) | |
14 | + end | |
15 | + permissions.include?(true) | |
16 | + end | |
17 | + alias_method_chain :has_permission?, :plugins | |
18 | + | |
9 | 19 | def memberships |
10 | 20 | Profile.memberships_of(self) |
11 | 21 | end | ... | ... |
app/models/profile.rb
... | ... | @@ -54,6 +54,7 @@ class Profile < ActiveRecord::Base |
54 | 54 | } |
55 | 55 | |
56 | 56 | acts_as_accessible |
57 | + acts_as_having_hotspots | |
57 | 58 | |
58 | 59 | named_scope :memberships_of, lambda { |person| { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.accessor_type = ? AND role_assignments.accessor_id = ?', person.class.base_class.name, person.id ] } } |
59 | 60 | #FIXME: these will work only if the subclass is already loaded |
... | ... | @@ -61,13 +62,24 @@ class Profile < ActiveRecord::Base |
61 | 62 | named_scope :communities, lambda { {:conditions => (Community.send(:subclasses).map(&:name) << 'Community').map { |klass| "profiles.type = '#{klass}'"}.join(" OR ")} } |
62 | 63 | |
63 | 64 | def members |
64 | - Person.members_of(self) | |
65 | + scopes = dispatch_scopes(:organization_members, self) | |
66 | + scopes << Person.members_of(self) | |
67 | + scopes.size == 1 ? scopes.first : Person.or_scope(scopes) | |
65 | 68 | end |
66 | 69 | |
67 | 70 | def members_count |
68 | - members.count('DISTINCT(profiles.id)') | |
71 | + members.count | |
69 | 72 | end |
70 | 73 | |
74 | + class << self | |
75 | + def count_with_distinct(*args) | |
76 | + options = args.last || {} | |
77 | + count_without_distinct(:id, {:distinct => true}.merge(options)) | |
78 | + end | |
79 | + alias_method_chain :count, :distinct | |
80 | + end | |
81 | + | |
82 | + | |
71 | 83 | def members_by_role(role) |
72 | 84 | Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', role.id]) |
73 | 85 | end | ... | ... |
app/models/profile_list_block.rb
app/views/enterprise_registration/basic_information.rhtml
... | ... | @@ -28,6 +28,12 @@ |
28 | 28 | <%= hidden_field_tag 'create_enterprise[target_id]', environment.id %> |
29 | 29 | <% end %> |
30 | 30 | |
31 | + <% @plugins.dispatch(:enterprise_registration_hidden_fields).each do |field| %> | |
32 | + <% field.each do |key, value| %> | |
33 | + <%= f.hidden_field(key, :value => value) %> | |
34 | + <% end %> | |
35 | + <% end %> | |
36 | + | |
31 | 37 | <% button_bar do %> |
32 | 38 | <%= submit_button('next', _('Next'), :cancel => {:profile => current_user.person.identifier, :action=>"enterprises", :controller=>"profile"}) %> |
33 | 39 | <% end %> | ... | ... |
app/views/memberships/new_community.rhtml
... | ... | @@ -16,6 +16,12 @@ |
16 | 16 | |
17 | 17 | <%= required f.text_field(:name) %> |
18 | 18 | |
19 | + <% @plugins.dispatch(:new_community_hidden_fields).each do |field| %> | |
20 | + <% field.each do |key, value| %> | |
21 | + <%= f.hidden_field(key, :value => value) %> | |
22 | + <% end %> | |
23 | + <% end %> | |
24 | + | |
19 | 25 | <%= render :partial => 'shared/organization_custom_fields', :locals => { :f => f, :object_name => 'community', :profile => @community } %> |
20 | 26 | |
21 | 27 | <% f.fields_for :image_builder, @community.image do |i| %> | ... | ... |
lib/noosfero/plugin.rb
... | ... | @@ -238,4 +238,29 @@ class Noosfero::Plugin |
238 | 238 | def comment_saved(comment) |
239 | 239 | end |
240 | 240 | |
241 | + # -> Extends organization list of members | |
242 | + # returns = An instance of ActiveRecord::NamedScope::Scope retrieved through | |
243 | + # Person.members_of method. | |
244 | + def organization_members(organization) | |
245 | + nil | |
246 | + end | |
247 | + | |
248 | + # -> Extends person permission access | |
249 | + # returns = boolean | |
250 | + def has_permission?(person, permission, target) | |
251 | + nil | |
252 | + end | |
253 | + | |
254 | + # -> Adds hidden_fields to the new community view | |
255 | + # returns = {key => value} | |
256 | + def new_community_hidden_fields | |
257 | + nil | |
258 | + end | |
259 | + | |
260 | + # -> Adds hidden_fields to the enterprise registration view | |
261 | + # returns = {key => value} | |
262 | + def enterprise_registration_hidden_fields | |
263 | + nil | |
264 | + end | |
265 | + | |
241 | 266 | end | ... | ... |
test/functional/enterprise_registration_controller_test.rb
... | ... | @@ -178,4 +178,27 @@ all_fixtures |
178 | 178 | get :index |
179 | 179 | assert_equal assigns(:create_enterprise).target, environment |
180 | 180 | end |
181 | + | |
182 | + should 'include hidden fields supplied by plugins on enterprise registration' do | |
183 | + class Plugin1 < Noosfero::Plugin | |
184 | + def enterprise_registration_hidden_fields | |
185 | + {'plugin1' => 'Plugin 1'} | |
186 | + end | |
187 | + end | |
188 | + | |
189 | + class Plugin2 < Noosfero::Plugin | |
190 | + def enterprise_registration_hidden_fields | |
191 | + {'plugin2' => 'Plugin 2'} | |
192 | + end | |
193 | + end | |
194 | + | |
195 | + environment = Environment.default | |
196 | + environment.enable_plugin(Plugin1.name) | |
197 | + environment.enable_plugin(Plugin2.name) | |
198 | + | |
199 | + get :index | |
200 | + | |
201 | + assert_tag :tag => 'input', :attributes => {:id => 'create_enterprise_plugin1', :type => 'hidden', :value => 'Plugin 1'} | |
202 | + assert_tag :tag => 'input', :attributes => {:id => 'create_enterprise_plugin2', :type => 'hidden', :value => 'Plugin 2'} | |
203 | + end | |
181 | 204 | end | ... | ... |
test/functional/memberships_controller_test.rb
... | ... | @@ -213,4 +213,27 @@ class MembershipsControllerTest < ActionController::TestCase |
213 | 213 | assert_no_tag :tag => 'textarea', :attributes => {:name => 'community[description]'} |
214 | 214 | end |
215 | 215 | |
216 | + should 'include hidden fields supplied by plugins on new community' do | |
217 | + class Plugin1 < Noosfero::Plugin | |
218 | + def new_community_hidden_fields | |
219 | + {'plugin1' => 'Plugin 1'} | |
220 | + end | |
221 | + end | |
222 | + | |
223 | + class Plugin2 < Noosfero::Plugin | |
224 | + def new_community_hidden_fields | |
225 | + {'plugin2' => 'Plugin 2'} | |
226 | + end | |
227 | + end | |
228 | + | |
229 | + environment = Environment.default | |
230 | + environment.enable_plugin(Plugin1.name) | |
231 | + environment.enable_plugin(Plugin2.name) | |
232 | + | |
233 | + get :new_community, :profile => profile.identifier | |
234 | + | |
235 | + assert_tag :tag => 'input', :attributes => {:id => 'community_plugin1', :type => 'hidden', :value => 'Plugin 1'} | |
236 | + assert_tag :tag => 'input', :attributes => {:id => 'community_plugin2', :type => 'hidden', :value => 'Plugin 2'} | |
237 | + end | |
238 | + | |
216 | 239 | end | ... | ... |
test/unit/person_test.rb
... | ... | @@ -1247,4 +1247,26 @@ class PersonTest < ActiveSupport::TestCase |
1247 | 1247 | assert !person.visible |
1248 | 1248 | assert_not_equal password, person.user.password |
1249 | 1249 | end |
1250 | + | |
1251 | + should 'allow plugins to extend person\'s permission access' do | |
1252 | + person = create_user('some-user').person | |
1253 | + class Plugin1 < Noosfero::Plugin | |
1254 | + def has_permission?(person, permission, target) | |
1255 | + true | |
1256 | + end | |
1257 | + end | |
1258 | + | |
1259 | + class Plugin2 < Noosfero::Plugin | |
1260 | + def has_permission?(person, permission, target) | |
1261 | + false | |
1262 | + end | |
1263 | + end | |
1264 | + | |
1265 | + e = Environment.default | |
1266 | + e.enable_plugin(Plugin1.name) | |
1267 | + e.enable_plugin(Plugin2.name) | |
1268 | + person.stubs('has_permission_without_plugins?').returns(false) | |
1269 | + | |
1270 | + assert person.has_permission?('bli', Profile.new) | |
1271 | + end | |
1250 | 1272 | end | ... | ... |
test/unit/profile_test.rb
... | ... | @@ -1768,6 +1768,37 @@ class ProfileTest < ActiveSupport::TestCase |
1768 | 1768 | end |
1769 | 1769 | end |
1770 | 1770 | |
1771 | + should 'merge members of plugins to original members' do | |
1772 | + original_community = fast_create(Community) | |
1773 | + community1 = fast_create(Community, :identifier => 'community1') | |
1774 | + community2 = fast_create(Community, :identifier => 'community2') | |
1775 | + original_member = fast_create(Person) | |
1776 | + plugin1_member = fast_create(Person) | |
1777 | + plugin2_member = fast_create(Person) | |
1778 | + original_community.add_member(original_member) | |
1779 | + community1.add_member(plugin1_member) | |
1780 | + community2.add_member(plugin2_member) | |
1781 | + | |
1782 | + class Plugin1 < Noosfero::Plugin | |
1783 | + def organization_members(profile) | |
1784 | + Person.members_of(Community.find_by_identifier('community1')) | |
1785 | + end | |
1786 | + end | |
1787 | + | |
1788 | + class Plugin2 < Noosfero::Plugin | |
1789 | + def organization_members(profile) | |
1790 | + Person.members_of(Community.find_by_identifier('community2')) | |
1791 | + end | |
1792 | + end | |
1793 | + | |
1794 | + original_community.stubs(:enabled_plugins).returns([Plugin1.new, Plugin2.new]) | |
1795 | + | |
1796 | + assert_includes original_community.members, original_member | |
1797 | + assert_includes original_community.members, plugin1_member | |
1798 | + assert_includes original_community.members, plugin2_member | |
1799 | + assert 3, original_community.members.count | |
1800 | + end | |
1801 | + | |
1771 | 1802 | private |
1772 | 1803 | |
1773 | 1804 | def assert_invalid_identifier(id) | ... | ... |
vendor/plugins/monkey_patches/methods_from_fake_arel/init.rb
0 → 100644
... | ... | @@ -0,0 +1,71 @@ |
1 | +# monkey patch to add fake_arel select, or_scope and where methods | |
2 | +# this gem requires activesupport-2.3.14 and activerecord-2.3.14 | |
3 | +# | |
4 | +# https://github.com/gammons/fake_arel | |
5 | + | |
6 | +module Rails3Finder | |
7 | + def self.included(base) | |
8 | + base.class_eval do | |
9 | + | |
10 | + # the default named scopes | |
11 | + named_scope :offset, lambda {|offset| {:offset => offset}} | |
12 | + named_scope :limit, lambda {|limit| {:limit => limit}} | |
13 | + named_scope :includes, lambda { |*includes| { :include => includes }} | |
14 | + named_scope :order, lambda {|*order| {:order => order.join(',') }} | |
15 | + named_scope :joins, lambda {|*join| {:joins => join } if join[0]} | |
16 | + named_scope :from, lambda {|*from| {:from => from }} | |
17 | + named_scope :having, lambda {|*having| {:having => having }} | |
18 | + named_scope :group, lambda {|*group| {:group => group.join(',') }} | |
19 | + named_scope :readonly, lambda {|readonly| {:readonly => readonly }} | |
20 | + named_scope :lock, lambda {|lock| {:lock => lock }} | |
21 | + | |
22 | + def self.select(value = Proc.new) | |
23 | + if block_given? | |
24 | + all.select {|*block_args| value.call(*block_args) } | |
25 | + else | |
26 | + self.scoped(:select => Array.wrap(value).join(',')) | |
27 | + end | |
28 | + end | |
29 | + | |
30 | + __where_fn = lambda do |*where| | |
31 | + if where.is_a?(Array) and where.size == 1 | |
32 | + {:conditions => where.first} | |
33 | + else | |
34 | + {:conditions => where} | |
35 | + end | |
36 | + end | |
37 | + | |
38 | + named_scope :where, __where_fn | |
39 | + | |
40 | + # Use carefully this method! It might get lost with different classes | |
41 | + # scopes or different types of joins. | |
42 | + def self.or_scope(*scopes) | |
43 | + where = [] | |
44 | + joins = [] | |
45 | + includes = [] | |
46 | + | |
47 | + # for some reason, flatten is actually executing the scope | |
48 | + scopes = scopes[0] if scopes.size == 1 | |
49 | + scopes.each do |s| | |
50 | + s = s.proxy_options | |
51 | + begin | |
52 | + where << merge_conditions(s[:conditions]) | |
53 | + rescue NoMethodError | |
54 | + # I am ActiveRecord::Base. Only my subclasses define merge_conditions: | |
55 | + where << subclasses.first.merge_conditions(s[:conditions]) | |
56 | + end | |
57 | + #where << merge_conditions(s[:conditions]) | |
58 | + joins << s[:joins] unless s[:joins].nil? | |
59 | + includes << s[:include] unless s[:include].nil? | |
60 | + end | |
61 | + scoped = self | |
62 | + scoped = scoped.select("DISTINCT #{self.table_name}.*") | |
63 | + scoped = scoped.includes(includes.uniq.flatten) unless includes.blank? | |
64 | + scoped = scoped.joins(joins.uniq.flatten) unless joins.blank? | |
65 | + scoped.where(where.join(" OR ")) | |
66 | + end | |
67 | + end | |
68 | + end | |
69 | +end | |
70 | + | |
71 | +ActiveRecord::Base.send :include, Rails3Finder | ... | ... |