Commit cbac61f7f0457ee547bb321989013a25b973efe2

Authored by Antonio Terceiro
2 parents dff781c5 b5d67b0a

Merge branch 'master' into antispam

Conflicts:
	lib/noosfero/plugin/context.rb
	lib/noosfero/plugin/manager.rb
	test/unit/plugin_manager_test.rb
Showing 38 changed files with 1112 additions and 47 deletions   Show diff stats
app/controllers/application_controller.rb
1 class ApplicationController < ActionController::Base 1 class ApplicationController < ActionController::Base
2 2
3 before_filter :setup_multitenancy 3 before_filter :setup_multitenancy
  4 + before_filter :detect_stuff_by_domain
  5 + before_filter :init_noosfero_plugins
4 6
5 include ApplicationHelper 7 include ApplicationHelper
6 layout :get_layout 8 layout :get_layout
@@ -51,8 +53,6 @@ class ApplicationController &lt; ActionController::Base @@ -51,8 +53,6 @@ class ApplicationController &lt; ActionController::Base
51 53
52 include NeedsProfile 54 include NeedsProfile
53 55
54 - before_filter :detect_stuff_by_domain  
55 - before_filter :init_noosfero_plugins  
56 attr_reader :environment 56 attr_reader :environment
57 57
58 before_filter :load_terminology 58 before_filter :load_terminology
app/helpers/application_helper.rb
@@ -1352,4 +1352,53 @@ module ApplicationHelper @@ -1352,4 +1352,53 @@ module ApplicationHelper
1352 :style => 'margin-top: 1em' 1352 :style => 'margin-top: 1em'
1353 ) 1353 )
1354 end 1354 end
  1355 +
  1356 + def token_input_field_tag(name, element_id, search_action, options = {}, text_field_options = {}, html_options = {})
  1357 + options[:min_chars] ||= 3
  1358 + options[:hint_text] ||= _("Type in a search term")
  1359 + options[:no_results_text] ||= _("No results")
  1360 + options[:searching_text] ||= _("Searching...")
  1361 + options[:search_delay] ||= 1000
  1362 + options[:prevent_duplicates] ||= true
  1363 + options[:backspace_delete_item] ||= false
  1364 + options[:focus] ||= false
  1365 + options[:avoid_enter] ||= true
  1366 + options[:on_result] ||= 'null'
  1367 + options[:on_add] ||= 'null'
  1368 + options[:on_delete] ||= 'null'
  1369 + options[:on_ready] ||= 'null'
  1370 +
  1371 + result = text_field_tag(name, nil, text_field_options.merge(html_options.merge({:id => element_id})))
  1372 + result +=
  1373 + "
  1374 + <script type='text/javascript'>
  1375 + jQuery('##{element_id}')
  1376 + .tokenInput('#{url_for(search_action)}', {
  1377 + minChars: #{options[:min_chars].to_json},
  1378 + prePopulate: #{options[:pre_populate].to_json},
  1379 + hintText: #{options[:hint_text].to_json},
  1380 + noResultsText: #{options[:no_results_text].to_json},
  1381 + searchingText: #{options[:searching_text].to_json},
  1382 + searchDelay: #{options[:serach_delay].to_json},
  1383 + preventDuplicates: #{options[:prevent_duplicates].to_json},
  1384 + backspaceDeleteItem: #{options[:backspace_delete_item].to_json},
  1385 + queryParam: #{name.to_json},
  1386 + tokenLimit: #{options[:token_limit].to_json},
  1387 + onResult: #{options[:on_result]},
  1388 + onAdd: #{options[:on_add]},
  1389 + onDelete: #{options[:on_delete]},
  1390 + onReady: #{options[:on_ready]},
  1391 + })
  1392 + "
  1393 + result += options[:focus] ? ".focus();" : ";"
  1394 + if options[:avoid_enter]
  1395 + result += "jQuery('#token-input-#{element_id}')
  1396 + .live('keydown', function(event){
  1397 + if(event.keyCode == '13') return false;
  1398 + });"
  1399 + end
  1400 + result += "</script>"
  1401 + result
  1402 + end
  1403 +
1355 end 1404 end
app/models/person.rb
@@ -22,7 +22,22 @@ class Person &lt; Profile @@ -22,7 +22,22 @@ class Person &lt; Profile
22 super 22 super
23 end 23 end
24 24
25 - 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 ] } } 25 + acts_as_having_hotspots
  26 +
  27 + named_scope :members_of, lambda { |resources|
  28 + resources = [resources] if !resources.kind_of?(Array)
  29 + conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ')
  30 + { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => [conditions] }
  31 + }
  32 +
  33 + def has_permission_with_plugins?(permission, profile)
  34 + permissions = [has_permission_without_plugins?(permission, profile)]
  35 + permissions += enabled_plugins.map do |plugin|
  36 + plugin.has_permission?(self, permission, profile)
  37 + end
  38 + permissions.include?(true)
  39 + end
  40 + alias_method_chain :has_permission?, :plugins
26 41
27 def memberships 42 def memberships
28 Profile.memberships_of(self) 43 Profile.memberships_of(self)
app/models/profile.rb
@@ -60,6 +60,7 @@ class Profile &lt; ActiveRecord::Base @@ -60,6 +60,7 @@ class Profile &lt; ActiveRecord::Base
60 } 60 }
61 61
62 acts_as_accessible 62 acts_as_accessible
  63 + acts_as_having_hotspots
63 64
64 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 ] } } 65 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 ] } }
65 #FIXME: these will work only if the subclass is already loaded 66 #FIXME: these will work only if the subclass is already loaded
@@ -68,13 +69,24 @@ class Profile &lt; ActiveRecord::Base @@ -68,13 +69,24 @@ class Profile &lt; ActiveRecord::Base
68 named_scope :templates, :conditions => {:is_template => true} 69 named_scope :templates, :conditions => {:is_template => true}
69 70
70 def members 71 def members
71 - Person.members_of(self) 72 + scopes = dispatch_scopes(:organization_members, self)
  73 + scopes << Person.members_of(self)
  74 + scopes.size == 1 ? scopes.first : Person.or_scope(scopes)
72 end 75 end
73 76
74 def members_count 77 def members_count
75 - members.count('DISTINCT(profiles.id)') 78 + members.count
76 end 79 end
77 80
  81 + class << self
  82 + def count_with_distinct(*args)
  83 + options = args.last || {}
  84 + count_without_distinct(:id, {:distinct => true}.merge(options))
  85 + end
  86 + alias_method_chain :count, :distinct
  87 + end
  88 +
  89 +
