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 @@ |
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 | 83 | include NeedsProfile |
84 | 84 | |
85 | 85 | before_filter :detect_stuff_by_domain |
86 | + before_filter :init_noosfero_plugins | |
86 | 87 | attr_reader :environment |
87 | 88 | |
88 | 89 | before_filter :load_terminology |
... | ... | @@ -114,6 +115,10 @@ class ApplicationController < ActionController::Base |
114 | 115 | end |
115 | 116 | end |
116 | 117 | |
118 | + def init_noosfero_plugins | |
119 | + @plugins = Noosfero::Plugin::Manager.new(self) | |
120 | + end | |
121 | + | |
117 | 122 | def load_terminology |
118 | 123 | # cache terminology for performance |
119 | 124 | @@terminology_cache ||= {} | ... | ... |
app/helpers/profile_helper.rb
... | ... | @@ -15,4 +15,12 @@ module ProfileHelper |
15 | 15 | end |
16 | 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 | 26 | end | ... | ... |
app/models/environment.rb
... | ... | @@ -220,6 +220,8 @@ class Environment < ActiveRecord::Base |
220 | 220 | |
221 | 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 | 225 | def news_amount_by_folder=(amount) |
224 | 226 | settings[:news_amount_by_folder] = amount.to_i |
225 | 227 | end | ... | ... |
app/views/admin_panel/index.rhtml
... | ... | @@ -6,6 +6,7 @@ |
6 | 6 | <tr><td><%= link_to _('Edit site info'), :action => 'site_info' %></td></tr> |
7 | 7 | <tr><td><%= link_to __('Edit message for disabled enterprises'), :action => 'message_for_disabled_enterprise' %></td></tr> |
8 | 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 | 10 | <tr><td><%= link_to _('Edit sideboxes'), :controller => 'environment_design'%></td></tr> |
10 | 11 | <tr><td><%= link_to _('Manage Categories'), :controller => 'categories'%></td></tr> |
11 | 12 | <tr><td><%= link_to _('Manage User roles'), :controller => 'role' %></td></tr> | ... | ... |
... | ... | @@ -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 | - | |
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 @@ |
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 | -<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 @@ |
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 @@ |
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 | 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 | 65 | <% end %> |
66 | 66 | |
67 | 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 | 73 | <% end %> |
69 | 74 | |
70 | 75 | <% if profile.person? && environment.enabled?('enterprise_activation') %> | ... | ... |
config/routes.rb
... | ... | @@ -109,6 +109,12 @@ ActionController::Routing::Routes.draw do |map| |
109 | 109 | map.system 'system', :controller => 'system' |
110 | 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 | 118 | # cache stuff - hack |
113 | 119 | map.cache 'public/:action/:id', :controller => 'public' |
114 | 120 | ... | ... |
... | ... | @@ -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 | 146 | end |
147 | 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 | 172 | Given /^I am logged in as "(.+)"$/ do |username| |
150 | 173 | visit('/account/logout') |
151 | 174 | visit('/account/login') |
... | ... | @@ -184,6 +207,16 @@ Given /^feature "(.+)" is (enabled|disabled) on environment$/ do |feature, statu |
184 | 207 | e.save |
185 | 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 | 220 | Given /^organization_approval_method is "(.+)" on environment$/ do |approval_method| |
188 | 221 | e = Environment.default |
189 | 222 | e.organization_approval_method = approval_method | ... | ... |
features/support/paths.rb
... | ... | @@ -33,6 +33,9 @@ module NavigationHelpers |
33 | 33 | when /^(.*)'s profile/ |
34 | 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 | 39 | when /^login page$/ |
37 | 40 | '/account/login' |
38 | 41 | |
... | ... | @@ -45,6 +48,9 @@ module NavigationHelpers |
45 | 48 | when /^the Control panel$/ |
46 | 49 | '/myprofile/%s' % User.find_by_id(session[:user]).login |
47 | 50 | |
51 | + when /the environment control panel/ | |
52 | + '/admin' | |
53 | + | |
48 | 54 | when /^the search page$/ |
49 | 55 | '/search' |
50 | 56 | ... | ... |
... | ... | @@ -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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
1 | +require "mezuro_plugin" | ... | ... |
... | ... | @@ -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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 | 1113 | assert_equal '', @response.body |
1114 | 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 | 1130 | end | ... | ... |
test/functional/profile_editor_controller_test.rb
... | ... | @@ -858,4 +858,18 @@ class ProfileEditorControllerTest < Test::Unit::TestCase |
858 | 858 | end |
859 | 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 | 875 | end | ... | ... |
... | ... | @@ -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 @@ |
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 | + | ... | ... |