Commit f8137b7ade33cedeb5ffea051098f597d0194fb3
Committed by
Antonio Terceiro
1 parent
19be5f1a
Exists in
master
and in
23 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 | + |