78 def members_by_role(role) 90 def members_by_role(role)
79 Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', role.id]) 91 Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', role.id])
80 end 92 end
app/models/profile_list_block.rb
@@ -25,7 +25,7 @@ class ProfileListBlock &lt; Block @@ -25,7 +25,7 @@ class ProfileListBlock &lt; Block
25 end 25 end
26 26
27 def profile_count 27 def profile_count
28 - profiles.visible.count('DISTINCT(profiles.id)') 28 + profiles.visible.count
29 end 29 end
30 30
31 # the title of the block. Probably will be overriden in subclasses. 31 # the title of the block. Probably will be overriden in subclasses.
app/views/enterprise_registration/basic_information.rhtml
@@ -28,6 +28,12 @@ @@ -28,6 +28,12 @@
28 <%= hidden_field_tag 'create_enterprise[target_id]', environment.id %> 28 <%= hidden_field_tag 'create_enterprise[target_id]', environment.id %>
29 <% end %> 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 <%= template_options(Enterprise, 'create_enterprise')%> 37 <%= template_options(Enterprise, 'create_enterprise')%>
32 38
33 <% button_bar do %> 39 <% button_bar do %>
app/views/memberships/index.rhtml
@@ -8,30 +8,6 @@ @@ -8,30 +8,6 @@
8 <%= button :back, _('Go back'), :controller => 'profile_editor' %> 8 <%= button :back, _('Go back'), :controller => 'profile_editor' %>
9 <% end %> 9 <% end %>
10 10
11 -<ul>  
12 -<% for membership in @memberships %>  
13 - <li>  
14 - <div class='common-profile-list-block'>  
15 - <%= profile_image_link(membership, :portrait, 'div') %>  
16 - </div>  
17 - <span class='profile-details'>  
18 - <strong><%= membership.name %></strong><br/>  
19 - <%= _('Role: %s') % rolename_for(profile, membership) %> <br/>  
20 - <%= _('Type: %s') % getterm(membership.class.identification) %> <br/>  
21 - <%= _('Description: %s') % membership.description + '<br/>' if membership.community? %>  
22 - <%= _('Members: %s') % membership.members_count.to_s %> <br/>  
23 - <%= _('Created at: %s') % show_date(membership.created_at) unless membership.enterprise? %> <br/>  
24 - <% button_bar do %>  
25 - <%= button 'menu-ctrl-panel', _('Control panel of this group'), membership.admin_url %>  
26 - <%= button 'menu-logout', _('Leave community'), membership.leave_url(true), :class => 'leave-community' %>  
27 - <% if (membership.community? && user.has_permission?(:destroy_profile, membership)) %>  
28 - <%= button 'delete', _('Remove'), { :controller => 'profile_editor', :action => 'destroy_profile', :profile => membership.identifier } %>  
29 - <% end %>  
30 - <% end %>  
31 - </span>  
32 - <br class="may-clear" />  
33 - </li>  
34 -<% end %>  
35 -</ul> 11 +<%= render :partial => 'shared/list_groups', :locals => {:groups => @memberships} %>
36 12
37 </div> 13 </div>
app/views/memberships/new_community.rhtml
@@ -16,6 +16,12 @@ @@ -16,6 +16,12 @@
16 16
17 <%= required f.text_field(:name) %> 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 <%= render :partial => 'shared/organization_custom_fields', :locals => { :f => f, :object_name => 'community', :profile => @community } %> 25 <%= render :partial => 'shared/organization_custom_fields', :locals => { :f => f, :object_name => 'community', :profile => @community } %>
20 26
21 <% f.fields_for :image_builder, @community.image do |i| %> 27 <% f.fields_for :image_builder, @community.image do |i| %>
app/views/shared/_list_groups.html.erb 0 → 100644
@@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
  1 +<ul id="groups-list">
  2 +<% for group in groups %>
  3 + <li>
  4 + <div class='common-profile-list-block'>
  5 + <%= profile_image_link(group, :portrait, 'div') %>
  6 + </div>
  7 + <span class='profile-details'>
  8 + <strong><%= group.name %></strong><br/>
  9 + <%= _('Role: %s') % rolename_for(profile, group) + '<br/>' if profile.role_assignments.find_by_resource_id(group.id) %>
  10 + <%= _('Type: %s') % getterm(group.class.identification) %> <br/>
  11 + <%= _('Description: %s') % group.description + '<br/>' if group.community? %>
  12 + <%= _('Members: %s') % group.members_count.to_s %> <br/>
  13 + <%= _('Created at: %s') % show_date(group.created_at) unless group.enterprise? %> <br/>
  14 + <% button_bar do %>
  15 + <%= button 'menu-ctrl-panel', _('Control panel of this group'), group.admin_url %>
  16 + <%= button 'menu-logout', _('Leave community'), group.leave_url(true), :class => 'leave-community' %>
  17 + <% if (group.community? && user.has_permission?(:destroy_profile, group)) %>
  18 + <%= button 'delete', _('Remove'), { :controller => 'profile_editor', :action => 'destroy_profile', :profile => group.identifier } %>
  19 + <% end %>
  20 + <% end %>
  21 + </span>
  22 + <br class="may-clear" />
  23 + </li>
  24 +<% end %>
  25 +</ul>
  26 +
config/initializers/plugins.rb
1 require 'noosfero/plugin' 1 require 'noosfero/plugin'
  2 +require 'noosfero/plugin/acts_as_having_hotspots'
2 require 'noosfero/plugin/manager' 3 require 'noosfero/plugin/manager'
3 require 'noosfero/plugin/active_record' 4 require 'noosfero/plugin/active_record'
4 require 'noosfero/plugin/mailer_base' 5 require 'noosfero/plugin/mailer_base'
lib/noosfero/plugin.rb
@@ -293,4 +293,29 @@ class Noosfero::Plugin @@ -293,4 +293,29 @@ class Noosfero::Plugin
293 nil 293 nil
294 end 294 end
295 295
  296 + # -> Extends organization list of members
  297 + # returns = An instance of ActiveRecord::NamedScope::Scope retrieved through
  298 + # Person.members_of method.
  299 + def organization_members(organization)
  300 + nil
  301 + end
  302 +
  303 + # -> Extends person permission access
  304 + # returns = boolean
  305 + def has_permission?(person, permission, target)
  306 + nil
  307 + end
  308 +
  309 + # -> Adds hidden_fields to the new community view
  310 + # returns = {key => value}
  311 + def new_community_hidden_fields
  312 + nil
  313 + end
  314 +
  315 + # -> Adds hidden_fields to the enterprise registration view
  316 + # returns = {key => value}
  317 + def enterprise_registration_hidden_fields
  318 + nil
  319 + end
  320 +
296 end 321 end
lib/noosfero/plugin/acts_as_having_hotspots.rb 0 → 100644
@@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
  1 +module ActsAsHavingHotspots
  2 + module ClassMethods
  3 + # Adding this feature to a class demands that it defines an instance method
  4 + # 'environment' that returns the environment associated with the instance.
  5 + def acts_as_having_hotspots
  6 + send :include, InstanceMethods
  7 + end
  8 +
  9 + module InstanceMethods
  10 + # Dispatches +event+ to each enabled plugin and collect the results.
  11 + #
  12 + # Returns an Array containing the objects returned by the event method in
  13 + # each plugin. This array is compacted (i.e. nils are removed) and flattened
  14 + # (i.e. elements of arrays are added to the resulting array). For example, if
  15 + # the enabled plugins return 1, 0, nil, and [1,2,3], then this method will
  16 + # return [1,0,1,2,3]
  17 + #
  18 + def dispatch(event, *args)
  19 + enabled_plugins.map { |plugin| plugin.send(event, *args) }.compact.flatten
  20 + end
  21 +
  22 + # Dispatch without flatten since scopes are executed if you run flatten on them
  23 + def dispatch_scopes(event, *args)
  24 + enabled_plugins.map { |plugin| plugin.send(event, *args) }.compact
  25 + end
  26 +
  27 + def enabled_plugins
  28 + Thread.current[:enabled_plugins] ||= (Noosfero::Plugin.all & environment.enabled_plugins).map do |plugin_name|
  29 + plugin = plugin_name.constantize.new
  30 + plugin.context = context
  31 + plugin
  32 + end
  33 + end
  34 +
  35 + if !method_defined?(:context)
  36 + define_method(:context) do
  37 + Noosfero::Plugin::Context.new
  38 + end
  39 + end
  40 + end
  41 + end
  42 +end
  43 +
  44 +ActiveRecord::Base.send(:extend, ActsAsHavingHotspots::ClassMethods)
lib/noosfero/plugin/manager.rb
@@ -8,6 +8,7 @@ class Noosfero::Plugin::Manager @@ -8,6 +8,7 @@ class Noosfero::Plugin::Manager
8 @constantize = context 8 @constantize = context
9 end 9 end
10 10
  11 + delegate :environment, :to => :context
