Commit f8137b7ade33cedeb5ffea051098f597d0194fb3
Committed by
Antonio Terceiro
1 parent
19be5f1a
Exists in
master
and in
29 other branches
Plugins
Done: * Plugin loading process * Infra-structure for plugins registering and event activations. * Three fixed routes for plugins. * Methods to define plugins meta-information. * Interface for activation and deactivation of plugins in the environment panel. * Hotspots: - Buttons in the control panel. - Tabs in the profile (including expanded_template method). - New attributes for profile. * Possibility for the plugin to add new tables. * Mezuro prototype. Missing: * Test integration. * Dependencies integration. * Possibility to add behaviours in noosfero base models.
Showing
54 changed files
with
1257 additions
and
186 deletions
Show diff stats
@@ -0,0 +1,18 @@ | @@ -0,0 +1,18 @@ | ||
1 | +class PluginsController < AdminController | ||
2 | + | ||
3 | + def index | ||
4 | + @active_plugins = Noosfero::Plugin.all.map {|plugin_name| eval(plugin_name)}.compact | ||
5 | + end | ||
6 | + | ||
7 | + post_only :update | ||
8 | + def update | ||
9 | + params[:environment][:enabled_plugins].delete('') | ||
10 | + if @environment.update_attributes(params[:environment]) | ||
11 | + session[:notice] = _('Plugins updated successfully.') | ||
12 | + else | ||
13 | + session[:error] = _('Plugins were not updated successfully.') | ||
14 | + end | ||
15 | + redirect_to :action => 'index' | ||
16 | + end | ||
17 | + | ||
18 | +end |
app/controllers/application.rb
@@ -83,6 +83,7 @@ class ApplicationController < ActionController::Base | @@ -83,6 +83,7 @@ class ApplicationController < ActionController::Base | ||
83 | include NeedsProfile | 83 | include NeedsProfile |
84 | 84 | ||
85 | before_filter :detect_stuff_by_domain | 85 | before_filter :detect_stuff_by_domain |
86 | + before_filter :init_noosfero_plugins | ||
86 | attr_reader :environment | 87 | attr_reader :environment |
87 | 88 | ||
88 | before_filter :load_terminology | 89 | before_filter :load_terminology |
@@ -114,6 +115,10 @@ class ApplicationController < ActionController::Base | @@ -114,6 +115,10 @@ class ApplicationController < ActionController::Base | ||
114 | end | 115 | end |
115 | end | 116 | end |
116 | 117 | ||
118 | + def init_noosfero_plugins | ||
119 | + @plugins = Noosfero::Plugin::Manager.new(self) | ||
120 | + end | ||
121 | + | ||
117 | def load_terminology | 122 | def load_terminology |
118 | # cache terminology for performance | 123 | # cache terminology for performance |
119 | @@terminology_cache ||= {} | 124 | @@terminology_cache ||= {} |
app/helpers/profile_helper.rb
@@ -15,4 +15,12 @@ module ProfileHelper | @@ -15,4 +15,12 @@ module ProfileHelper | ||
15 | end | 15 | end |
16 | end | 16 | end |
17 | 17 | ||
18 | + def render_tabs(tabs) | ||
19 | + titles = tabs.inject(''){ |result, tab| result << content_tag(:li, link_to(tab[:title], '#'+tab[:id]), :class => 'tab') } | ||
20 | + contents = tabs.inject(''){ |result, tab| result << content_tag(:div, tab[:content], :id => tab[:id]) } | ||
21 | + | ||
22 | + content_tag :div, :class => 'ui-tabs' do | ||
23 | + content_tag(:ul, titles) + contents | ||
24 | + end | ||
25 | + end | ||
18 | end | 26 | end |
app/models/environment.rb
@@ -220,6 +220,8 @@ class Environment < ActiveRecord::Base | @@ -220,6 +220,8 @@ class Environment < ActiveRecord::Base | ||
220 | 220 | ||
221 | settings_items :trusted_sites_for_iframe, :type => Array, :default => ['itheora.org', 'tv.softwarelivre.org', 'stream.softwarelivre.org'] | 221 | settings_items :trusted_sites_for_iframe, :type => Array, :default => ['itheora.org', 'tv.softwarelivre.org', 'stream.softwarelivre.org'] |
222 | 222 | ||
223 | + settings_items :enabled_plugins, :type => Array, :default => [] | ||
224 | + | ||
223 | def news_amount_by_folder=(amount) | 225 | def news_amount_by_folder=(amount) |
224 | settings[:news_amount_by_folder] = amount.to_i | 226 | settings[:news_amount_by_folder] = amount.to_i |
225 | end | 227 | end |
app/views/admin_panel/index.rhtml
@@ -6,6 +6,7 @@ | @@ -6,6 +6,7 @@ | ||
6 | <tr><td><%= link_to _('Edit site info'), :action => 'site_info' %></td></tr> | 6 | <tr><td><%= link_to _('Edit site info'), :action => 'site_info' %></td></tr> |
7 | <tr><td><%= link_to __('Edit message for disabled enterprises'), :action => 'message_for_disabled_enterprise' %></td></tr> | 7 | <tr><td><%= link_to __('Edit message for disabled enterprises'), :action => 'message_for_disabled_enterprise' %></td></tr> |
8 | <tr><td><%= link_to _('Enable/disable features'), :controller => 'features' %></td></tr> | 8 | <tr><td><%= link_to _('Enable/disable features'), :controller => 'features' %></td></tr> |
9 | + <tr><td><%= link_to _('Enable/disable plugins'), :controller => 'plugins' %></td></tr> | ||
9 | <tr><td><%= link_to _('Edit sideboxes'), :controller => 'environment_design'%></td></tr> | 10 | <tr><td><%= link_to _('Edit sideboxes'), :controller => 'environment_design'%></td></tr> |
10 | <tr><td><%= link_to _('Manage Categories'), :controller => 'categories'%></td></tr> | 11 | <tr><td><%= link_to _('Manage Categories'), :controller => 'categories'%></td></tr> |
11 | <tr><td><%= link_to _('Manage User roles'), :controller => 'role' %></td></tr> | 12 | <tr><td><%= link_to _('Manage User roles'), :controller => 'role' %></td></tr> |
@@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
1 | +<h1><%= _('Manage plugins') %></h1> | ||
2 | +<%= _('Here you can enable or disable any plugin of your environment.')%> | ||
3 | + | ||
4 | +<% labelled_form_for(:environment, @environment, :url => {:action => 'update'}) do |f| %> | ||
5 | + | ||
6 | +<table> | ||
7 | + <tr> | ||
8 | + <th><%= _('Plugin') %></th> | ||
9 | + <th><%= _('Description') %></th> | ||
10 | + <th><%= _('Enabled?') %></th> | ||
11 | + </tr> | ||
12 | + <%= hidden_field_tag('environment[enabled_plugins][]', '') %> | ||
13 | + <% @active_plugins.each do |plugin| %> | ||
14 | + <tr> | ||
15 | + <td><%= plugin.plugin_name %></td> | ||
16 | + <td><%= plugin.plugin_description %></td> | ||
17 | + <td><%= check_box_tag "environment[enabled_plugins][]", plugin, @environment.enabled_plugins.include?(plugin.to_s), :id => plugin.plugin_name %></td> | ||
18 | + </tr> | ||
19 | + <% end %> | ||
20 | +</table> | ||
21 | + | ||
22 | +<div> | ||
23 | + <% button_bar do %> | ||
24 | + <%= submit_button('save', _('Save changes')) %> | ||
25 | + <%= button :back, _('Back to admin panel'), :controller => 'admin_panel', :action => 'index' %> | ||
26 | + <% end %> | ||
27 | +</div> | ||
28 | + | ||
29 | +<% end %> |
app/views/profile/_organization.rhtml
@@ -1,62 +0,0 @@ | @@ -1,62 +0,0 @@ | ||
1 | - | ||
2 | -<tr> | ||
3 | - <td colspan='2'> | ||
4 | - <div class='ui-tabs'> | ||
5 | - | ||
6 | - <ul> | ||
7 | - <% if logged_in? && current_person.follows?(@profile) %> | ||
8 | - <li class='tab'><a href='#profile-wall'><%= _('Wall') %></a></li> | ||
9 | - <% end %> | ||
10 | - <li class='tab'><a href='#profile-network'><%= _('What\'s new') %></a></li> | ||
11 | - <li class='tab'><a href='#community-profile'><%= _('Profile') %></a></li> | ||
12 | - </ul> | ||
13 | - | ||
14 | - <% if logged_in? && current_person.follows?(@profile) %> | ||
15 | - <%= render :partial => 'profile_wall' %> | ||
16 | - <% end %> | ||
17 | - <%= render :partial => 'profile_network' %> | ||
18 | - | ||
19 | - <div id='community-profile'> | ||
20 | - <table> | ||
21 | - <tr> | ||
22 | - <th colspan='2'><%= _('Basic information')%></th> | ||
23 | - </tr> | ||
24 | - | ||
25 | - <tr> | ||
26 | - <td class='field-name'><%= _('Members') %></td> | ||
27 | - <td> | ||
28 | - <%= link_to profile.members.count, :controller => 'profile', :action => 'members' %> | ||
29 | - </td> | ||
30 | - </tr> | ||
31 | - | ||
32 | - <%= display_field(_('Type:'), profile, :privacy_setting, true) %> | ||
33 | - | ||
34 | - <%= display_field(_('Location:'), profile, :location, true) %> | ||
35 | - | ||
36 | - <tr> | ||
37 | - <td class='field-name'><%= _('Created at:') %></td> | ||
38 | - <td><%= show_date(profile.created_at) %></td> | ||
39 | - </tr> | ||
40 | - | ||
41 | - <% if profile.kind_of?(Enterprise) && !profile.environment.enabled?('disable_products_for_enterprises') %> | ||
42 | - <tr> | ||
43 | - <td></td> | ||
44 | - <td> | ||
45 | - <%= link_to _('Products/Services'), :controller => 'catalog', :action => 'index' %> | ||
46 | - </td> | ||
47 | - </tr> | ||
48 | - <% end %> | ||
49 | - | ||
50 | - <tr> | ||
51 | - <td class='field-name'><%= _('Administrators:') %></td> | ||
52 | - <td> | ||
53 | - <%= profile.admins.map { |admin| link_to(admin.short_name, admin.url)}.join(', ') %> | ||
54 | - </td> | ||
55 | - </tr> | ||
56 | - | ||
57 | - <%= render :partial => 'common' %> | ||
58 | - </table> | ||
59 | - </div> | ||
60 | - </div> | ||
61 | - </td> | ||
62 | -</tr> |
@@ -0,0 +1,39 @@ | @@ -0,0 +1,39 @@ | ||
1 | +<table> | ||
2 | + <tr> | ||
3 | + <th colspan='2'><%= _('Basic information')%></th> | ||
4 | + </tr> | ||
5 | + | ||
6 | + <tr> | ||
7 | + <td class='field-name'><%= _('Members') %></td> | ||
8 | + <td> | ||
9 | + <%= link_to profile.members.count, :controller => 'profile', :action => 'members' %> | ||
10 | + </td> | ||
11 | + </tr> | ||
12 | + | ||
13 | + <%= display_field(_('Type:'), profile, :privacy_setting, true) %> | ||
14 | + | ||
15 | + <%= display_field(_('Location:'), profile, :location, true) %> | ||
16 | + | ||
17 | + <tr> | ||
18 | + <td class='field-name'><%= _('Created at:') %></td> | ||
19 | + <td><%= show_date(profile.created_at) %></td> | ||
20 | + </tr> | ||
21 | + | ||
22 | + <% if profile.kind_of?(Enterprise) && !profile.environment.enabled?('disable_products_for_enterprises') %> | ||
23 | + <tr> | ||
24 | + <td></td> | ||
25 | + <td> | ||
26 | + <%= link_to _('Products/Services'), :controller => 'catalog', :action => 'index' %> | ||
27 | + </td> | ||
28 | + </tr> | ||
29 | + <% end %> | ||
30 | + | ||
31 | + <tr> | ||
32 | + <td class='field-name'><%= _('Administrators:') %></td> | ||
33 | + <td> | ||
34 | + <%= profile.admins.map { |admin| link_to(admin.short_name, admin.url)}.join(', ') %> | ||
35 | + </td> | ||
36 | + </tr> | ||
37 | + | ||
38 | + <%= render :partial => 'common' %> | ||
39 | +</table> |
app/views/profile/_person.rhtml
@@ -1,104 +0,0 @@ | @@ -1,104 +0,0 @@ | ||
1 | -<tr> | ||
2 | - <td colspan='2'> | ||
3 | - <div class='ui-tabs'> | ||
4 | - | ||
5 | - <ul> | ||
6 | - <% if logged_in? && current_person.follows?(@profile) %> | ||
7 | - <li class='tab'><a href='#profile-wall'><%= _('Wall') %></a></li> | ||
8 | - <li class='tab'><a href='#profile-network'><%= _('Network') %></a></li> | ||
9 | - <% end %> | ||
10 | - <% if @profile.public? || (logged_in? && current_person.follows?(@profile)) %> | ||
11 | - <li class='tab'><a href='#profile-activity'><%= _('Activity') %></a></li> | ||
12 | - <% end %> | ||
13 | - <li class='tab'><a href='#person-profile'><%= _('Profile') %></a></li> | ||
14 | - </ul> | ||
15 | - | ||
16 | - <% if logged_in? && current_person.follows?(@profile) %> | ||
17 | - <%= render :partial => 'profile_wall' %> | ||
18 | - <%= render :partial => 'profile_network' %> | ||
19 | - <% end %> | ||
20 | - | ||
21 | - <% if @profile.public? || (logged_in? && current_person.follows?(@profile)) %> | ||
22 | - <%= render :partial => 'profile_activity' %> | ||
23 | - <% end %> | ||
24 | - | ||
25 | - <div id='person-profile'> | ||
26 | - <table> | ||
27 | - <tr> | ||
28 | - <th colspan='2'><%= _('Basic information')%></th> | ||
29 | - </tr> | ||
30 | - <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %> | ||
31 | - <%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%> | ||
32 | - <%= display_field(_('Location:'), profile, :location, true) %> | ||
33 | - | ||
34 | - <%= display_field(_('Type:'), profile, :privacy_setting, true) %> | ||
35 | - | ||
36 | - <tr> | ||
37 | - <td class='field-name'><%= _('Created at:') %></td> | ||
38 | - <td><%= show_date(profile.created_at) %></td> | ||
39 | - </tr> | ||
40 | - | ||
41 | - <% if profile == user || profile.friends.include?(user) %> | ||
42 | - <tr> | ||
43 | - <th colspan='2'><%= _('Contact')%></th> | ||
44 | - </tr> | ||
45 | - <%= display_field(_('Address:'), profile, :address) %> | ||
46 | - <%= display_field(_('ZIP code:'), profile, :zip_code) %> | ||
47 | - <%= display_field(_('Contact phone:'), profile, :contact_phone) %> | ||
48 | - <%= display_field(_('e-Mail:'), profile, :email, true) { |email| link_to_email(email) } %> | ||
49 | - <% end %> | ||
50 | - | ||
51 | - <% cache_timeout(profile.relationships_cache_key, 4.hours.from_now) do %> | ||
52 | - <% if !(profile.organization.blank? && profile.organization_website.blank?) && (profile.active_fields.include?('organization') || profile.active_fields.include?('organization_website')) %> | ||
53 | - <tr> | ||
54 | - <th colspan='2'><%= _('Work')%></th> | ||
55 | - </tr> | ||
56 | - <% end %> | ||
57 | - <%= display_field(_('Organization:'), profile, :organization) %> | ||
58 | - <%= display_field(_('Organization website:'), profile, :organization_website) { |url| link_to(url, url) }%> | ||
59 | - | ||
60 | - | ||
61 | - <% if !environment.enabled?('disable_asset_enterprises') && !profile.enterprises.empty? %> | ||
62 | - <tr> | ||
63 | - <th colspan='2'><%= __('Enterprises') %></th> | ||
64 | - </tr> | ||
65 | - <% profile.enterprises.each do |item| %> | ||
66 | - <tr> | ||
67 | - <td></td> | ||
68 | - <td><%= button 'menu-enterprise', item.name, item.url %></td> | ||
69 | - </tr> | ||
70 | - <% end %> | ||
71 | - <% end %> | ||
72 | - | ||
73 | - <tr> | ||
74 | - <th colspan='2'><%= _('Network')%></th> | ||
75 | - </tr> | ||
76 | - <tr> | ||
77 | - <td><%= __('Friends') + ':' %></td> | ||
78 | - <td><%= link_to profile.friends.count, { :controller => 'profile', :action => 'friends' } %></td> | ||
79 | - </tr> | ||
80 | - <tr> | ||
81 | - <td><%= __('Communities') + ':' %></td> | ||
82 | - <td><%= link_to profile.communities.count, :controller => "profile", :action => 'communities' %></td> | ||
83 | - </tr> | ||
84 | - | ||
85 | - <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %> | ||
86 | - <tr> | ||
87 | - <th colspan='2'><%= _('Interests') %></th> | ||
88 | - </tr> | ||
89 | - <% profile.interests.each do |item| %> | ||
90 | - <tr> | ||
91 | - <td></td> | ||
92 | - <td><%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %></td> | ||
93 | - </tr> | ||
94 | - <% end %> | ||
95 | - <% end %> | ||
96 | - | ||
97 | - <%= render :partial => 'common' %> | ||
98 | - | ||
99 | - </table> | ||
100 | - <% end %> | ||
101 | - </div> | ||
102 | - </div> | ||
103 | - </td> | ||
104 | -</tr> |
@@ -0,0 +1,76 @@ | @@ -0,0 +1,76 @@ | ||
1 | +<table> | ||
2 | + <tr> | ||
3 | + <th colspan='2'><%= _('Basic information')%></th> | ||
4 | + </tr> | ||
5 | + <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %> | ||
6 | + <%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%> | ||
7 | + <%= display_field(_('Location:'), profile, :location, true) %> | ||
8 | + | ||
9 | + <%= display_field(_('Type:'), profile, :privacy_setting, true) %> | ||
10 | + | ||
11 | + <tr> | ||
12 | + <td class='field-name'><%= _('Created at:') %></td> | ||
13 | + <td><%= show_date(profile.created_at) %></td> | ||
14 | + </tr> | ||
15 | + | ||
16 | + <% if profile == user || profile.friends.include?(user) %> | ||
17 | + <tr> | ||
18 | + <th colspan='2'><%= _('Contact')%></th> | ||
19 | + </tr> | ||
20 | + <%= display_field(_('Address:'), profile, :address) %> | ||
21 | + <%= display_field(_('ZIP code:'), profile, :zip_code) %> | ||
22 | + <%= display_field(_('Contact phone:'), profile, :contact_phone) %> | ||
23 | + <%= display_field(_('e-Mail:'), profile, :email, true) { |email| link_to_email(email) } %> | ||
24 | + <% end %> | ||
25 | + | ||
26 | + <% cache_timeout(profile.relationships_cache_key, 4.hours.from_now) do %> | ||
27 | + <% if !(profile.organization.blank? && profile.organization_website.blank?) && (profile.active_fields.include?('organization') || profile.active_fields.include?('organization_website')) %> | ||
28 | + <tr> | ||
29 | + <th colspan='2'><%= _('Work')%></th> | ||
30 | + </tr> | ||
31 | + <% end %> | ||
32 | + <%= display_field(_('Organization:'), profile, :organization) %> | ||
33 | + <%= display_field(_('Organization website:'), profile, :organization_website) { |url| link_to(url, url) }%> | ||
34 | + | ||
35 | + | ||
36 | + <% if !environment.enabled?('disable_asset_enterprises') && !profile.enterprises.empty? %> | ||
37 | + <tr> | ||
38 | + <th colspan='2'><%= __('Enterprises') %></th> | ||
39 | + </tr> | ||
40 | + <% profile.enterprises.each do |item| %> | ||
41 | + <tr> | ||
42 | + <td></td> | ||
43 | + <td><%= button 'menu-enterprise', item.name, item.url %></td> | ||
44 | + </tr> | ||
45 | + <% end %> | ||
46 | + <% end %> | ||
47 | + | ||
48 | + <tr> | ||
49 | + <th colspan='2'><%= _('Network')%></th> | ||
50 | + </tr> | ||
51 | + <tr> | ||
52 | + <td><%= __('Friends') + ':' %></td> | ||
53 | + <td><%= link_to profile.friends.count, { :controller => 'profile', :action => 'friends' } %></td> | ||
54 | + </tr> | ||
55 | + <tr> | ||
56 | + <td><%= __('Communities') + ':' %></td> | ||
57 | + <td><%= link_to profile.communities.count, :controller => "profile", :action => 'communities' %></td> | ||
58 | + </tr> | ||
59 | + | ||
60 | + <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %> | ||
61 | + <tr> | ||
62 | + <th colspan='2'><%= _('Interests') %></th> | ||
63 | + </tr> | ||
64 | + <% profile.interests.each do |item| %> | ||
65 | + <tr> | ||
66 | + <td></td> | ||
67 | + <td><%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %></td> | ||
68 | + </tr> | ||
69 | + <% end %> | ||
70 | + <% end %> | ||
71 | + | ||
72 | + <%= render :partial => 'common' %> | ||
73 | + | ||
74 | + </table> | ||
75 | +<% end %> | ||
76 | + |
@@ -0,0 +1,32 @@ | @@ -0,0 +1,32 @@ | ||
1 | +<tr> | ||
2 | + <td colspan='2'> | ||
3 | + | ||
4 | + <% plugins_tabs = @plugins.map(:profile_tabs) %> | ||
5 | + | ||
6 | + <% tabs = plugins_tabs.select { |tab| tab[:start] } %> | ||
7 | + | ||
8 | + <% if logged_in? && current_person.follows?(@profile) %> | ||
9 | + <% tabs << {:title => _('Wall'), :id => 'profile-wall', :content => (render :partial => 'profile_wall')} %> | ||
10 | + <% end %> | ||
11 | + | ||
12 | + <% if @profile.organization? %> | ||
13 | + <% tabs << {:title => _('What\'s new'), :id => 'profile-network', :content => (render :partial => 'profile_network')} %> | ||
14 | + <% tabs << {:title => _('Profile'), :id => 'organization-profile', :content => (render :partial => 'organization_profile')} %> | ||
15 | + <% elsif @profile.person? %> | ||
16 | + <% if logged_in? && current_person.follows?(@profile) %> | ||
17 | + <% tabs << {:title => _('Network'), :id => 'profile-network', :content => (render :partial => 'profile_network')} %> | ||
18 | + <% end %> | ||
19 | + | ||
20 | + <% if @profile.public? || (logged_in? && current_person.follows?(@profile)) %> | ||
21 | + <% tabs << {:title => _('Activity'), :id => 'profile-activity', :content => (render :partial => 'profile_activity')} %> | ||
22 | + <% end %> | ||
23 | + | ||
24 | + <% tabs << {:title => _('Profile'), :id => 'person-profile', :content => (render :partial => 'person_profile')} %> | ||
25 | + <% end %> | ||
26 | + | ||
27 | + <% tabs += plugins_tabs.select { |tab| !tab[:start] } %> | ||
28 | + | ||
29 | + <%= render_tabs(tabs) %> | ||
30 | + | ||
31 | + </td> | ||
32 | +</tr> |
app/views/profile/_profile_network.rhtml
1 | -<div id='profile-network'> | ||
2 | - <h3><%= _("%s's network activity") % @profile.name %></h3> | ||
3 | - <ul> | ||
4 | - <%= render :partial => 'profile_network_activities', :locals => {:network_activities => @network_activities} %> | ||
5 | - </ul> | ||
6 | -</div> | 1 | +<h3><%= _("%s's network activity") % @profile.name %></h3> |
2 | +<ul> | ||
3 | + <%= render :partial => 'profile_network_activities', :locals => {:network_activities => @network_activities} %> | ||
4 | +</ul> |
app/views/profile/_profile_wall.rhtml
1 | -<div id='profile-wall'> | ||
2 | - <h3><%= _("%s's wall") % @profile.name %></h3> | ||
3 | - <div id='leave_scrap'> | ||
4 | - <%= flash[:error] %> | ||
5 | - <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_scraps', :success => "$('leave_scrap_content').value=''" do %> | ||
6 | - <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2 %> | ||
7 | - <%= submit_button :scrap, _('Leave a scrap') %> | ||
8 | - <% end %> | ||
9 | - </div> | ||
10 | - <div id='leave_scrap_response'></div> | ||
11 | - <ul id='profile_scraps'> | ||
12 | - <%= render :partial => 'profile_scraps', :locals => {:scraps => @wall_items} %> | ||
13 | - </ul> | 1 | +<h3><%= _("%s's wall") % @profile.name %></h3> |
2 | +<div id='leave_scrap'> | ||
3 | + <%= flash[:error] %> | ||
4 | + <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_scraps', :success => "$('leave_scrap_content').value=''" do %> | ||
5 | + <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2 %> | ||
6 | + <%= submit_button :scrap, _('Leave a scrap') %> | ||
7 | + <% end %> | ||
14 | </div> | 8 | </div> |
9 | +<div id='leave_scrap_response'></div> | ||
10 | +<ul id='profile_scraps'> | ||
11 | + <%= render :partial => 'profile_scraps', :locals => {:scraps => @wall_items} %> | ||
12 | +</ul> |
app/views/profile/index.rhtml
app/views/profile_editor/index.rhtml
@@ -65,6 +65,11 @@ | @@ -65,6 +65,11 @@ | ||
65 | <% end %> | 65 | <% end %> |
66 | 66 | ||
67 | <%= control_panel_button(_('Manage my groups'), 'groups', :controller => 'memberships') if profile.person? %> | 67 | <%= control_panel_button(_('Manage my groups'), 'groups', :controller => 'memberships') if profile.person? %> |
68 | + | ||
69 | + <% @plugins.map(:control_panel_buttons).each do |button| %> | ||
70 | + <%= control_panel_button(button[:title], button[:icon], button[:url]) %> | ||
71 | + <% end %> | ||
72 | + | ||
68 | <% end %> | 73 | <% end %> |
69 | 74 | ||
70 | <% if profile.person? && environment.enabled?('enterprise_activation') %> | 75 | <% if profile.person? && environment.enabled?('enterprise_activation') %> |
config/routes.rb
@@ -109,6 +109,12 @@ ActionController::Routing::Routes.draw do |map| | @@ -109,6 +109,12 @@ ActionController::Routing::Routes.draw do |map| | ||
109 | map.system 'system', :controller => 'system' | 109 | map.system 'system', :controller => 'system' |
110 | map.system 'system/:controller/:action/:id', :controller => Noosfero.pattern_for_controllers_in_directory('system') | 110 | map.system 'system/:controller/:action/:id', :controller => Noosfero.pattern_for_controllers_in_directory('system') |
111 | 111 | ||
112 | + ###################################################### | ||
113 | + # plugin routes | ||
114 | + ###################################################### | ||
115 | + plugins_routes = File.join(Rails.root + '/lib/noosfero/plugin/routes.rb') | ||
116 | + eval(IO.read(plugins_routes), binding, plugins_routes) | ||
117 | + | ||
112 | # cache stuff - hack | 118 | # cache stuff - hack |
113 | map.cache 'public/:action/:id', :controller => 'public' | 119 | map.cache 'public/:action/:id', :controller => 'public' |
114 | 120 |
@@ -0,0 +1,59 @@ | @@ -0,0 +1,59 @@ | ||
1 | +Feature: plugins | ||
2 | + As a noosfero\'s developer | ||
3 | + I want to create hot spots that a plugin should use | ||
4 | + As a plugins\' developer | ||
5 | + I want to create plugins that uses noosfero\'s hot spots | ||
6 | + As an admin of a noosfero environment | ||
7 | + I want to activate and deactivate some plugins | ||
8 | + As a user | ||
9 | + I want to use the features implemented by the plugins | ||
10 | + | ||
11 | + Background: | ||
12 | + Given the following users | ||
13 | + | login | name | | ||
14 | + | joaosilva | Joao Silva | | ||
15 | + And I am logged in as "joaosilva" | ||
16 | + And the following plugin | ||
17 | + | klass | | ||
18 | + | TestPlugin | | ||
19 | + And the following events of TestPlugin | ||
20 | + | event | body | | ||
21 | + | control_panel_buttons | lambda { {:title => 'Test plugin button', :icon => '', :url => ''} } | | ||
22 | + | profile_tabs | lambda { {:title => 'Test plugin tab', :id => 'test_plugin', :content => 'Test plugin random content'} } | | ||
23 | + | ||
24 | + Scenario: a user must see the plugin\'s button in the control panel if the plugin is enabled | ||
25 | + Given plugin TestPlugin is enabled on environment | ||
26 | + And I go to Joao Silva's control panel | ||
27 | + Then I should see "Test plugin button" | ||
28 | + | ||
29 | + Scenario: a user must see the plugin\'s tab in in the profile if the plugin is enabled | ||
30 | + Given plugin TestPlugin is enabled on environment | ||
31 | + And I am on Joao Silva's profile | ||
32 | + Then I should see "Test plugin tab" | ||
33 | + | ||
34 | + Scenario: a user must not see the plugin\'s button in the control panel if the plugin is disabled | ||
35 | + Given plugin TestPlugin is disabled on environment | ||
36 | + And I go to Joao Silva's control panel | ||
37 | + Then I should not see "Test plugin button" | ||
38 | + | ||
39 | + Scenario: a user must not see the plugin\'s tab in in the profile if the plugin is disabled | ||
40 | + Given plugin TestPlugin is disabled on environment | ||
41 | + And I am on Joao Silva's profile | ||
42 | + Then I should not see "Test plugin tab" | ||
43 | + | ||
44 | + Scenario: an admin should be able to deactivate a plugin | ||
45 | + Given plugin TestPlugin is enabled on environment | ||
46 | + And I am logged in as admin | ||
47 | + When I go to the Control panel | ||
48 | + Then I should see "Test plugin button" | ||
49 | + When I go to the profile | ||
50 | + Then I should see "Test plugin tab" | ||
51 | + And I go to the environment control panel | ||
52 | + And I follow "Enable/disable plugins" | ||
53 | + And I uncheck "Test plugin" | ||
54 | + And I press "Save changes" | ||
55 | + When I go to the Control panel | ||
56 | + Then I should not see "Test plugin button" | ||
57 | + When I go to the profile | ||
58 | + Then I should not see "Test plugin tab" | ||
59 | + |
features/step_definitions/noosfero_steps.rb
@@ -146,6 +146,29 @@ Given /^the following certifiers$/ do |table| | @@ -146,6 +146,29 @@ Given /^the following certifiers$/ do |table| | ||
146 | end | 146 | end |
147 | end | 147 | end |
148 | 148 | ||
149 | +Given /^the following plugin?$/ do |table| | ||
150 | + table.hashes.each do |row| | ||
151 | + row = row.dup | ||
152 | + klass_name = row.delete('klass') | ||
153 | + eval("class #{klass_name} < Noosfero::Plugin; end;") unless eval("defined?(#{klass_name})") | ||
154 | + end | ||
155 | +end | ||
156 | + | ||
157 | +Given /^the following events of (.+)$/ do |plugin,table| | ||
158 | + klass = eval(plugin) | ||
159 | + table.hashes.each do |row| | ||
160 | + row = row.dup | ||
161 | + event = row.delete('event').to_sym | ||
162 | + body = eval(row.delete('body')) | ||
163 | + | ||
164 | + klass.class_eval do | ||
165 | + define_method(event) do | ||
166 | + body.call | ||
167 | + end | ||
168 | + end | ||
169 | + end | ||
170 | +end | ||
171 | + | ||
149 | Given /^I am logged in as "(.+)"$/ do |username| | 172 | Given /^I am logged in as "(.+)"$/ do |username| |
150 | visit('/account/logout') | 173 | visit('/account/logout') |
151 | visit('/account/login') | 174 | visit('/account/login') |
@@ -184,6 +207,16 @@ Given /^feature "(.+)" is (enabled|disabled) on environment$/ do |feature, statu | @@ -184,6 +207,16 @@ Given /^feature "(.+)" is (enabled|disabled) on environment$/ do |feature, statu | ||
184 | e.save | 207 | e.save |
185 | end | 208 | end |
186 | 209 | ||
210 | +Given /^plugin (.+) is (enabled|disabled) on environment$/ do |plugin, status| | ||
211 | + e = Environment.default | ||
212 | + if status == 'enabled' | ||
213 | + e.enabled_plugins += [plugin] | ||
214 | + else | ||
215 | + e.enabled_plugins -= [plugin] | ||
216 | + end | ||
217 | + e.save! | ||
218 | +end | ||
219 | + | ||
187 | Given /^organization_approval_method is "(.+)" on environment$/ do |approval_method| | 220 | Given /^organization_approval_method is "(.+)" on environment$/ do |approval_method| |
188 | e = Environment.default | 221 | e = Environment.default |
189 | e.organization_approval_method = approval_method | 222 | e.organization_approval_method = approval_method |
features/support/paths.rb
@@ -33,6 +33,9 @@ module NavigationHelpers | @@ -33,6 +33,9 @@ module NavigationHelpers | ||
33 | when /^(.*)'s profile/ | 33 | when /^(.*)'s profile/ |
34 | '/profile/%s' % Profile.find_by_name($1).identifier | 34 | '/profile/%s' % Profile.find_by_name($1).identifier |
35 | 35 | ||
36 | + when /^the profile$/ | ||
37 | + '/profile/%s' % User.find_by_id(session[:user]).login | ||
38 | + | ||
36 | when /^login page$/ | 39 | when /^login page$/ |
37 | '/account/login' | 40 | '/account/login' |
38 | 41 | ||
@@ -45,6 +48,9 @@ module NavigationHelpers | @@ -45,6 +48,9 @@ module NavigationHelpers | ||
45 | when /^the Control panel$/ | 48 | when /^the Control panel$/ |
46 | '/myprofile/%s' % User.find_by_id(session[:user]).login | 49 | '/myprofile/%s' % User.find_by_id(session[:user]).login |
47 | 50 | ||
51 | + when /the environment control panel/ | ||
52 | + '/admin' | ||
53 | + | ||
48 | when /^the search page$/ | 54 | when /^the search page$/ |
49 | '/search' | 55 | '/search' |
50 | 56 |
@@ -0,0 +1,75 @@ | @@ -0,0 +1,75 @@ | ||
1 | +require 'noosfero' | ||
2 | +class Noosfero::Plugin | ||
3 | + | ||
4 | + attr_accessor :context | ||
5 | + | ||
6 | + class << self | ||
7 | + | ||
8 | + def init_system | ||
9 | + Dir.glob(File.join(Rails.root, 'config', 'plugins', '*')).select do |entry| | ||
10 | + File.directory?(entry) | ||
11 | + end.each do |dir| | ||
12 | + Rails.configuration.controller_paths << File.join(dir, 'controllers') | ||
13 | + Dependencies.load_paths << File.join(dir, 'controllers') | ||
14 | + [ Dependencies.load_paths, $:].each do |path| | ||
15 | + path << File.join(dir, 'models') | ||
16 | + path << File.join(dir, 'lib') | ||
17 | + end | ||
18 | + | ||
19 | + plugin_name = File.basename(dir).camelize + 'Plugin' | ||
20 | + plugin_name.constantize # load the plugin | ||
21 | + end | ||
22 | + end | ||
23 | + | ||
24 | + def all | ||
25 | + @all ||= [] | ||
26 | + end | ||
27 | + | ||
28 | + def inherited(subclass) | ||
29 | + all << subclass.to_s unless all.include?(subclass.to_s) | ||
30 | + end | ||
31 | + | ||
32 | + # Here the developer should specify the meta-informations that the plugin can | ||
33 | + # inform. | ||
34 | + def plugin_name | ||
35 | + self.to_s.underscore.humanize | ||
36 | + end | ||
37 | + | ||
38 | + def plugin_description | ||
39 | + _("No description informed.") | ||
40 | + end | ||
41 | + | ||
42 | + end | ||
43 | + | ||
44 | + def expanded_template(original_path, file_path, locals = {}) | ||
45 | + while(File.basename(File.dirname(original_path)) != 'plugins') | ||
46 | + original_path = File.dirname(original_path) | ||
47 | + end | ||
48 | + | ||
49 | + ERB.new(File.read("#{original_path}/#{file_path}")).result(binding) | ||
50 | + end | ||
51 | + | ||
52 | + # Here the developer should specify the events to which the plugins can | ||
53 | + # register to. Must be explicitly defined its returning | ||
54 | + # variables. | ||
55 | + | ||
56 | + # -> Adds buttons to the control panel | ||
57 | + # returns = { :title => title, :icon => icon, :url => url } | ||
58 | + # title = name that will be displayed. | ||
59 | + # icon = css class name (for customized icons include them in a css file). | ||
60 | + # url = url or route to which the button will redirect. | ||
61 | + def control_panel_buttons | ||
62 | + nil | ||
63 | + end | ||
64 | + | ||
65 | + # -> Adds tabs to the profile | ||
66 | + # returns = { :title => title, :id => id, :content => content, :start => start } | ||
67 | + # title = name that will be displayed. | ||
68 | + # id = div id. | ||
69 | + # content = content of the tab (use expanded_template method to import content from another file). | ||
70 | + # start = boolean that specifies if the tab must come before noosfero tabs (optional). | ||
71 | + def profile_tabs | ||
72 | + nil | ||
73 | + end | ||
74 | + | ||
75 | +end |
@@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
1 | +class Noosfero::Plugin::Context | ||
2 | + | ||
3 | + def initialize(controller) | ||
4 | + @controller = controller | ||
5 | + end | ||
6 | + | ||
7 | + # Here the developer should define the interface to important context | ||
8 | + # information from the controller to the plugins to access | ||
9 | + def profile | ||
10 | + @profile ||= @controller.send(:profile) | ||
11 | + end | ||
12 | + | ||
13 | + def request | ||
14 | + @request ||= @controller.send(:request) | ||
15 | + end | ||
16 | + | ||
17 | + def response | ||
18 | + @response ||= @controller.send(:response) | ||
19 | + end | ||
20 | + | ||
21 | + def environment | ||
22 | + @environment ||= @controller.send(:environment) | ||
23 | + end | ||
24 | + | ||
25 | + def params | ||
26 | + @params ||= @controller.send(:params) | ||
27 | + end | ||
28 | + | ||
29 | +end |
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +class Noosfero::Plugin::Manager | ||
2 | + | ||
3 | + attr_reader :context | ||
4 | + | ||
5 | + def initialize(controller) | ||
6 | + @context = Noosfero::Plugin::Context.new(controller) | ||
7 | + end | ||
8 | + | ||
9 | + def map(event) | ||
10 | + enabled_plugins.map { |plugin| plugin.send(event) }.compact.flatten | ||
11 | + end | ||
12 | + | ||
13 | + def enabled_plugins | ||
14 | + @enabled_plugins ||= (Noosfero::Plugin.all & context.environment.enabled_plugins).map do |plugin| | ||
15 | + p = eval(plugin).new | ||
16 | + p.context = context | ||
17 | + p | ||
18 | + end | ||
19 | + end | ||
20 | + | ||
21 | +end |
@@ -0,0 +1,7 @@ | @@ -0,0 +1,7 @@ | ||
1 | +Dir.glob(File.join(Rails.root, 'config', 'plugins', '*', 'controllers')) do |dir| | ||
2 | + plugin_name = File.basename(File.dirname(dir)) | ||
3 | + map.connect 'plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_environment' | ||
4 | + map.connect 'profile/:profile/plugins/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_profile' | ||
5 | + map.connect 'myprofile/:profile/plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_myprofile' | ||
6 | +end | ||
7 | + |
@@ -0,0 +1,9 @@ | @@ -0,0 +1,9 @@ | ||
1 | +namespace :plugins do | ||
2 | + task :migrate do | ||
3 | + Dir.glob(File.join(Rails.root, 'config', 'plugins', '*', 'db', 'migrate')).each do |path| | ||
4 | + ActiveRecord::Migrator.migrate(path, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) | ||
5 | + end | ||
6 | + end | ||
7 | +end | ||
8 | + | ||
9 | +task 'db:migrate' => 'plugins:migrate' |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +This plugin depends on ruby binds for subversion. Checkout http://packages.debian.org/lenny/libsvn-ruby and http://subversion.tigris.org/ for further information. |
plugins/mezuro/controllers/mezuro_plugin_myprofile_controller.rb
0 → 100644
@@ -0,0 +1,55 @@ | @@ -0,0 +1,55 @@ | ||
1 | +class MezuroPluginMyprofileController < MyProfileController | ||
2 | + append_view_path File.join(File.dirname(__FILE__) + '/../views') | ||
3 | + | ||
4 | + def index | ||
5 | + @projects = MezuroPlugin::Project.by_profile(profile) | ||
6 | + end | ||
7 | + | ||
8 | + def new | ||
9 | + @project = MezuroPlugin::Project.new | ||
10 | + end | ||
11 | + | ||
12 | + def create | ||
13 | + @project = MezuroPlugin::Project.new(params[:project]) | ||
14 | + if @project.save | ||
15 | + session[:notice] = _('Project successfully registered') | ||
16 | + redirect_to :action => 'index' | ||
17 | + else | ||
18 | + render :action => 'new' | ||
19 | + end | ||
20 | + end | ||
21 | + | ||
22 | + def edit | ||
23 | + @project = MezuroPlugin::Project.find(params[:id]) | ||
24 | + end | ||
25 | + | ||
26 | + def update | ||
27 | + @project = MezuroPlugin::Project.find(params[:id]) | ||
28 | + if @project.update_attributes(params[:project]) | ||
29 | + session[:notice] = _('Project successfully updated') | ||
30 | + redirect_to :action => 'index' | ||
31 | + else | ||
32 | + render :action => 'edit' | ||
33 | + end | ||
34 | + end | ||
35 | + | ||
36 | + def show | ||
37 | + @project = MezuroPlugin::Project.find_by_identifier params[:identifier] | ||
38 | + @total_metrics = @project.total_metrics if @project != nil | ||
39 | + @statistical_metrics = @project.statistical_metrics if @project != nil | ||
40 | + @svn_error = @project.svn_error if (@project != nil && @project.svn_error) | ||
41 | + end | ||
42 | + | ||
43 | + def destroy | ||
44 | + @project = MezuroPlugin::Project.by_profile(profile).find(params[:id]) | ||
45 | + if request.post? | ||
46 | + if @project.destroy | ||
47 | + session[:notice] = _('Project successfully removed.') | ||
48 | + else | ||
49 | + session[:notice] = _('Project was not successfully removed.') | ||
50 | + end | ||
51 | + redirect_to :action => 'index' | ||
52 | + end | ||
53 | + end | ||
54 | + | ||
55 | +end |
plugins/mezuro/db/migrate/20101209151530_create_projects.rb
0 → 100644
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +class CreateProjects < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + create_table :mezuro_plugin_projects do |t| | ||
4 | + t.string :name | ||
5 | + t.string :identifier | ||
6 | + t.string :personal_webpage | ||
7 | + t.text :description | ||
8 | + t.string :repository_url | ||
9 | + t.string :svn_error | ||
10 | + t.boolean :with_tab | ||
11 | + t.references :profile | ||
12 | + | ||
13 | + t.timestamps | ||
14 | + end | ||
15 | + end | ||
16 | + | ||
17 | + def self.down | ||
18 | + drop_table :mezuro_plugin_projects | ||
19 | + end | ||
20 | +end |
plugins/mezuro/db/migrate/20101209151640_create_metrics.rb
0 → 100644
@@ -0,0 +1,16 @@ | @@ -0,0 +1,16 @@ | ||
1 | +class CreateMetrics < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + create_table :mezuro_plugin_metrics do |t| | ||
4 | + t.string :name | ||
5 | + t.float :value | ||
6 | + t.integer :metricable_id | ||
7 | + t.string :metricable_type | ||
8 | + | ||
9 | + t.timestamps | ||
10 | + end | ||
11 | + end | ||
12 | + | ||
13 | + def self.down | ||
14 | + drop_table :mezuro_plugin_metrics | ||
15 | + end | ||
16 | +end |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +require "mezuro_plugin" |
@@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
1 | +class MezuroPlugin < Noosfero::Plugin | ||
2 | + | ||
3 | + def self.plugin_name | ||
4 | + "Mezuro" | ||
5 | + end | ||
6 | + | ||
7 | + def self.plugin_description | ||
8 | + _("A metric analizer plugin.") | ||
9 | + end | ||
10 | + | ||
11 | + def control_panel_buttons | ||
12 | + if context.profile.community? | ||
13 | + { :title => 'Mezuro projects', :icon => 'mezuro', :url => {:controller => 'mezuro_plugin_myprofile', :action => 'index'} } | ||
14 | + end | ||
15 | + end | ||
16 | + | ||
17 | + def profile_tabs | ||
18 | + if context.profile.community? && !MezuroPlugin::Project.by_profile(context.profile).blank? | ||
19 | + MezuroPlugin::Project.by_profile(context.profile).with_tab.map do |project| | ||
20 | + { :title => 'Mezuro ' + project.name, | ||
21 | + :id => 'mezuro-project-'+project.identifier, | ||
22 | + :content => expanded_template(__FILE__,"views/show.html.erb",{:current_project => project}) } | ||
23 | + end | ||
24 | + end | ||
25 | + end | ||
26 | + | ||
27 | +end |
@@ -0,0 +1,43 @@ | @@ -0,0 +1,43 @@ | ||
1 | +class MezuroPlugin::AnalizoExtractor < Noosfero::Plugin::ActiveRecord | ||
2 | + attr_reader :string_output, :hash_output | ||
3 | + | ||
4 | + def initialize project | ||
5 | + @project = project | ||
6 | + end | ||
7 | + | ||
8 | + def perform | ||
9 | + run_analizo | ||
10 | + create_hash | ||
11 | + save_metrics | ||
12 | + end | ||
13 | + | ||
14 | + def run_analizo | ||
15 | + project_path = "#{RAILS_ROOT}/tmp/#{@project.identifier}" | ||
16 | + @string_output = `analizo metrics #{project_path}` | ||
17 | + end | ||
18 | + | ||
19 | + def create_hash | ||
20 | + @hash_output = {} | ||
21 | + first_line = true | ||
22 | + | ||
23 | + @string_output.lines.each do |line| | ||
24 | + if line =~ /---/ | ||
25 | + if first_line | ||
26 | + first_line = false | ||
27 | + else | ||
28 | + break | ||
29 | + end | ||
30 | + end | ||
31 | + | ||
32 | + if line =~ /(\S+): (~|(\d+)(\.\d+)?).*/ | ||
33 | + @hash_output[$1.to_sym] = $2 | ||
34 | + end | ||
35 | + end | ||
36 | + end | ||
37 | + | ||
38 | + def save_metrics | ||
39 | + @hash_output.each do | key, value | | ||
40 | + MezuroPlugin::Metric.create(:name => key.to_s, :value => value.to_f, :metricable => @project) | ||
41 | + end | ||
42 | + end | ||
43 | +end |
plugins/mezuro/lib/mezuro_plugin/calculate_metrics_job.rb
0 → 100644
@@ -0,0 +1,19 @@ | @@ -0,0 +1,19 @@ | ||
1 | +class MezuroPlugin::Metric < Noosfero::Plugin::ActiveRecord | ||
2 | + validates_presence_of :name, :metricable_id, :metricable_type | ||
3 | + | ||
4 | + belongs_to :metricable, :polymorphic => true | ||
5 | + before_save :round_value | ||
6 | + | ||
7 | + def initialize params | ||
8 | + params[:value] = nil if params[:value] == '~' | ||
9 | + super params | ||
10 | + end | ||
11 | + | ||
12 | + def round_value | ||
13 | + if self.value | ||
14 | + multiplied = self.value * 100 | ||
15 | + rounded = multiplied.round | ||
16 | + self.value = rounded / 100.0 | ||
17 | + end | ||
18 | + end | ||
19 | +end |
@@ -0,0 +1,74 @@ | @@ -0,0 +1,74 @@ | ||
1 | +class MezuroPlugin::Project < Noosfero::Plugin::ActiveRecord | ||
2 | + has_many :metrics, :as => :metricable | ||
3 | + | ||
4 | + validates_presence_of :name, :repository_url, :identifier | ||
5 | + validates_format_of :identifier, :with => /^[a-z0-9|\-|\.]*$/, :message => "Identifier can only have a combination of lower case, number, hyphen and dot!" | ||
6 | + validates_uniqueness_of :identifier | ||
7 | + | ||
8 | + named_scope :with_tab, :conditions => {:with_tab => true} | ||
9 | + named_scope :by_profile, lambda {|profile| {:conditions => {:profile_id => profile.id}}} | ||
10 | + | ||
11 | + | ||
12 | + after_create :asynchronous_calculate_metrics | ||
13 | + | ||
14 | + def calculate_metrics | ||
15 | + begin | ||
16 | + download_source_code | ||
17 | + extractor = MezuroPlugin::AnalizoExtractor.new self | ||
18 | + extractor.perform | ||
19 | + rescue Svn::Error => error | ||
20 | + update_attribute :svn_error, error.error_message | ||
21 | + end | ||
22 | + end | ||
23 | + | ||
24 | + def asynchronous_calculate_metrics | ||
25 | + Delayed::Job.enqueue MezuroPlugin::CalculateMetricsJob.new(id) | ||
26 | + end | ||
27 | + | ||
28 | + def download_source_code | ||
29 | + download_prepare | ||
30 | + Svn::Client::Context.new.checkout(repository_url, "#{RAILS_ROOT}/tmp/#{identifier}") | ||
31 | + end | ||
32 | + | ||
33 | + def download_prepare | ||
34 | + project_path = "#{RAILS_ROOT}/tmp/#{identifier}" | ||
35 | + FileUtils.rm_r project_path if (File.exists? project_path) | ||
36 | + end | ||
37 | + | ||
38 | + def metrics_calculated? | ||
39 | + return !metrics.empty? | ||
40 | + end | ||
41 | + | ||
42 | + def total_metrics | ||
43 | + total_metrics = metrics.select do |metric| | ||
44 | + metric.name.start_with?("total") | ||
45 | + end | ||
46 | + return total_metrics.sort_by {|metric| metric.name} | ||
47 | + end | ||
48 | + | ||
49 | + def statistical_metrics | ||
50 | + statistical_metrics = collect_statistical_metrics | ||
51 | + | ||
52 | + hash = {} | ||
53 | + statistical_metrics.each do |metric| | ||
54 | + insert_metric_in_hash metric, hash | ||
55 | + end | ||
56 | + hash | ||
57 | + end | ||
58 | + | ||
59 | + def collect_statistical_metrics | ||
60 | + statistical_metrics = metrics.select do |metric| | ||
61 | + not metric.name.start_with?("total") | ||
62 | + end | ||
63 | + statistical_metrics.sort_by {|metric| metric.name} | ||
64 | + end | ||
65 | + | ||
66 | + def insert_metric_in_hash metric, hash | ||
67 | + metric_name, metric_statistic = metric.name.split("_") | ||
68 | + unless hash.key?(metric_name) | ||
69 | + hash[metric_name] = {metric_statistic => metric.value} | ||
70 | + else | ||
71 | + hash[metric_name][metric_statistic] = metric.value | ||
72 | + end | ||
73 | + end | ||
74 | +end |
@@ -0,0 +1,47 @@ | @@ -0,0 +1,47 @@ | ||
1 | +# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. | ||
2 | +# It is recommended to regenerate this file in the future when you upgrade to a | ||
3 | +# newer version of cucumber-rails. Consider adding your own code to a new file | ||
4 | +# instead of editing this one. Cucumber will automatically load all features/**/*.rb | ||
5 | +# files. | ||
6 | + | ||
7 | + | ||
8 | +unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks | ||
9 | + | ||
10 | +vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first | ||
11 | +$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil? | ||
12 | + | ||
13 | +begin | ||
14 | + require 'cucumber/rake/task' | ||
15 | + | ||
16 | + namespace :cucumber do | ||
17 | + Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t| | ||
18 | + t.binary = vendored_cucumber_bin # If nil, the gem's binary is used. | ||
19 | + t.fork = true # You may get faster startup if you set this to false | ||
20 | + t.profile = 'default' | ||
21 | + end | ||
22 | + | ||
23 | + Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t| | ||
24 | + t.binary = vendored_cucumber_bin | ||
25 | + t.fork = true # You may get faster startup if you set this to false | ||
26 | + t.profile = 'wip' | ||
27 | + end | ||
28 | + | ||
29 | + desc 'Run all features' | ||
30 | + task :all => [:ok, :wip] | ||
31 | + end | ||
32 | + desc 'Alias for cucumber:ok' | ||
33 | + task :cucumber => 'cucumber:ok' | ||
34 | + | ||
35 | + task :default => :cucumber | ||
36 | + | ||
37 | + task :features => :cucumber do | ||
38 | + STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***" | ||
39 | + end | ||
40 | +rescue LoadError | ||
41 | + desc 'cucumber rake task not available (cucumber not installed)' | ||
42 | + task :cucumber do | ||
43 | + abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin' | ||
44 | + end | ||
45 | +end | ||
46 | + | ||
47 | +end |
@@ -0,0 +1,144 @@ | @@ -0,0 +1,144 @@ | ||
1 | +gem 'test-unit', '1.2.3' if RUBY_VERSION.to_f >= 1.9 | ||
2 | +rspec_gem_dir = nil | ||
3 | +Dir["#{RAILS_ROOT}/vendor/gems/*"].each do |subdir| | ||
4 | + rspec_gem_dir = subdir if subdir.gsub("#{RAILS_ROOT}/vendor/gems/","") =~ /^(\w+-)?rspec-(\d+)/ && File.exist?("#{subdir}/lib/spec/rake/spectask.rb") | ||
5 | +end | ||
6 | +rspec_plugin_dir = File.expand_path(File.dirname(__FILE__) + '/../../vendor/plugins/rspec') | ||
7 | + | ||
8 | +if rspec_gem_dir && (test ?d, rspec_plugin_dir) | ||
9 | + raise "\n#{'*'*50}\nYou have rspec installed in both vendor/gems and vendor/plugins\nPlease pick one and dispose of the other.\n#{'*'*50}\n\n" | ||
10 | +end | ||
11 | + | ||
12 | +if rspec_gem_dir | ||
13 | + $LOAD_PATH.unshift("#{rspec_gem_dir}/lib") | ||
14 | +elsif File.exist?(rspec_plugin_dir) | ||
15 | + $LOAD_PATH.unshift("#{rspec_plugin_dir}/lib") | ||
16 | +end | ||
17 | + | ||
18 | +# Don't load rspec if running "rake gems:*" | ||
19 | +unless ARGV.any? {|a| a =~ /^gems/} | ||
20 | + | ||
21 | +begin | ||
22 | + require 'spec/rake/spectask' | ||
23 | +rescue MissingSourceFile | ||
24 | + module Spec | ||
25 | + module Rake | ||
26 | + class SpecTask | ||
27 | + def initialize(name) | ||
28 | + task name do | ||
29 | + # if rspec-rails is a configured gem, this will output helpful material and exit ... | ||
30 | + require File.expand_path(File.join(File.dirname(__FILE__),"..","..","config","environment")) | ||
31 | + | ||
32 | + # ... otherwise, do this: | ||
33 | + raise <<-MSG | ||
34 | + | ||
35 | +#{"*" * 80} | ||
36 | +* You are trying to run an rspec rake task defined in | ||
37 | +* #{__FILE__}, | ||
38 | +* but rspec can not be found in vendor/gems, vendor/plugins or system gems. | ||
39 | +#{"*" * 80} | ||
40 | +MSG | ||
41 | + end | ||
42 | + end | ||
43 | + end | ||
44 | + end | ||
45 | + end | ||
46 | +end | ||
47 | + | ||
48 | +Rake.application.instance_variable_get('@tasks').delete('default') | ||
49 | + | ||
50 | +spec_prereq = File.exist?(File.join(RAILS_ROOT, 'config', 'database.yml')) ? "db:test:prepare" : :noop | ||
51 | +task :noop do | ||
52 | +end | ||
53 | + | ||
54 | +task :default => :spec | ||
55 | +task :stats => "spec:statsetup" | ||
56 | + | ||
57 | +desc "Run all specs in spec directory (excluding plugin specs)" | ||
58 | +Spec::Rake::SpecTask.new(:spec => spec_prereq) do |t| | ||
59 | + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] | ||
60 | + t.spec_files = FileList['spec/**/*_spec.rb'] | ||
61 | +end | ||
62 | + | ||
63 | +namespace :spec do | ||
64 | + desc "Run all specs in spec directory with RCov (excluding plugin specs)" | ||
65 | + Spec::Rake::SpecTask.new(:rcov) do |t| | ||
66 | + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] | ||
67 | + t.spec_files = FileList['spec/**/*_spec.rb'] | ||
68 | + t.rcov = true | ||
69 | + t.rcov_opts = lambda do | ||
70 | + IO.readlines("#{RAILS_ROOT}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten | ||
71 | + end | ||
72 | + end | ||
73 | + | ||
74 | + desc "Print Specdoc for all specs (excluding plugin specs)" | ||
75 | + Spec::Rake::SpecTask.new(:doc) do |t| | ||
76 | + t.spec_opts = ["--format", "specdoc", "--dry-run"] | ||
77 | + t.spec_files = FileList['spec/**/*_spec.rb'] | ||
78 | + end | ||
79 | + | ||
80 | + desc "Print Specdoc for all plugin examples" | ||
81 | + Spec::Rake::SpecTask.new(:plugin_doc) do |t| | ||
82 | + t.spec_opts = ["--format", "specdoc", "--dry-run"] | ||
83 | + t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*') | ||
84 | + end | ||
85 | + | ||
86 | + [:models, :controllers, :views, :helpers, :lib, :integration].each do |sub| | ||
87 | + desc "Run the code examples in spec/#{sub}" | ||
88 | + Spec::Rake::SpecTask.new(sub => spec_prereq) do |t| | ||
89 | + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] | ||
90 | + t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"] | ||
91 | + end | ||
92 | + end | ||
93 | + | ||
94 | + desc "Run the code examples in vendor/plugins (except RSpec's own)" | ||
95 | + Spec::Rake::SpecTask.new(:plugins => spec_prereq) do |t| | ||
96 | + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] | ||
97 | + t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*').exclude("vendor/plugins/rspec-rails/*") | ||
98 | + end | ||
99 | + | ||
100 | + namespace :plugins do | ||
101 | + desc "Runs the examples for rspec_on_rails" | ||
102 | + Spec::Rake::SpecTask.new(:rspec_on_rails) do |t| | ||
103 | + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] | ||
104 | + t.spec_files = FileList['vendor/plugins/rspec-rails/spec/**/*_spec.rb'] | ||
105 | + end | ||
106 | + end | ||
107 | + | ||
108 | + # Setup specs for stats | ||
109 | + task :statsetup do | ||
110 | + require 'code_statistics' | ||
111 | + ::STATS_DIRECTORIES << %w(Model\ specs spec/models) if File.exist?('spec/models') | ||
112 | + ::STATS_DIRECTORIES << %w(View\ specs spec/views) if File.exist?('spec/views') | ||
113 | + ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers) if File.exist?('spec/controllers') | ||
114 | + ::STATS_DIRECTORIES << %w(Helper\ specs spec/helpers) if File.exist?('spec/helpers') | ||
115 | + ::STATS_DIRECTORIES << %w(Library\ specs spec/lib) if File.exist?('spec/lib') | ||
116 | + ::STATS_DIRECTORIES << %w(Routing\ specs spec/routing) if File.exist?('spec/routing') | ||
117 | + ::STATS_DIRECTORIES << %w(Integration\ specs spec/integration) if File.exist?('spec/integration') | ||
118 | + ::CodeStatistics::TEST_TYPES << "Model specs" if File.exist?('spec/models') | ||
119 | + ::CodeStatistics::TEST_TYPES << "View specs" if File.exist?('spec/views') | ||
120 | + ::CodeStatistics::TEST_TYPES << "Controller specs" if File.exist?('spec/controllers') | ||
121 | + ::CodeStatistics::TEST_TYPES << "Helper specs" if File.exist?('spec/helpers') | ||
122 | + ::CodeStatistics::TEST_TYPES << "Library specs" if File.exist?('spec/lib') | ||
123 | + ::CodeStatistics::TEST_TYPES << "Routing specs" if File.exist?('spec/routing') | ||
124 | + ::CodeStatistics::TEST_TYPES << "Integration specs" if File.exist?('spec/integration') | ||
125 | + end | ||
126 | + | ||
127 | + namespace :db do | ||
128 | + namespace :fixtures do | ||
129 | + desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z." | ||
130 | + task :load => :environment do | ||
131 | + ActiveRecord::Base.establish_connection(Rails.env) | ||
132 | + base_dir = File.join(Rails.root, 'spec', 'fixtures') | ||
133 | + fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir | ||
134 | + | ||
135 | + require 'active_record/fixtures' | ||
136 | + (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file| | ||
137 | + Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*')) | ||
138 | + end | ||
139 | + end | ||
140 | + end | ||
141 | + end | ||
142 | +end | ||
143 | + | ||
144 | +end |
2 KB
3.65 KB
215 Bytes
400 Bytes
plugins/mezuro/views/mezuro_plugin_myprofile/_form.html.erb
0 → 100644
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +<%= error_messages_for 'project' %> | ||
2 | + | ||
3 | +<% labelled_form_for :project, @project, :url => @url do |f| %> | ||
4 | + <%= hidden_field_tag 'project[profile_id]', profile.id %> | ||
5 | + <%= hidden_field_tag 'id', @project.id %> | ||
6 | + | ||
7 | + <%= required_fields_message %> | ||
8 | + | ||
9 | + <%= required f.text_field(:name) %> | ||
10 | + <%= required f.text_field(:repository_url) %> | ||
11 | + <%= required f.text_field(:identifier) %> | ||
12 | + <%= f.check_box(:with_tab) %> | ||
13 | + <%= f.text_area(:description, :size => '30x3') %> | ||
14 | + | ||
15 | + <% button_bar do %> | ||
16 | + <%= submit_button(:save, @submit_button, :cancel => {:action => 'index'})%> | ||
17 | + <% end %> | ||
18 | +<% end %> | ||
19 | + | ||
20 | +<%= javascript_tag "$('project_name').focus();" %> |
plugins/mezuro/views/mezuro_plugin_myprofile/edit.html.erb
0 → 100644
plugins/mezuro/views/mezuro_plugin_myprofile/index.html.erb
0 → 100644
@@ -0,0 +1,28 @@ | @@ -0,0 +1,28 @@ | ||
1 | +<h1> <%= _("%s's Mezuro projects") % profile.name %> </h1> | ||
2 | + | ||
3 | +<% if @projects.blank? %> | ||
4 | + <%= _("%s has no projects registered.") % profile.name %> | ||
5 | +<% else %> | ||
6 | + <table> | ||
7 | + <tr> | ||
8 | + <th><%= _('Project') %></th> | ||
9 | + <th><%= _('Actions') %></th> | ||
10 | + </tr> | ||
11 | + | ||
12 | + <% @projects.each do |project| %> | ||
13 | + <tr> | ||
14 | + <td><%= project.name %></td> | ||
15 | + <td class="article-controls"> | ||
16 | + <%= button_without_text :edit, _('Edit'), :action => 'edit', :id => project.id %> | ||
17 | + <%= button_without_text :delete, _('Delete'), { :action => 'destroy', :id => project.id }, | ||
18 | + :method => :post, | ||
19 | + :confirm => _("Are you sure you want to remove this project?") %> | ||
20 | + </td> | ||
21 | + </tr> | ||
22 | + <% end %> | ||
23 | + </table> | ||
24 | +<% end %> | ||
25 | + | ||
26 | +<% button_bar do %> | ||
27 | + <%= button :new, _('Register a new project'), :action => 'new' %> | ||
28 | +<% end %> |
plugins/mezuro/views/mezuro_plugin_myprofile/new.html.erb
0 → 100644
@@ -0,0 +1,81 @@ | @@ -0,0 +1,81 @@ | ||
1 | +<% @project = locals[:current_project] %> | ||
2 | +<% @total_metrics = @project.total_metrics if @project != nil %> | ||
3 | +<% @statistical_metrics = @project.statistical_metrics if @project != nil %> | ||
4 | +<% @svn_error = @project.svn_error if (@project != nil && @project.svn_error) %> | ||
5 | + | ||
6 | +<h1> <%= @project.name %>'s Info</h1> | ||
7 | +<table id="show_info"> | ||
8 | + <tr> | ||
9 | + <td><%= _("Name") %></td> | ||
10 | + <td><%= @project.name %></td> | ||
11 | + </tr> | ||
12 | + <% if (@project.description != nil && @project.description != "" ) %> | ||
13 | + <tr> | ||
14 | + <td><%= _("Description") %></td> | ||
15 | + <td><%= @project.description %></td> | ||
16 | + </tr> | ||
17 | + <% end %> | ||
18 | + <tr> | ||
19 | + <td><%= _("Repository address") %></td> | ||
20 | + <td><%= @project.repository_url %></td> | ||
21 | + </tr> | ||
22 | +</table> | ||
23 | + | ||
24 | +<br /> | ||
25 | + | ||
26 | + | ||
27 | +<% if @svn_error %> | ||
28 | + <h3>ERROR</h3> | ||
29 | + <div id="svn_error"> | ||
30 | + <%= @svn_error %> | ||
31 | + </div> | ||
32 | + <br /> | ||
33 | + <br /> | ||
34 | + <p> | ||
35 | + <%= _("Possible causes:") %> | ||
36 | + <ul> | ||
37 | + <li> | ||
38 | + <%= _("Server is down") %> | ||
39 | + </li> | ||
40 | + <li> | ||
41 | + <% _("URL invalid, in this case create another project with the correct URL | ||
42 | + (Sorry for the incovenience, we're working for a better solution)") %> | ||
43 | + </li> | ||
44 | + </ul> | ||
45 | + </p> | ||
46 | +<%else%> | ||
47 | + <h3> <%= _("Metric Results") %> </h3> | ||
48 | + <% if @project.metrics_calculated? %> | ||
49 | + <h3> <% _("Total Metrics") %> </h3> | ||
50 | + <table id="total_metrics"> | ||
51 | + <% @total_metrics.each_with_index do |metric, index| %> | ||
52 | + <tr id="tr_<%= metric.name %>" class="d<%= index % 2 %>"> | ||
53 | + <td class="metric_name"> <%= metric.name %> </td> | ||
54 | + <td class="metric_value"> <%= metric.value %> </td> | ||
55 | + </tr> | ||
56 | + <% end %> | ||
57 | + </table> | ||
58 | + | ||
59 | + <h3> <%= _("Statistical Metrics") %> </h3> | ||
60 | + <% @statistical_metrics.each_key do |metric_name| %> | ||
61 | + <div id="statistical_metrics"> | ||
62 | + <%= "#{metric_name}_average: #{@statistical_metrics[metric_name]["average"]}" %> | ||
63 | + <ul> | ||
64 | + <% @statistical_metrics[metric_name].each do |stat_name, stat_value| %> | ||
65 | + <% if stat_name != "average" %> | ||
66 | + <li> | ||
67 | + <%= "#{metric_name}_#{stat_name}: #{stat_value}" %> | ||
68 | + </li> | ||
69 | + <% end %> | ||
70 | + <% end %> | ||
71 | + </ul> | ||
72 | + </div> | ||
73 | + <% end %> | ||
74 | + | ||
75 | + <% else %> | ||
76 | + <div id="progress_message"> | ||
77 | + <%= _("Wait a moment while the metrics are calculated.<br/> | ||
78 | + Reload the page manually in a few moment. ") %> | ||
79 | + </div> | ||
80 | + <% end %> | ||
81 | +<% end %> |
@@ -0,0 +1,68 @@ | @@ -0,0 +1,68 @@ | ||
1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
2 | +require 'plugins_controller' | ||
3 | + | ||
4 | +# Re-raise errors caught by the controller. | ||
5 | +class PluginsController; def rescue_action(e) raise e end; end | ||
6 | + | ||
7 | +class PluginsControllerTest < Test::Unit::TestCase | ||
8 | + | ||
9 | + all_fixtures | ||
10 | + def setup | ||
11 | + @controller = PluginsController.new | ||
12 | + @request = ActionController::TestRequest.new | ||
13 | + @request.stubs(:ssl?).returns(true) | ||
14 | + @response = ActionController::TestResponse.new | ||
15 | + @environment = Environment.default | ||
16 | + login_as(create_admin_user(@environment)) | ||
17 | + end | ||
18 | + attr_reader :environment | ||
19 | + | ||
20 | + def test_local_files_reference | ||
21 | + assert_local_files_reference | ||
22 | + end | ||
23 | + | ||
24 | + def test_valid_xhtml | ||
25 | + assert_valid_xhtml | ||
26 | + end | ||
27 | + | ||
28 | + should 'list system active plugins' do | ||
29 | + class Plugin1 < Noosfero::Plugin | ||
30 | + class << self | ||
31 | + def plugin_name | ||
32 | + "Plugin1" | ||
33 | + end | ||
34 | + def plugin_description | ||
35 | + "This plugin is from hell!" | ||
36 | + end | ||
37 | + end | ||
38 | + end | ||
39 | + | ||
40 | + class Plugin2 < Noosfero::Plugin | ||
41 | + class << self | ||
42 | + def plugin_name | ||
43 | + "Plugin2" | ||
44 | + end | ||
45 | + def plugin_description | ||
46 | + "This plugin is from heaven!" | ||
47 | + end | ||
48 | + end | ||
49 | + end | ||
50 | + | ||
51 | + Noosfero::Plugin.stubs(:all).returns([Plugin1.to_s,Plugin2.to_s]) | ||
52 | + | ||
53 | + get :index | ||
54 | + | ||
55 | + assert_tag :tag => 'td', :content => /#{Plugin1.plugin_name}/ | ||
56 | + assert_tag :tag => 'td', :content => /#{Plugin1.plugin_description}/ | ||
57 | + assert_tag :tag => 'td', :content => /#{Plugin2.plugin_name}/ | ||
58 | + assert_tag :tag => 'td', :content => /#{Plugin2.plugin_description}/ | ||
59 | + end | ||
60 | + | ||
61 | + should 'enable or disable plugins' do | ||
62 | + assert_not_equal ['Plugin1'], environment.enabled_plugins | ||
63 | + post :update, :environment => { :enabled_plugins => ['Plugin1']} | ||
64 | + environment.reload | ||
65 | + assert_equal ['Plugin1'], environment.enabled_plugins | ||
66 | + end | ||
67 | + | ||
68 | +end |
test/functional/profile_controller_test.rb
@@ -1113,4 +1113,18 @@ class ProfileControllerTest < Test::Unit::TestCase | @@ -1113,4 +1113,18 @@ class ProfileControllerTest < Test::Unit::TestCase | ||
1113 | assert_equal '', @response.body | 1113 | assert_equal '', @response.body |
1114 | end | 1114 | end |
1115 | 1115 | ||
1116 | + should 'display plugins tabs' do | ||
1117 | + plugin1_tab = {:title => 'Plugin1 tab', :id => 'plugin1_tab', :content => 'Content from plugin1.'} | ||
1118 | + plugin2_tab = {:title => 'Plugin2 tab', :id => 'plugin2_tab', :content => 'Content from plugin2.'} | ||
1119 | + tabs = [plugin1_tab, plugin2_tab] | ||
1120 | + plugins = mock() | ||
1121 | + plugins.stubs(:map).with(:profile_tabs).returns(tabs) | ||
1122 | + Noosfero::Plugin::Manager.stubs(:new).returns(plugins) | ||
1123 | + | ||
1124 | + get :index, :profile => profile.identifier | ||
1125 | + | ||
1126 | + assert_tag :tag => 'a', :content => /#{plugin1_tab[:title]}/, :attributes => {:href => /#{plugin1_tab[:id]}/} | ||
1127 | + assert_tag :tag => 'div', :content => /#{plugin1_tab[:content]}/, :attributes => {:id => /#{plugin1_tab[:id]}/} | ||
1128 | + end | ||
1129 | + | ||
1116 | end | 1130 | end |
test/functional/profile_editor_controller_test.rb
@@ -858,4 +858,18 @@ class ProfileEditorControllerTest < Test::Unit::TestCase | @@ -858,4 +858,18 @@ class ProfileEditorControllerTest < Test::Unit::TestCase | ||
858 | end | 858 | end |
859 | end | 859 | end |
860 | 860 | ||
861 | + should 'display plugins buttons on the control panel' do | ||
862 | + plugin1_button = {:title => "Plugin1 button", :icon => 'plugin1_icon', :url => 'plugin1_url'} | ||
863 | + plugin2_button = {:title => "Plugin2 button", :icon => 'plugin2_icon', :url => 'plugin2_url'} | ||
864 | + buttons = [plugin1_button, plugin2_button] | ||
865 | + plugins = mock() | ||
866 | + plugins.stubs(:map).with(:control_panel_buttons).returns(buttons) | ||
867 | + Noosfero::Plugin::Manager.stubs(:new).returns(plugins) | ||
868 | + | ||
869 | + get :index, :profile => profile.identifier | ||
870 | + | ||
871 | + assert_tag :tag => 'a', :content => plugin1_button[:title], :attributes => {:class => /#{plugin1_button[:icon]}/, :href => /#{plugin1_button[:url]}/} | ||
872 | + assert_tag :tag => 'a', :content => plugin2_button[:title], :attributes => {:class => /#{plugin2_button[:icon]}/, :href => /#{plugin2_button[:url]}/} | ||
873 | + end | ||
874 | + | ||
861 | end | 875 | end |
@@ -0,0 +1,58 @@ | @@ -0,0 +1,58 @@ | ||
1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
2 | + | ||
3 | +class ManagerTest < Test::Unit::TestCase | ||
4 | + | ||
5 | + def setup | ||
6 | + @environment = Environment.default | ||
7 | + @controller = mock() | ||
8 | + @controller.stubs(:profile).returns() | ||
9 | + @controller.stubs(:request).returns() | ||
10 | + @controller.stubs(:response).returns() | ||
11 | + @controller.stubs(:environment).returns(@environment) | ||
12 | + @controller.stubs(:params).returns() | ||
13 | + @manager = Noosfero::Plugin::Manager.new(@controller) | ||
14 | + end | ||
15 | + attr_reader :environment | ||
16 | + attr_reader :manager | ||
17 | + | ||
18 | + should 'return the intersection between environment\'s enabled plugins and system available plugins' do | ||
19 | + class Plugin1 < Noosfero::Plugin; end; | ||
20 | + class Plugin2 < Noosfero::Plugin; end; | ||
21 | + class Plugin3 < Noosfero::Plugin; end; | ||
22 | + class Plugin4 < Noosfero::Plugin; end; | ||
23 | + environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s, Plugin4.to_s]) | ||
24 | + Noosfero::Plugin.stubs(:all).returns([Plugin1.to_s, Plugin3.to_s, Plugin4.to_s]) | ||
25 | + plugins = manager.enabled_plugins.map { |instance| instance.class.to_s } | ||
26 | + assert_equal [Plugin1.to_s, Plugin4.to_s], plugins | ||
27 | + end | ||
28 | + | ||
29 | + should 'map events to registered plugins' do | ||
30 | + | ||
31 | + class Noosfero::Plugin | ||
32 | + def random_event | ||
33 | + nil | ||
34 | + end | ||
35 | + end | ||
36 | + | ||
37 | + class Plugin1 < Noosfero::Plugin | ||
38 | + def random_event | ||
39 | + 'Plugin 1 action.' | ||
40 | + end | ||
41 | + end | ||
42 | + | ||
43 | + class Plugin2 < Noosfero::Plugin | ||
44 | + def random_event | ||
45 | + 'Plugin 2 action.' | ||
46 | + end | ||
47 | + end | ||
48 | + | ||
49 | + environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s]) | ||
50 | + | ||
51 | + p1 = Plugin1.new | ||
52 | + p2 = Plugin2.new | ||
53 | + | ||
54 | + assert_equal [p1.random_event, p2.random_event], manager.map(:random_event) | ||
55 | + end | ||
56 | + | ||
57 | +end | ||
58 | + |
@@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
2 | + | ||
3 | +class PluginTest < Test::Unit::TestCase | ||
4 | + | ||
5 | + def setup | ||
6 | + @environment = Environment.default | ||
7 | + end | ||
8 | + attr_reader :environment | ||
9 | + | ||
10 | + should 'keep the list of all loaded subclasses' do | ||
11 | + class Plugin1 < Noosfero::Plugin | ||
12 | + end | ||
13 | + | ||
14 | + class Plugin2 < Noosfero::Plugin | ||
15 | + end | ||
16 | + | ||
17 | + assert_includes Noosfero::Plugin.all, Plugin1.to_s | ||
18 | + assert_includes Noosfero::Plugin.all, Plugin1.to_s | ||
19 | + end | ||
20 | + | ||
21 | +end | ||
22 | + | ||
23 | + |