11 delegate :each, :to => :enabled_plugins 12 delegate :each, :to => :enabled_plugins
12 include Enumerable 13 include Enumerable
13 14
plugins/sub_organizations/controllers/sub_organizations_plugin_myprofile_controller.rb 0 → 100644
@@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
  1 +class SubOrganizationsPluginMyprofileController < MyProfileController
  2 + append_view_path File.join(File.dirname(__FILE__) + '/../views')
  3 +
  4 + before_filter :organizations_only
  5 + protect 'edit_profile', :profile
  6 +
  7 + def index
  8 + @children = SubOrganizationsPlugin::Relation.children(profile)
  9 + @tokenized_children = prepare_to_token_input(@children)
  10 + @pending_children = SubOrganizationsPlugin::ApprovePaternityRelation.pending_children(profile)
  11 + if request.post?
  12 + begin
  13 + original = SubOrganizationsPlugin::Relation.children(profile)
  14 + requested = Organization.find(params[:q].split(','))
  15 + added = requested - original
  16 + removed = original - requested
  17 + added.each do |organization|
  18 + if current_person.has_permission?('perform_task',organization)
  19 + SubOrganizationsPlugin::Relation.add_children(profile, organization)
  20 + else
  21 + SubOrganizationsPlugin::ApprovePaternity.create!(:requestor => user, :temp_parent_type => profile.class.name, :temp_parent_id => profile.id, :target => organization)
  22 + end
  23 + end
  24 + SubOrganizationsPlugin::Relation.remove_children(profile,removed)
  25 + session[:notice] = _('Sub-organizations updated')
  26 + rescue Exception => exception
  27 + logger.error(exception.to_s)
  28 + session[:notice] = _('Sub-organizations could not be updated')
  29 + end
  30 + redirect_to :action => :index
  31 + end
  32 + end
  33 +
  34 + def search_organization
  35 + render :text => prepare_to_token_input(environment.organizations.find(:all, :conditions =>
  36 + ["(LOWER(name) LIKE ? OR LOWER(identifier) LIKE ?)
  37 + AND (identifier NOT LIKE ?) AND (id != ?)",
  38 + "%#{params[:q]}%", "%#{params[:q]}%", "%_template", profile.id]).
  39 + select { |organization|
  40 + SubOrganizationsPlugin::Relation.children(organization).blank? &&
  41 + !SubOrganizationsPlugin::ApprovePaternityRelation.pending_children(profile).include?(organization)
  42 + }).to_json
  43 + end
  44 +
  45 + private
  46 +
  47 + def organizations_only
  48 + render_not_found if !profile.organization?
  49 + end
  50 +
  51 + def prepare_to_token_input(array)
  52 + array.map { |object| {:id => object.id, :name => object.name} }
  53 + end
  54 +end
plugins/sub_organizations/db/migrate/20120530173629_create_relation.rb 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +class CreateRelation < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :sub_organizations_plugin_relations do |t|
  4 + t.references :parent, :polymorphic => true
  5 + t.references :child, :polymorphic => true
  6 + end
  7 + end
  8 +
  9 + def self.down
  10 + drop_table :sub_organizations_plugin_relations
  11 + end
  12 +end
plugins/sub_organizations/db/migrate/20120615202224_approve_paternity_relation.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class ApprovePaternityRelation < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :sub_organizations_plugin_approve_paternity_relations do |t|
  4 + t.references :task
  5 + t.references :parent, :polymorphic => true
  6 + t.references :child, :polymorphic => true
  7 + end
  8 + end
  9 +
  10 + def self.down
  11 + drop_table :sub_organizations_plugin_approve_paternity_relations
  12 + end
  13 +end
plugins/sub_organizations/lib/sub_organizations_plugin.rb 0 → 100644
@@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
  1 +require_dependency 'sub_organizations_plugin/ext/organization'
  2 +require_dependency 'sub_organizations_plugin/ext/create_enterprise'
  3 +
  4 +class SubOrganizationsPlugin < Noosfero::Plugin
  5 +
  6 + def self.plugin_name
  7 + _("Sub-groups")
  8 + end
  9 +
  10 + def self.plugin_description
  11 + _("Adds the ability for groups to have sub-groups.")
  12 + end
  13 +
  14 + def control_panel_buttons
  15 + if context.profile.organization? && SubOrganizationsPlugin::Relation.parents(context.profile).blank?
  16 + { :title => _('Manage sub-groups'), :icon => 'groups', :url => {:controller => 'sub_organizations_plugin_myprofile'} }
  17 + end
  18 + end
  19 +
  20 + def stylesheet?
  21 + true
  22 + end
  23 +
  24 + def organization_members(organization)
  25 + children = SubOrganizationsPlugin::Relation.children(organization)
  26 + Person.members_of(children) if children.present?
  27 + end
  28 +
  29 + def has_permission?(person, permission, target)
  30 + if !target.kind_of?(Environment) && target.organization?
  31 + SubOrganizationsPlugin::Relation.parents(target).map do |parent|
  32 + person.has_permission_without_plugins?(permission, parent)
  33 + end.include?(true)
  34 + end
  35 + end
  36 +
  37 + def new_community_hidden_fields
  38 + parent_to_be = context.params[:sub_organizations_plugin_parent_to_be]
  39 + {'sub_organizations_plugin_parent_to_be' => parent_to_be} if parent_to_be.present?
  40 + end
  41 +
  42 + def enterprise_registration_hidden_fields
  43 + parent_to_be = context.params[:sub_organizations_plugin_parent_to_be]
  44 + {'sub_organizations_plugin_parent_to_be' => parent_to_be} if parent_to_be.present?
  45 + end
  46 +end
plugins/sub_organizations/lib/sub_organizations_plugin/approve_paternity.rb 0 → 100644
@@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
  1 +class SubOrganizationsPlugin::ApprovePaternity < Task
  2 + validates_presence_of :requestor, :target
  3 +
  4 + settings_items :temp_parent_id
  5 + settings_items :temp_parent_type
  6 +
  7 + after_create do |task|
  8 + r = SubOrganizationsPlugin::ApprovePaternityRelation.create!(:task => task, :parent => task.temp_parent, :child => task.target)
  9 + end
  10 +
  11 + def temp_parent
  12 + temp_parent_type.constantize.find(temp_parent_id)
  13 + end
  14 +
  15 + def parent
  16 + SubOrganizationsPlugin::ApprovePaternityRelation.parent(self)
  17 + end
  18 +
  19 + def title
  20 + _("Paternity request")
  21 + end
  22 +
  23 + def linked_subject
  24 + {:text => parent.name, :url => parent.url}
  25 + end
  26 +
  27 + def information
  28 + {:message => _('%{requestor} wants to add this organization as a sub-organization of %{linked_subject}.')}
  29 + end
  30 +
  31 + def reject_details
  32 + true
  33 + end
  34 +
  35 + def icon
  36 + {:type => :profile_image, :profile => parent, :url => parent.url}
  37 + end
  38 +
  39 + def task_created_message
  40 + ('%{requestor} wants to add your organization %{target} as a sub-organization of %{parent}.') % {:requestor => requestor.name, :target => target.name, :parent => temp_parent.name}
  41 + end
  42 +
  43 + def task_finished_message
  44 + ('%{target} accepted your request to add it as a sub-organization of %{parent}.') % {:target => target.name, :parent => parent.name}
  45 + end
  46 +
  47 + def task_cancelled_message
  48 + ('%{target} refused your request to add it as a sub-organization of %{parent}.') % {:target => target.name, :parent => parent.name}
  49 + end
  50 +
  51 + protected
  52 +
  53 + def perform
  54 + SubOrganizationsPlugin::Relation.add_children(parent, target)
  55 + end
  56 +
  57 +end
plugins/sub_organizations/lib/sub_organizations_plugin/approve_paternity_relation.rb 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +class SubOrganizationsPlugin::ApprovePaternityRelation < Noosfero::Plugin::ActiveRecord
  2 + belongs_to :task
  3 + belongs_to :parent, :polymorphic => true
  4 + belongs_to :child, :polymorphic => true
  5 +
  6 + validates_presence_of :task, :parent, :child
  7 +
  8 + class << self
  9 + def parent(task)
  10 + find_by_task_id(task.id).parent
  11 + end
  12 +
  13 + def pending_children(parent)
  14 + options = {
  15 + :select => "distinct profiles.*",
  16 + :joins => "inner join sub_organizations_plugin_approve_paternity_relations as relations on profiles.id=relations.child_id inner join tasks on relations.task_id=tasks.id",
  17 + :conditions => ["relations.parent_id = ? AND tasks.status = 1", parent.id]
  18 + }
  19 + ActiveRecord::NamedScope::Scope.new(Organization, options)
  20 + end
  21 + end
  22 +
  23 +end
plugins/sub_organizations/lib/sub_organizations_plugin/ext/create_enterprise.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +require_dependency 'create_enterprise'
  2 +
  3 +class CreateEnterprise
  4 + settings_items :sub_organizations_plugin_parent_to_be
  5 +end
plugins/sub_organizations/lib/sub_organizations_plugin/ext/organization.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +require_dependency 'organization'
  2 +class Organization
  3 + settings_items :sub_organizations_plugin_parent_to_be
  4 +
  5 + after_create do |organization|
  6 + if organization.sub_organizations_plugin_parent_to_be.present?
  7 + parent = Organization.find(organization.sub_organizations_plugin_parent_to_be)
  8 + SubOrganizationsPlugin::Relation.add_children(parent,organization)
  9 + end
  10 + end
  11 +
  12 + FIELDS << 'sub_organizations_plugin_parent_to_be'
  13 +end
plugins/sub_organizations/lib/sub_organizations_plugin/relation.rb 0 → 100644
@@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
  1 +class SubOrganizationsPlugin::Relation < Noosfero::Plugin::ActiveRecord
  2 + belongs_to :parent, :polymorphic => true
  3 + belongs_to :child, :polymorphic => true
  4 +
  5 + validates_presence_of :parent, :child
  6 + validate :no_self_reference
  7 + validate :no_cyclical_reference, :if => 'parent.present? && child.present?'
  8 + validate :no_multi_level, :if => 'parent.present? && child.present?'
  9 +
  10 + def no_self_reference
  11 + errors.add(:child, _('self-reference is not allowed.')) if parent == child
  12 + end
  13 +
  14 + def no_cyclical_reference
  15 + if self.class.children(child).include?(parent)
  16 + errors.add(:child, _('cyclical reference is not allowed.'))
  17 + end
  18 + end
  19 +
  20 + def no_multi_level
  21 + if self.class.parents(parent).present? || self.class.children(child).present?
  22 + errors.add(:child, _('multi-level paternity is not allowed.'))
  23 + end
  24 + end
  25 +
  26 + class << self
  27 + def children(parent)
  28 + options = {
  29 + :select => "profiles.*",
  30 + :joins => "inner join sub_organizations_plugin_relations as relations on profiles.id=relations.child_id",
  31 + :conditions => ["relations.parent_id = ?", parent.id]
  32 + }
  33 + ActiveRecord::NamedScope::Scope.new(Organization, options)
  34 + end
  35 +
  36 + def parents(child)
  37 + options = {
  38 + :select => "profiles.*",
  39 + :joins => "inner join sub_organizations_plugin_relations as relations on profiles.id=relations.parent_id",
  40 + :conditions => ["relations.child_id = ?", child.id]
  41 + }
  42 + ActiveRecord::NamedScope::Scope.new(Organization, options)
  43 + end
  44 +
  45 + def add_children(parent, *children)
  46 + children.each {|child| create!(:parent => parent, :child => child)}
  47 + end
  48 +
  49 + def remove_children(parent, *children)
  50 + children.flatten.each {|child| find_by_parent_id_and_child_id(parent.id, child.id).destroy}
  51 + end
  52 + end
  53 +
  54 +end
plugins/sub_organizations/public/style.css 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +#content .token-input-list {
  2 + margin-bottom: 30px;
  3 +}
plugins/sub_organizations/test/functional/sub_organizations_plugin_myprofile_controller_test.rb 0 → 100644
@@ -0,0 +1,113 @@ @@ -0,0 +1,113 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +require File.dirname(__FILE__) + '/../../controllers/sub_organizations_plugin_myprofile_controller'
  3 +
  4 +# Re-raise errors caught by the controller.
  5 +class SubOrganizationsPluginMyprofileController; def rescue_action(e) raise e end; end
  6 +
  7 +class SubOrganizationsPluginMyprofileControllerTest < ActionController::TestCase
  8 + def setup
  9 + @controller = SubOrganizationsPluginMyprofileController.new
  10 + @request = ActionController::TestRequest.new
  11 + @response = ActionController::TestResponse.new
  12 + @organization = Organization.create!(:name => 'My Organization', :identifier => 'my-organization')
  13 + @person = create_user('person').person
  14 + @organization.add_admin(@person)
  15 + login_as(@person.user.login)
  16 + e = Environment.default
  17 + e.enable_plugin('SubOrganizationsPlugin')
  18 + e.save!
  19 + end
  20 +
  21 + attr_accessor :person, :organization
  22 +
  23 + should 'search organizations' do
  24 + # Should list if match name
  25 + o1 = fast_create(Organization, :name => 'sample organization 1')
  26 + # Should be case insensitive
  27 + o2 = fast_create(Organization, :name => 'SaMpLe OrGaNiZaTiOn 2')
  28 + # Should not list if don't match name
  29 + o3 = fast_create(Organization, :name => 'blo')
  30 + # Should not list if is has children
  31 + child = fast_create(Organization)
  32 + o4 = fast_create(Organization, :name => 'sample organization 4')
  33 + SubOrganizationsPlugin::Relation.add_children(o4, child)
  34 + # Should not list if is there is a task to add it
  35 + o5 = fast_create(Organization, :name => 'sample enterprise 5')
  36 + SubOrganizationsPlugin::ApprovePaternity.create!(:requestor => person, :target => o5, :temp_parent_id => organization.id, :temp_parent_type => organization.class.name)
  37 + # Should search by identifier
  38 + o6 = fast_create(Organization, :name => 'Bla', :identifier => 'sample-enterprise-6')
  39 + # Should not list itself
  40 + organization.name = 'Sample Organization'
  41 + organization.save!
  42 +
  43 + get :search_organization, :profile => organization.identifier, :q => 'sampl'
  44 +
  45 + assert_match /#{o1.name}/, @response.body
  46 + assert_match /#{o2.name}/, @response.body
  47 + assert_no_match /#{o3.name}/, @response.body
  48 + assert_no_match /#{o4.name}/, @response.body
  49 + assert_no_match /#{o5.name}/, @response.body
  50 + assert_match /#{o6.name}/, @response.body
  51 + assert_no_match /#{organization.name}/, @response.body
  52 + end
  53 +
  54 + should 'update sub-organizations list' do
  55 + org1 = fast_create(Organization)
  56 + org2 = fast_create(Organization)
  57 + org3 = fast_create(Organization)
  58 + org4 = fast_create(Organization)
  59 + SubOrganizationsPlugin::Relation.add_children(organization, org1, org2)
  60 +
  61 + post :index, :profile => organization.identifier, :q => [org2,org3,org4].map(&:id).join(',')
  62 +
  63 + children = SubOrganizationsPlugin::Relation.children(organization)
  64 + assert_not_includes children, org1
  65 + assert_includes children, org2
  66 + assert_not_includes children, org3
  67 + assert_not_includes children, org4
  68 +
  69 + SubOrganizationsPlugin::ApprovePaternity.all.map(&:finish)
  70 +
  71 + children = SubOrganizationsPlugin::Relation.children(organization)
  72 + assert_not_includes children, org1
  73 + assert_includes children, org2
  74 + assert_includes children, org3
  75 + assert_includes children, org4
  76 + end
  77 +
  78 + should 'establish relation right away if the user can perform tasks on the sub-organization' do
  79 + org1 = fast_create(Organization)
  80 + org2 = fast_create(Organization)
  81 + org2.add_admin(person)
  82 +
  83 + assert_difference SubOrganizationsPlugin::ApprovePaternity, :count, 1 do
  84 + post :index, :profile => organization.identifier, :q => [org1,org2].map(&:id).join(',')
  85 + end
  86 + assert_includes SubOrganizationsPlugin::Relation.children(organization), org2
  87 + end
  88 +
  89 + should 'not access index if dont have permission' do
  90 + member = create_user('member').person
  91 + organization.add_member(member)
  92 +
  93 + login_as(member.identifier)
  94 + get :index, :profile => organization.identifier
  95 +
  96 + assert_response 403
  97 + assert_template 'access_denied.rhtml'
  98 + end
  99 +
  100 + should 'not search organizations if dont have permission' do
  101 + member = create_user('member').person
  102 + organization.add_member(member)
  103 +
  104 + login_as(member.identifier)
  105 +
  106 + org1 = fast_create(Organization, :name => 'sample organization 1')
  107 + get :search_organization, :profile => organization.identifier, :q => 'sampl'
  108 +
  109 + assert_response 403
  110 + assert_template 'access_denied.rhtml'
  111 + end
  112 +
  113 +end
plugins/sub_organizations/test/unit/sub_organizations_plugin/approve_paternity_relation_test.rb 0 → 100644
@@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
  1 +require File.dirname(__FILE__) + '/../../../../../test/test_helper'
  2 +
  3 +class SubOrganizationsPlugin::ApprovePaternityRelationTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @requestor = create_user('some-user').person
  7 + end
  8 +
  9 + attr_reader :requestor
  10 +
  11 + should 'return parent' do
  12 + org1 = fast_create(Organization)
  13 + org2 = fast_create(Organization)
  14 + task = SubOrganizationsPlugin::ApprovePaternity.create!(:requestor => requestor, :target => org2, :temp_parent_id => org1.id, :temp_parent_type => org1.class.name)
  15 +
  16 + assert_equal SubOrganizationsPlugin::ApprovePaternityRelation.parent(task), org1
  17 + end
  18 +
  19 + should 'list pending children' do
  20 + organization = fast_create(Organization)
  21 + org1 = fast_create(Organization)
  22 + org2 = fast_create(Organization)
  23 + org3 = fast_create(Organization)
  24 +
  25 + SubOrganizationsPlugin::ApprovePaternity.create!(:requestor => requestor, :target => org1, :temp_parent_id => organization.id, :temp_parent_type => organization.class.name)
  26 + SubOrganizationsPlugin::ApprovePaternity.create!(:requestor => requestor, :target => org2, :temp_parent_id => organization.id, :temp_parent_type => organization.class.name)
  27 +
  28 + assert_includes SubOrganizationsPlugin::ApprovePaternityRelation.pending_children(organization), org1
  29 + assert_includes SubOrganizationsPlugin::ApprovePaternityRelation.pending_children(organization), org2
  30 + assert_not_includes SubOrganizationsPlugin::ApprovePaternityRelation.pending_children(organization), org3
  31 + end
  32 +end
plugins/sub_organizations/test/unit/sub_organizations_plugin/approve_paternity_test.rb 0 → 100644
@@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
  1 +require File.dirname(__FILE__) + '/../../../../../test/test_helper'
  2 +
  3 +class SubOrganizationsPlugin::ApprovePaternityTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @requestor = create_user('some-user').person
  7 + end
  8 +
  9 + attr_reader :requestor
  10 +
  11 + should 'create relation after creation' do
  12 + org1 = fast_create(Organization)
  13 + org2 = fast_create(Organization)
  14 + assert_difference SubOrganizationsPlugin::ApprovePaternityRelation, :count, 1 do
  15 + SubOrganizationsPlugin::ApprovePaternity.create!(:requestor => requestor, :temp_parent_id => org1.id, :temp_parent_type => org1.class.name, :target => org2)
  16 + end
  17 + end
  18 +
  19 + should 'add children to parent after approving' do
  20 + org1 = fast_create(Organization)
  21 + org2 = fast_create(Organization)
  22 +
  23 + task = SubOrganizationsPlugin::ApprovePaternity.create!(:requestor => requestor, :temp_parent_id => org1.id, :temp_parent_type => org1.class.name, :target => org2)
  24 + assert_not_includes SubOrganizationsPlugin::Relation.children(org1), org2
  25 +
  26 + task.finish
  27 + assert_includes SubOrganizationsPlugin::Relation.children(org1), org2
  28 + end
  29 +end
plugins/sub_organizations/test/unit/sub_organizations_plugin/ext/create_enterprise_test.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +require File.dirname(__FILE__) + '/../../../../../../test/test_helper'
  2 +
  3 +class CreateEnterpriseTest < ActiveSupport::TestCase
  4 +
  5 + should 'inlude the parent field in create enterprise' do
  6 + create_enterprise = CreateEnterprise.new
  7 + assert_nothing_raised { create_enterprise.sub_organizations_plugin_parent_to_be = '999' }
  8 + end
  9 +
  10 +end
  11 +
plugins/sub_organizations/test/unit/sub_organizations_plugin/ext/organization.rb 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +require File.dirname(__FILE__) + '/../../../../../../test/test_helper'
  2 +
  3 +class OrganizationTest < ActiveSupport::TestCase
  4 +
  5 + should 'inlude the parent field in organization' do
  6 + organization = Organization.new
  7 + assert_nothing_raised { organization.sub_organizations_plugin_parent_to_be = '999' }
  8 + end
  9 +
  10 + should 'include the parent field in the FIELDS constant' do
  11 + assert_includes Organization::FIELDS, 'sub_organizations_plugin_parent_to_be'
  12 + end
  13 +
  14 + should 'relate organization with parent if the attribute is set' do
  15 + parent = fast_create(Organization)
  16 + organization = Organization.new(:identifier => 'some-org',:name => 'Some Org', :sub_organizations_plugin_parent_to_be => parent.id)
  17 + assert_not_includes SubOrganizationsPlugin::Relation.children(parent), organization
  18 +
  19 + organization.save!
  20 + assert_includes SubOrganizationsPlugin::Relation.children(parent), organization
  21 + end
  22 +
  23 +end
plugins/sub_organizations/test/unit/sub_organizations_plugin/relation_test.rb 0 → 100644
@@ -0,0 +1,110 @@ @@ -0,0 +1,110 @@
  1 +require File.dirname(__FILE__) + '/../../../../../test/test_helper'
  2 +
  3 +class SubOrganizationsPlugin::RelationTest < ActiveSupport::TestCase
  4 +
  5 + should 'validates presence of child and parent' do
  6 + org = fast_create(Organization)
  7 + relation = SubOrganizationsPlugin::Relation.new
  8 +
  9 + relation.parent = org
  10 + relation.valid?
  11 + assert relation.errors.invalid?(:child)
  12 +
  13 + relation.parent = nil
  14 + relation.child = org
  15 + relation.valid?
  16 + assert relation.errors.invalid?(:parent)
  17 + end
  18 +
  19 + should 'relate two organizations' do
  20 + org1 = fast_create(Organization)
  21 + org2 = fast_create(Organization)
  22 + relation = SubOrganizationsPlugin::Relation.create!(:parent => org1, :child => org2)
  23 +
  24 + assert_equal org1, relation.parent
  25 + assert_equal org2, relation.child
  26 + end
  27 +
  28 + should 'not allow self relation' do
  29 + org = fast_create(Organization)
  30 + relation = SubOrganizationsPlugin::Relation.new(:parent_id => org, :child_id => org)
  31 + assert !relation.valid?
  32 + assert relation.errors.invalid?(:child)
  33 + end
  34 +
  35 + should 'be able to retrieve parents of an organization' do
  36 + child = fast_create(Organization)
  37 + parent1 = fast_create(Organization)
  38 + parent2 = fast_create(Organization)
  39 + SubOrganizationsPlugin::Relation.create!(:parent => parent1, :child => child)
  40 + SubOrganizationsPlugin::Relation.create!(:parent => parent2, :child => child)
  41 +
  42 + assert_includes SubOrganizationsPlugin::Relation.parents(child), parent1
  43 + assert_includes SubOrganizationsPlugin::Relation.parents(child), parent2
  44 + end
  45 +
  46 + should 'be able to retrieve children of an organization' do
  47 + parent = fast_create(Organization)
  48 + child1 = fast_create(Organization)
  49 + child2 = fast_create(Organization)
  50 + SubOrganizationsPlugin::Relation.create!(:parent => parent, :child => child1)
  51 + SubOrganizationsPlugin::Relation.create!(:parent => parent, :child => child2)
  52 +
  53 + assert_includes SubOrganizationsPlugin::Relation.children(parent), child1
  54 + assert_includes SubOrganizationsPlugin::Relation.children(parent), child2
  55 + end
  56 +
  57 + should 'not allow cyclical reference' do
  58 + org1 = fast_create(Organization)
  59 + org2 = fast_create(Organization)
  60 + SubOrganizationsPlugin::Relation.create!(:parent => org1, :child => org2)
  61 + relation = SubOrganizationsPlugin::Relation.new(:parent => org2, :child => org1)
  62 +
  63 + assert !relation.valid?
  64 + assert relation.errors.invalid?(:child)
  65 + end
  66 +
  67 + should 'not allow multi-level paternity' do
  68 + org1 = fast_create(Organization)
  69 + org2 = fast_create(Organization)
  70 + org3 = fast_create(Organization)
  71 + SubOrganizationsPlugin::Relation.create!(:parent => org1, :child => org2)
  72 + r1 = SubOrganizationsPlugin::Relation.new(:parent => org2, :child => org3)
  73 + r2 = SubOrganizationsPlugin::Relation.new(:parent => org3, :child => org1)
  74 +
  75 + assert !r1.valid?
  76 + assert r1.errors.invalid?(:child)
  77 +
  78 + assert !r2.valid?
  79 + assert r2.errors.invalid?(:child)
  80 + end
  81 +
  82 + should 'add children' do
  83 + org1 = fast_create(Organization)
  84 + org2 = fast_create(Organization)
  85 + org3 = fast_create(Organization)
  86 + org4 = fast_create(Organization)
  87 +
  88 + SubOrganizationsPlugin::Relation.add_children(org1,org2)
  89 + assert_includes SubOrganizationsPlugin::Relation.children(org1), org2
  90 +
  91 + SubOrganizationsPlugin::Relation.add_children(org1,org3,org4)
  92 + assert_includes SubOrganizationsPlugin::Relation.children(org1), org3
  93 + assert_includes SubOrganizationsPlugin::Relation.children(org1), org4
  94 + end
  95 +
  96 + should 'remove children' do
  97 + org1 = fast_create(Organization)
  98 + org2 = fast_create(Organization)
  99 + org3 = fast_create(Organization)
  100 + org4 = fast_create(Organization)
  101 + SubOrganizationsPlugin::Relation.add_children(org1,org2,org3,org4)
  102 +
  103 + SubOrganizationsPlugin::Relation.remove_children(org1,org2)
  104 + assert_not_includes SubOrganizationsPlugin::Relation.children(org1), org2
  105 +
  106 + SubOrganizationsPlugin::Relation.remove_children(org1,org3,org4)
  107 + assert_not_includes SubOrganizationsPlugin::Relation.children(org1), org3
  108 + assert_not_includes SubOrganizationsPlugin::Relation.children(org1), org4
  109 + end
  110 +end
plugins/sub_organizations/test/unit/sub_organizations_plugin_test.rb 0 → 100644
@@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +
  3 +class SubOrganizationsTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @plugin = SubOrganizationsPlugin.new
  7 + end
  8 +
  9 + attr_reader :plugin
  10 +
  11 + should 'include sub-organizations members in the parent organization' do
  12 + org1 = fast_create(Organization)
  13 + org2 = fast_create(Organization)
  14 + org3 = fast_create(Organization)
  15 + member1 = fast_create(Person)
  16 + member2 = fast_create(Person)
  17 + member3 = fast_create(Person)
  18 + member4 = fast_create(Person)
  19 + member5 = fast_create(Person)
  20 + member6 = fast_create(Person)
  21 + member7 = fast_create(Person)
  22 + org1.add_member(member1)
  23 + org2.add_member(member2)
  24 + org2.add_member(member3)
  25 + org3.add_member(member4)
  26 + org3.add_member(member5)
  27 + SubOrganizationsPlugin::Relation.create!(:parent => org1, :child => org2)
  28 + SubOrganizationsPlugin::Relation.create!(:parent => org1, :child => org3)
  29 +
  30 + org1_members = plugin.organization_members(org1)
  31 +
  32 + assert_equal ActiveRecord::NamedScope::Scope, org1_members.class
  33 + assert_not_includes org1_members, member1
  34 + assert_includes org1_members, member2
  35 + assert_includes org1_members, member3
  36 + assert_includes org1_members, member4
  37 + assert_includes org1_members, member5
  38 + assert_not_includes org1_members, member6
  39 + assert_not_includes org1_members, member7
  40 +
  41 + org2_members = plugin.organization_members(org2)
  42 + org3_members = plugin.organization_members(org3)
  43 +
  44 + assert org2_members.blank?
  45 + assert org3_members.blank?
  46 + end
  47 +
  48 + should 'grant permission that user has on parent organizations over children orgnaizations' do
  49 + person = create_user('admin-user').person
  50 + org1 = fast_create(Organization)
  51 + org2 = fast_create(Organization)
  52 + SubOrganizationsPlugin::Relation.add_children(org1,org2)
  53 + person.stubs('has_permission_without_plugins?').with(:make_ice_cream, org1).returns(true)
  54 + person.stubs('has_permission_without_plugins?').with(:make_ice_cream, org2).returns(false)
  55 +
  56 + assert plugin.has_permission?(person, :make_ice_cream, org2)
  57 + end
  58 +
  59 + should 'not crash if receives an environment as target of has permission' do
  60 + assert_nothing_raised do
  61 + plugin.has_permission?(fast_create(Person), :make_ice_cream, fast_create(Environment))
  62 + end
  63 + end
  64 +
  65 + should 'display control panel button only to organizations with no parent' do
  66 + org1 = fast_create(Organization)
  67 + org2 = fast_create(Organization)
  68 + profile = fast_create(Profile)
  69 + SubOrganizationsPlugin::Relation.add_children(org1,org2)
  70 + context = mock()
  71 + SubOrganizationsPlugin.any_instance.stubs(:context).returns(context)
  72 +
  73 + context.stubs(:profile).returns(org1)
  74 + assert_not_nil plugin.control_panel_buttons
  75 +
  76 + context.stubs(:profile).returns(org2)
  77 + assert_nil plugin.control_panel_buttons
  78 +
  79 + context.stubs(:profile).returns(profile)
  80 + assert_nil plugin.control_panel_buttons
  81 + end
  82 +end
  83 +
plugins/sub_organizations/views/sub_organizations_plugin_myprofile/index.html.erb 0 → 100644
@@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
  1 +<h1><%= _('Manage sub-groups') %></h1>
  2 +
  3 +<% if !@pending_children.blank? %>
  4 + <%= _('Sub-groups awaiting approval:') %>
  5 + <ul>
  6 + <% @pending_children.each do |child| %>
  7 + <li><%= link_to(child.name, child.url) %></li>
  8 + <% end %>
  9 + </ul>
  10 +<% end %>
  11 +
  12 +<% form_tag do %>
  13 + <% button_bar do %>
  14 + <%= button(:add, __('Create a new sub-community'), :controller => 'memberships', :action => 'new_community', :profile => user.identifier, :sub_organizations_plugin_parent_to_be => profile.id) %>
  15 + <%= button :add, __('Register a new sub-enterprise'), :controller => 'enterprise_registration', :sub_organizations_plugin_parent_to_be => profile.id if environment.enabled?('enterprise_registration') %>
  16 + <% end %>
  17 +
  18 + <p><%= _('Fill in the search field to find the groups that should be added as sub-group of this organization:') %></p>
  19 + <%= token_input_field_tag(:q, 'search-organization', {:action => 'search_organization'},
  20 + { :focus => true,
  21 + :hint_text => _('Type in a search term for a group'),
  22 + :pre_populate => @tokenized_children}) %>
  23 +
  24 + <% button_bar do %>
  25 + <%= submit_button('save', _('Save'))%>
  26 + <%= button('cancel', _('Cancel'), {:controller => 'profile_editor'})%>
  27 + <% end %>
  28 +<% end %>
  29 +
  30 +<br style='clear: both'/>
  31 +
  32 +<%= render :partial => 'shared/list_groups', :locals => {:profile => user, :groups => @children} %>
public/stylesheets/application.css
@@ -3598,46 +3598,50 @@ h1#agenda-title { @@ -3598,46 +3598,50 @@ h1#agenda-title {
3598 /* ==> public/stylesheets/controller_memberships.css <== */ 3598 /* ==> public/stylesheets/controller_memberships.css <== */
3599 /* @import url(profile-list-block.css); ==> BROKEN REFERENCE, OH MY! */ 3599 /* @import url(profile-list-block.css); ==> BROKEN REFERENCE, OH MY! */
3600 3600
3601 -.controller-memberships #memberships-index ul { 3601 +#groups-list {
3602 width: 100%; 3602 width: 100%;
3603 padding: 0px; 3603 padding: 0px;
3604 margin: 0px; 3604 margin: 0px;
3605 display: block; 3605 display: block;
3606 } 3606 }
3607 -.controller-memberships #memberships-index li { 3607 +
  3608 +#groups-list li {
3608 display: block; 3609 display: block;
3609 list-style: none; 3610 list-style: none;
3610 margin-bottom: 20px background-color: #B8CFE7; 3611 margin-bottom: 20px background-color: #B8CFE7;
3611 } 3612 }
3612 -.controller-memberships #memberships-index li .vcard { 3613 +
  3614 +#groups-list li .vcard {
3613 float: right; 3615 float: right;
3614 padding: 5px; 3616 padding: 5px;
3615 margin-bottom: 5px; 3617 margin-bottom: 5px;
3616 margin-left: 10px; 3618 margin-left: 10px;
3617 } 3619 }
3618 -.controller-memberships #memberships-index li .may-clear { 3620 +#groups-list li .may-clear {
3619 clear: right; 3621 clear: right;
3620 } 3622 }
3621 -.controller-memberships #memberships-index li .profile-details { 3623 +
  3624 +#groups-list li .profile-details {
3622 display: block; 3625 display: block;
3623 padding: 3% 0px 0px 30px; 3626 padding: 3% 0px 0px 30px;
3624 } 3627 }
3625 -.controller-memberships .action_memberships_destroy_community .main-block u {  
3626 - text-decoration: none;  
3627 - border-bottom: 1px dotted red;  
3628 -}  
3629 -#memberships-index .menu-submenu { 3628 +
  3629 +#groups-list .menu-submenu {
3630 bottom: 127px; 3630 bottom: 127px;
3631 right: -20px; 3631 right: -20px;
3632 } 3632 }
3633 -#memberships-index .menu-submenu li { 3633 +
  3634 +#groups-list .menu-submenu li {
3634 border: 0; 3635 border: 0;
3635 background: transparent; 3636 background: transparent;
3636 } 3637 }
3637 -.controller-memberships .button-bar { 3638 +
  3639 +#groups-list .button-bar {
3638 clear: both; 3640 clear: both;
3639 } 3641 }
3640 -.controller-memberships #memberships-index li .vcard a.profile_link.url, .controller-memberships #memberships-index li .vcard a.profile_link.url:hover { 3642 +
  3643 +#groups-list li .vcard a.profile_link.url,
  3644 +#groups-list li .vcard a.profile_link.url:hover {
3641 background: transparent; 3645 background: transparent;
3642 border: 0; 3646 border: 0;
3643 text-decoration: none; 3647 text-decoration: none;
test/functional/enterprise_registration_controller_test.rb
@@ -180,4 +180,27 @@ class EnterpriseRegistrationControllerTest &lt; ActionController::TestCase @@ -180,4 +180,27 @@ class EnterpriseRegistrationControllerTest &lt; ActionController::TestCase
180 get :index 180 get :index
181 assert_equal assigns(:create_enterprise).target, environment 181 assert_equal assigns(:create_enterprise).target, environment
182 end 182 end
  183 +
  184 + should 'include hidden fields supplied by plugins on enterprise registration' do
  185 + class Plugin1 < Noosfero::Plugin
  186 + def enterprise_registration_hidden_fields
  187 + {'plugin1' => 'Plugin 1'}
  188 + end
  189 + end
  190 +
  191 + class Plugin2 < Noosfero::Plugin
  192 + def enterprise_registration_hidden_fields
  193 + {'plugin2' => 'Plugin 2'}
  194 + end
  195 + end
  196 +
  197 + environment = Environment.default
  198 + environment.enable_plugin(Plugin1.name)
  199 + environment.enable_plugin(Plugin2.name)
  200 +
  201 + get :index
  202 +
  203 + assert_tag :tag => 'input', :attributes => {:id => 'create_enterprise_plugin1', :type => 'hidden', :value => 'Plugin 1'}
  204 + assert_tag :tag => 'input', :attributes => {:id => 'create_enterprise_plugin2', :type => 'hidden', :value => 'Plugin 2'}
  205 + end
183 end 206 end
test/functional/memberships_controller_test.rb
@@ -206,4 +206,27 @@ class MembershipsControllerTest &lt; ActionController::TestCase @@ -206,4 +206,27 @@ class MembershipsControllerTest &lt; ActionController::TestCase
206 assert_no_tag :tag => 'textarea', :attributes => {:name => 'community[description]'} 206 assert_no_tag :tag => 'textarea', :attributes => {:name => 'community[description]'}
207 end 207 end
208 208
  209 + should 'include hidden fields supplied by plugins on new community' do
  210 + class Plugin1 < Noosfero::Plugin
  211 + def new_community_hidden_fields
  212 + {'plugin1' => 'Plugin 1'}
  213 + end
  214 + end
  215 +
  216 + class Plugin2 < Noosfero::Plugin
  217 + def new_community_hidden_fields
  218 + {'plugin2' => 'Plugin 2'}
  219 + end
  220 + end
  221 +
  222 + environment = Environment.default
  223 + environment.enable_plugin(Plugin1.name)
  224 + environment.enable_plugin(Plugin2.name)
  225 +
  226 + get :new_community, :profile => profile.identifier
  227 +
  228 + assert_tag :tag => 'input', :attributes => {:id => 'community_plugin1', :type => 'hidden', :value => 'Plugin 1'}
  229 + assert_tag :tag => 'input', :attributes => {:id => 'community_plugin2', :type => 'hidden', :value => 'Plugin 2'}
  230 + end
  231 +
209 end 232 end
test/unit/person_test.rb
@@ -3,6 +3,10 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39; @@ -3,6 +3,10 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 class PersonTest < ActiveSupport::TestCase 3 class PersonTest < ActiveSupport::TestCase
4 fixtures :profiles, :users, :environments 4 fixtures :profiles, :users, :environments
5 5
  6 + def teardown
  7 + Thread.current[:enabled_plugins] = nil
  8 + end
  9 +
6 def test_person_must_come_form_the_cration_of_an_user 10 def test_person_must_come_form_the_cration_of_an_user
7 p = Person.new(:environment => Environment.default, :name => 'John', :identifier => 'john') 11 p = Person.new(:environment => Environment.default, :name => 'John', :identifier => 'john')
8 assert !p.valid? 12 assert !p.valid?
@@ -1104,6 +1108,18 @@ class PersonTest &lt; ActiveSupport::TestCase @@ -1104,6 +1108,18 @@ class PersonTest &lt; ActiveSupport::TestCase
1104 assert_equal [person], Person.members_of(community) 1108 assert_equal [person], Person.members_of(community)
1105 end 1109 end
1106 1110
  1111 + should 'be able to pass array to members_of' do
  1112 + person1 = fast_create(Person)
  1113 + community = fast_create(Community)
  1114 + community.add_member(person1)
  1115 + person2 = fast_create(Person)
  1116 + enterprise = fast_create(Enterprise)
  1117 + enterprise.add_member(person2)
  1118 +
  1119 + assert_includes Person.members_of([community, enterprise]), person1
  1120 + assert_includes Person.members_of([community, enterprise]), person2
  1121 + end
  1122 +
1107 should 'find more active people' do 1123 should 'find more active people' do
1108 Person.destroy_all 1124 Person.destroy_all
1109 p1 = fast_create(Person) 1125 p1 = fast_create(Person)
@@ -1228,4 +1244,26 @@ class PersonTest &lt; ActiveSupport::TestCase @@ -1228,4 +1244,26 @@ class PersonTest &lt; ActiveSupport::TestCase
1228 1244
1229 assert_equivalent [person_scrap,person_activity], person.activities.map { |a| a.klass.constantize.find(a.id) } 1245 assert_equivalent [person_scrap,person_activity], person.activities.map { |a| a.klass.constantize.find(a.id) }
1230 end 1246 end
  1247 +
  1248 + should 'allow plugins to extend person\'s permission access' do
  1249 + person = create_user('some-user').person
  1250 + class Plugin1 < Noosfero::Plugin
  1251 + def has_permission?(person, permission, target)
  1252 + true
  1253 + end
  1254 + end
  1255 +
  1256 + class Plugin2 < Noosfero::Plugin
  1257 + def has_permission?(person, permission, target)
  1258 + false
  1259 + end
  1260 + end
  1261 +
  1262 + e = Environment.default
  1263 + e.enable_plugin(Plugin1.name)
  1264 + e.enable_plugin(Plugin2.name)
  1265 + person.stubs('has_permission_without_plugins?').returns(false)
  1266 +
  1267 + assert person.has_permission?('bli', Profile.new)
  1268 + end
1231 end 1269 end
test/unit/plugin_manager_test.rb
@@ -12,7 +12,6 @@ class PluginManagerTest &lt; ActiveSupport::TestCase @@ -12,7 +12,6 @@ class PluginManagerTest &lt; ActiveSupport::TestCase
12 @manager = Noosfero::Plugin::Manager.new(@environment, @controller) 12 @manager = Noosfero::Plugin::Manager.new(@environment, @controller)
13 end 13 end
14 attr_reader :environment 14 attr_reader :environment
15 - attr_reader :manager  
16 15
17 should 'return the intersection between environment\'s enabled plugins and system available plugins' do 16 should 'return the intersection between environment\'s enabled plugins and system available plugins' do
18 class Plugin1 < Noosfero::Plugin; end; 17 class Plugin1 < Noosfero::Plugin; end;
@@ -21,6 +20,7 @@ class PluginManagerTest &lt; ActiveSupport::TestCase @@ -21,6 +20,7 @@ class PluginManagerTest &lt; ActiveSupport::TestCase
21 class Plugin4 < Noosfero::Plugin; end; 20 class Plugin4 < Noosfero::Plugin; end;
22 environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s, Plugin4.to_s]) 21 environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s, Plugin4.to_s])
23 Noosfero::Plugin.stubs(:all).returns([Plugin1.to_s, Plugin3.to_s, Plugin4.to_s]) 22 Noosfero::Plugin.stubs(:all).returns([Plugin1.to_s, Plugin3.to_s, Plugin4.to_s])
  23 + manager = Noosfero::Plugin::Manager.new(@controller)
24 plugins = manager.enabled_plugins.map { |instance| instance.class.to_s } 24 plugins = manager.enabled_plugins.map { |instance| instance.class.to_s }
25 assert_equal [Plugin1.to_s, Plugin4.to_s], plugins 25 assert_equal [Plugin1.to_s, Plugin4.to_s], plugins
26 end 26 end
@@ -49,6 +49,7 @@ class PluginManagerTest &lt; ActiveSupport::TestCase @@ -49,6 +49,7 @@ class PluginManagerTest &lt; ActiveSupport::TestCase
49 49
50 p1 = Plugin1.new 50 p1 = Plugin1.new
51 p2 = Plugin2.new 51 p2 = Plugin2.new
  52 + manager = Noosfero::Plugin::Manager.new(@controller)
52 53
53 assert_equal [p1.random_event, p2.random_event], manager.dispatch(:random_event) 54 assert_equal [p1.random_event, p2.random_event], manager.dispatch(:random_event)
54 end 55 end
test/unit/profile_test.rb
@@ -3,8 +3,8 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39; @@ -3,8 +3,8 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 class ProfileTest < ActiveSupport::TestCase 3 class ProfileTest < ActiveSupport::TestCase
4 fixtures :profiles, :environments, :users, :roles, :domains 4 fixtures :profiles, :environments, :users, :roles, :domains
5 5
6 - def setup  
7 - super 6 + def teardown
  7 + Thread.current[:enabled_plugins] = nil
8 end 8 end
9 9
10 def test_identifier_validation 10 def test_identifier_validation
@@ -1833,6 +1833,37 @@ class ProfileTest &lt; ActiveSupport::TestCase @@ -1833,6 +1833,37 @@ class ProfileTest &lt; ActiveSupport::TestCase
1833 assert_equal [], profile.activities 1833 assert_equal [], profile.activities
1834 end 1834 end
1835 1835
  1836 + should 'merge members of plugins to original members' do
  1837 + original_community = fast_create(Community)
  1838 + community1 = fast_create(Community, :identifier => 'community1')
  1839 + community2 = fast_create(Community, :identifier => 'community2')
  1840 + original_member = fast_create(Person)
  1841 + plugin1_member = fast_create(Person)
  1842 + plugin2_member = fast_create(Person)
  1843 + original_community.add_member(original_member)
  1844 + community1.add_member(plugin1_member)
  1845 + community2.add_member(plugin2_member)
  1846 +
  1847 + class Plugin1 < Noosfero::Plugin
  1848 + def organization_members(profile)
  1849 + Person.members_of(Community.find_by_identifier('community1'))
  1850 + end
  1851 + end
  1852 +
  1853 + class Plugin2 < Noosfero::Plugin
  1854 + def organization_members(profile)
  1855 + Person.members_of(Community.find_by_identifier('community2'))
  1856 + end
  1857 + end
  1858 +
  1859 + original_community.stubs(:enabled_plugins).returns([Plugin1.new, Plugin2.new])
  1860 +
  1861 + assert_includes original_community.members, original_member
  1862 + assert_includes original_community.members, plugin1_member
  1863 + assert_includes original_community.members, plugin2_member
  1864 + assert 3, original_community.members.count
  1865 + end
  1866 +
1836 private 1867 private
1837 1868
1838 def assert_invalid_identifier(id) 1869 def assert_invalid_identifier(id)
vendor/plugins/monkey_patches/methods_from_fake_arel/init.rb 0 → 100644
@@ -0,0 +1,71 @@ @@ -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