diff --git a/app/controllers/admin/plugins_controller.rb b/app/controllers/admin/plugins_controller.rb new file mode 100644 index 0000000..883977e --- /dev/null +++ b/app/controllers/admin/plugins_controller.rb @@ -0,0 +1,18 @@ +class PluginsController < AdminController + + def index + @active_plugins = Noosfero::Plugin.all.map {|plugin_name| eval(plugin_name)}.compact + end + + post_only :update + def update + params[:environment][:enabled_plugins].delete('') + if @environment.update_attributes(params[:environment]) + session[:notice] = _('Plugins updated successfully.') + else + session[:error] = _('Plugins were not updated successfully.') + end + redirect_to :action => 'index' + end + +end diff --git a/app/controllers/application.rb b/app/controllers/application.rb index 239055e..60e1c5f 100644 --- a/app/controllers/application.rb +++ b/app/controllers/application.rb @@ -83,6 +83,7 @@ class ApplicationController < ActionController::Base include NeedsProfile before_filter :detect_stuff_by_domain + before_filter :init_noosfero_plugins attr_reader :environment before_filter :load_terminology @@ -114,6 +115,10 @@ class ApplicationController < ActionController::Base end end + def init_noosfero_plugins + @plugins = Noosfero::Plugin::Manager.new(self) + end + def load_terminology # cache terminology for performance @@terminology_cache ||= {} diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb index fbfb6dd..9ec824b 100644 --- a/app/helpers/profile_helper.rb +++ b/app/helpers/profile_helper.rb @@ -15,4 +15,12 @@ module ProfileHelper end end + def render_tabs(tabs) + titles = tabs.inject(''){ |result, tab| result << content_tag(:li, link_to(tab[:title], '#'+tab[:id]), :class => 'tab') } + contents = tabs.inject(''){ |result, tab| result << content_tag(:div, tab[:content], :id => tab[:id]) } + + content_tag :div, :class => 'ui-tabs' do + content_tag(:ul, titles) + contents + end + end end diff --git a/app/models/environment.rb b/app/models/environment.rb index 34cc7fe..db99dd9 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -220,6 +220,8 @@ class Environment < ActiveRecord::Base settings_items :trusted_sites_for_iframe, :type => Array, :default => ['itheora.org', 'tv.softwarelivre.org', 'stream.softwarelivre.org'] + settings_items :enabled_plugins, :type => Array, :default => [] + def news_amount_by_folder=(amount) settings[:news_amount_by_folder] = amount.to_i end diff --git a/app/views/admin_panel/index.rhtml b/app/views/admin_panel/index.rhtml index 0f2dba0..813d41a 100644 --- a/app/views/admin_panel/index.rhtml +++ b/app/views/admin_panel/index.rhtml @@ -6,6 +6,7 @@ <%= link_to _('Edit site info'), :action => 'site_info' %> <%= link_to __('Edit message for disabled enterprises'), :action => 'message_for_disabled_enterprise' %> <%= link_to _('Enable/disable features'), :controller => 'features' %> + <%= link_to _('Enable/disable plugins'), :controller => 'plugins' %> <%= link_to _('Edit sideboxes'), :controller => 'environment_design'%> <%= link_to _('Manage Categories'), :controller => 'categories'%> <%= link_to _('Manage User roles'), :controller => 'role' %> diff --git a/app/views/plugins/index.rhtml b/app/views/plugins/index.rhtml new file mode 100644 index 0000000..13a76c2 --- /dev/null +++ b/app/views/plugins/index.rhtml @@ -0,0 +1,29 @@ +

<%= _('Manage plugins') %>

+<%= _('Here you can enable or disable any plugin of your environment.')%> + +<% labelled_form_for(:environment, @environment, :url => {:action => 'update'}) do |f| %> + + + + + + + + <%= hidden_field_tag('environment[enabled_plugins][]', '') %> + <% @active_plugins.each do |plugin| %> + + + + + + <% end %> +
<%= _('Plugin') %><%= _('Description') %><%= _('Enabled?') %>
<%= plugin.plugin_name %><%= plugin.plugin_description %><%= check_box_tag "environment[enabled_plugins][]", plugin, @environment.enabled_plugins.include?(plugin.to_s), :id => plugin.plugin_name %>
+ +
+ <% button_bar do %> + <%= submit_button('save', _('Save changes')) %> + <%= button :back, _('Back to admin panel'), :controller => 'admin_panel', :action => 'index' %> + <% end %> +
+ +<% end %> diff --git a/app/views/profile/_organization.rhtml b/app/views/profile/_organization.rhtml deleted file mode 100644 index ca96d0d..0000000 --- a/app/views/profile/_organization.rhtml +++ /dev/null @@ -1,62 +0,0 @@ - - - -
- - - - <% if logged_in? && current_person.follows?(@profile) %> - <%= render :partial => 'profile_wall' %> - <% end %> - <%= render :partial => 'profile_network' %> - -
- - - - - - - - - - - <%= display_field(_('Type:'), profile, :privacy_setting, true) %> - - <%= display_field(_('Location:'), profile, :location, true) %> - - - - - - - <% if profile.kind_of?(Enterprise) && !profile.environment.enabled?('disable_products_for_enterprises') %> - - - - - <% end %> - - - - - - - <%= render :partial => 'common' %> -
<%= _('Basic information')%>
<%= _('Members') %> - <%= link_to profile.members.count, :controller => 'profile', :action => 'members' %> -
<%= _('Created at:') %><%= show_date(profile.created_at) %>
- <%= link_to _('Products/Services'), :controller => 'catalog', :action => 'index' %> -
<%= _('Administrators:') %> - <%= profile.admins.map { |admin| link_to(admin.short_name, admin.url)}.join(', ') %> -
-
-
- - diff --git a/app/views/profile/_organization_profile.rhtml b/app/views/profile/_organization_profile.rhtml new file mode 100644 index 0000000..ca4c386 --- /dev/null +++ b/app/views/profile/_organization_profile.rhtml @@ -0,0 +1,39 @@ + + + + + + + + + + + <%= display_field(_('Type:'), profile, :privacy_setting, true) %> + + <%= display_field(_('Location:'), profile, :location, true) %> + + + + + + + <% if profile.kind_of?(Enterprise) && !profile.environment.enabled?('disable_products_for_enterprises') %> + + + + + <% end %> + + + + + + + <%= render :partial => 'common' %> +
<%= _('Basic information')%>
<%= _('Members') %> + <%= link_to profile.members.count, :controller => 'profile', :action => 'members' %> +
<%= _('Created at:') %><%= show_date(profile.created_at) %>
+ <%= link_to _('Products/Services'), :controller => 'catalog', :action => 'index' %> +
<%= _('Administrators:') %> + <%= profile.admins.map { |admin| link_to(admin.short_name, admin.url)}.join(', ') %> +
diff --git a/app/views/profile/_person.rhtml b/app/views/profile/_person.rhtml deleted file mode 100644 index ddc8084..0000000 --- a/app/views/profile/_person.rhtml +++ /dev/null @@ -1,104 +0,0 @@ - - -
- - - - <% if logged_in? && current_person.follows?(@profile) %> - <%= render :partial => 'profile_wall' %> - <%= render :partial => 'profile_network' %> - <% end %> - - <% if @profile.public? || (logged_in? && current_person.follows?(@profile)) %> - <%= render :partial => 'profile_activity' %> - <% end %> - -
- - - - - <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %> - <%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%> - <%= display_field(_('Location:'), profile, :location, true) %> - - <%= display_field(_('Type:'), profile, :privacy_setting, true) %> - - - - - - - <% if profile == user || profile.friends.include?(user) %> - - - - <%= display_field(_('Address:'), profile, :address) %> - <%= display_field(_('ZIP code:'), profile, :zip_code) %> - <%= display_field(_('Contact phone:'), profile, :contact_phone) %> - <%= display_field(_('e-Mail:'), profile, :email, true) { |email| link_to_email(email) } %> - <% end %> - - <% cache_timeout(profile.relationships_cache_key, 4.hours.from_now) do %> - <% if !(profile.organization.blank? && profile.organization_website.blank?) && (profile.active_fields.include?('organization') || profile.active_fields.include?('organization_website')) %> - - - - <% end %> - <%= display_field(_('Organization:'), profile, :organization) %> - <%= display_field(_('Organization website:'), profile, :organization_website) { |url| link_to(url, url) }%> - - - <% if !environment.enabled?('disable_asset_enterprises') && !profile.enterprises.empty? %> - - - - <% profile.enterprises.each do |item| %> - - - - - <% end %> - <% end %> - - - - - - - - - - - - - - <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %> - - - - <% profile.interests.each do |item| %> - - - - - <% end %> - <% end %> - - <%= render :partial => 'common' %> - -
<%= _('Basic information')%>
<%= _('Created at:') %><%= show_date(profile.created_at) %>
<%= _('Contact')%>
<%= _('Work')%>
<%= __('Enterprises') %>
<%= button 'menu-enterprise', item.name, item.url %>
<%= _('Network')%>
<%= __('Friends') + ':' %><%= link_to profile.friends.count, { :controller => 'profile', :action => 'friends' } %>
<%= __('Communities') + ':' %><%= link_to profile.communities.count, :controller => "profile", :action => 'communities' %>
<%= _('Interests') %>
<%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %>
- <% end %> -
-
- - diff --git a/app/views/profile/_person_profile.rhtml b/app/views/profile/_person_profile.rhtml new file mode 100644 index 0000000..8d4c04c --- /dev/null +++ b/app/views/profile/_person_profile.rhtml @@ -0,0 +1,76 @@ + + + + + <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %> + <%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%> + <%= display_field(_('Location:'), profile, :location, true) %> + + <%= display_field(_('Type:'), profile, :privacy_setting, true) %> + + + + + + + <% if profile == user || profile.friends.include?(user) %> + + + + <%= display_field(_('Address:'), profile, :address) %> + <%= display_field(_('ZIP code:'), profile, :zip_code) %> + <%= display_field(_('Contact phone:'), profile, :contact_phone) %> + <%= display_field(_('e-Mail:'), profile, :email, true) { |email| link_to_email(email) } %> + <% end %> + + <% cache_timeout(profile.relationships_cache_key, 4.hours.from_now) do %> + <% if !(profile.organization.blank? && profile.organization_website.blank?) && (profile.active_fields.include?('organization') || profile.active_fields.include?('organization_website')) %> + + + + <% end %> + <%= display_field(_('Organization:'), profile, :organization) %> + <%= display_field(_('Organization website:'), profile, :organization_website) { |url| link_to(url, url) }%> + + + <% if !environment.enabled?('disable_asset_enterprises') && !profile.enterprises.empty? %> + + + + <% profile.enterprises.each do |item| %> + + + + + <% end %> + <% end %> + + + + + + + + + + + + + + <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %> + + + + <% profile.interests.each do |item| %> + + + + + <% end %> + <% end %> + + <%= render :partial => 'common' %> + +
<%= _('Basic information')%>
<%= _('Created at:') %><%= show_date(profile.created_at) %>
<%= _('Contact')%>
<%= _('Work')%>
<%= __('Enterprises') %>
<%= button 'menu-enterprise', item.name, item.url %>
<%= _('Network')%>
<%= __('Friends') + ':' %><%= link_to profile.friends.count, { :controller => 'profile', :action => 'friends' } %>
<%= __('Communities') + ':' %><%= link_to profile.communities.count, :controller => "profile", :action => 'communities' %>
<%= _('Interests') %>
<%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %>
+<% end %> + diff --git a/app/views/profile/_profile.rhtml b/app/views/profile/_profile.rhtml new file mode 100644 index 0000000..1e33bdc --- /dev/null +++ b/app/views/profile/_profile.rhtml @@ -0,0 +1,32 @@ + + + + <% plugins_tabs = @plugins.map(:profile_tabs) %> + + <% tabs = plugins_tabs.select { |tab| tab[:start] } %> + + <% if logged_in? && current_person.follows?(@profile) %> + <% tabs << {:title => _('Wall'), :id => 'profile-wall', :content => (render :partial => 'profile_wall')} %> + <% end %> + + <% if @profile.organization? %> + <% tabs << {:title => _('What\'s new'), :id => 'profile-network', :content => (render :partial => 'profile_network')} %> + <% tabs << {:title => _('Profile'), :id => 'organization-profile', :content => (render :partial => 'organization_profile')} %> + <% elsif @profile.person? %> + <% if logged_in? && current_person.follows?(@profile) %> + <% tabs << {:title => _('Network'), :id => 'profile-network', :content => (render :partial => 'profile_network')} %> + <% end %> + + <% if @profile.public? || (logged_in? && current_person.follows?(@profile)) %> + <% tabs << {:title => _('Activity'), :id => 'profile-activity', :content => (render :partial => 'profile_activity')} %> + <% end %> + + <% tabs << {:title => _('Profile'), :id => 'person-profile', :content => (render :partial => 'person_profile')} %> + <% end %> + + <% tabs += plugins_tabs.select { |tab| !tab[:start] } %> + + <%= render_tabs(tabs) %> + + + diff --git a/app/views/profile/_profile_network.rhtml b/app/views/profile/_profile_network.rhtml index 7022934..0a0f17b 100644 --- a/app/views/profile/_profile_network.rhtml +++ b/app/views/profile/_profile_network.rhtml @@ -1,6 +1,4 @@ -
-

<%= _("%s's network activity") % @profile.name %>

- -
+

<%= _("%s's network activity") % @profile.name %>

+ diff --git a/app/views/profile/_profile_wall.rhtml b/app/views/profile/_profile_wall.rhtml index 8956aea..2bec4e2 100644 --- a/app/views/profile/_profile_wall.rhtml +++ b/app/views/profile/_profile_wall.rhtml @@ -1,14 +1,12 @@ -
-

<%= _("%s's wall") % @profile.name %>

-
- <%= flash[:error] %> - <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_scraps', :success => "$('leave_scrap_content').value=''" do %> - <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2 %> - <%= submit_button :scrap, _('Leave a scrap') %> - <% end %> -
-
- +

<%= _("%s's wall") % @profile.name %>

+
+ <%= flash[:error] %> + <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_scraps', :success => "$('leave_scrap_content').value=''" do %> + <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2 %> + <%= submit_button :scrap, _('Leave a scrap') %> + <% end %>
+
+ diff --git a/app/views/profile/index.rhtml b/app/views/profile/index.rhtml index 504a5db..3560123 100644 --- a/app/views/profile/index.rhtml +++ b/app/views/profile/index.rhtml @@ -13,5 +13,5 @@ <% end %> - <%= render :partial => partial_for_class(profile.class) %> + <%= render :partial => 'profile' %>
diff --git a/app/views/profile_editor/index.rhtml b/app/views/profile_editor/index.rhtml index 97c73d5..99b4804 100644 --- a/app/views/profile_editor/index.rhtml +++ b/app/views/profile_editor/index.rhtml @@ -65,6 +65,11 @@ <% end %> <%= control_panel_button(_('Manage my groups'), 'groups', :controller => 'memberships') if profile.person? %> + + <% @plugins.map(:control_panel_buttons).each do |button| %> + <%= control_panel_button(button[:title], button[:icon], button[:url]) %> + <% end %> + <% end %> <% if profile.person? && environment.enabled?('enterprise_activation') %> diff --git a/config/initializers/plugins.rb b/config/initializers/plugins.rb new file mode 100644 index 0000000..5348975 --- /dev/null +++ b/config/initializers/plugins.rb @@ -0,0 +1,5 @@ +require 'noosfero/plugin' +require 'noosfero/plugin/manager' +require 'noosfero/plugin/context' +require 'noosfero/plugin/active_record' +Noosfero::Plugin.init_system diff --git a/config/plugins/mezuro b/config/plugins/mezuro new file mode 120000 index 0000000..0efaf37 --- /dev/null +++ b/config/plugins/mezuro @@ -0,0 +1 @@ +../../plugins/mezuro \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 4621b94..8410e12 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -109,6 +109,12 @@ ActionController::Routing::Routes.draw do |map| map.system 'system', :controller => 'system' map.system 'system/:controller/:action/:id', :controller => Noosfero.pattern_for_controllers_in_directory('system') + ###################################################### + # plugin routes + ###################################################### + plugins_routes = File.join(Rails.root + '/lib/noosfero/plugin/routes.rb') + eval(IO.read(plugins_routes), binding, plugins_routes) + # cache stuff - hack map.cache 'public/:action/:id', :controller => 'public' diff --git a/features/plugins.feature b/features/plugins.feature new file mode 100644 index 0000000..0bbeca8 --- /dev/null +++ b/features/plugins.feature @@ -0,0 +1,59 @@ +Feature: plugins + As a noosfero\'s developer + I want to create hot spots that a plugin should use + As a plugins\' developer + I want to create plugins that uses noosfero\'s hot spots + As an admin of a noosfero environment + I want to activate and deactivate some plugins + As a user + I want to use the features implemented by the plugins + + Background: + Given the following users + | login | name | + | joaosilva | Joao Silva | + And I am logged in as "joaosilva" + And the following plugin + | klass | + | TestPlugin | + And the following events of TestPlugin + | event | body | + | control_panel_buttons | lambda { {:title => 'Test plugin button', :icon => '', :url => ''} } | + | profile_tabs | lambda { {:title => 'Test plugin tab', :id => 'test_plugin', :content => 'Test plugin random content'} } | + + Scenario: a user must see the plugin\'s button in the control panel if the plugin is enabled + Given plugin TestPlugin is enabled on environment + And I go to Joao Silva's control panel + Then I should see "Test plugin button" + + Scenario: a user must see the plugin\'s tab in in the profile if the plugin is enabled + Given plugin TestPlugin is enabled on environment + And I am on Joao Silva's profile + Then I should see "Test plugin tab" + + Scenario: a user must not see the plugin\'s button in the control panel if the plugin is disabled + Given plugin TestPlugin is disabled on environment + And I go to Joao Silva's control panel + Then I should not see "Test plugin button" + + Scenario: a user must not see the plugin\'s tab in in the profile if the plugin is disabled + Given plugin TestPlugin is disabled on environment + And I am on Joao Silva's profile + Then I should not see "Test plugin tab" + + Scenario: an admin should be able to deactivate a plugin + Given plugin TestPlugin is enabled on environment + And I am logged in as admin + When I go to the Control panel + Then I should see "Test plugin button" + When I go to the profile + Then I should see "Test plugin tab" + And I go to the environment control panel + And I follow "Enable/disable plugins" + And I uncheck "Test plugin" + And I press "Save changes" + When I go to the Control panel + Then I should not see "Test plugin button" + When I go to the profile + Then I should not see "Test plugin tab" + diff --git a/features/step_definitions/noosfero_steps.rb b/features/step_definitions/noosfero_steps.rb index f5404f1..35bcb06 100644 --- a/features/step_definitions/noosfero_steps.rb +++ b/features/step_definitions/noosfero_steps.rb @@ -146,6 +146,29 @@ Given /^the following certifiers$/ do |table| end end +Given /^the following plugin?$/ do |table| + table.hashes.each do |row| + row = row.dup + klass_name = row.delete('klass') + eval("class #{klass_name} < Noosfero::Plugin; end;") unless eval("defined?(#{klass_name})") + end +end + +Given /^the following events of (.+)$/ do |plugin,table| + klass = eval(plugin) + table.hashes.each do |row| + row = row.dup + event = row.delete('event').to_sym + body = eval(row.delete('body')) + + klass.class_eval do + define_method(event) do + body.call + end + end + end +end + Given /^I am logged in as "(.+)"$/ do |username| visit('/account/logout') visit('/account/login') @@ -184,6 +207,16 @@ Given /^feature "(.+)" is (enabled|disabled) on environment$/ do |feature, statu e.save end +Given /^plugin (.+) is (enabled|disabled) on environment$/ do |plugin, status| + e = Environment.default + if status == 'enabled' + e.enabled_plugins += [plugin] + else + e.enabled_plugins -= [plugin] + end + e.save! +end + Given /^organization_approval_method is "(.+)" on environment$/ do |approval_method| e = Environment.default e.organization_approval_method = approval_method diff --git a/features/support/paths.rb b/features/support/paths.rb index db6acce..99e1eb2 100644 --- a/features/support/paths.rb +++ b/features/support/paths.rb @@ -33,6 +33,9 @@ module NavigationHelpers when /^(.*)'s profile/ '/profile/%s' % Profile.find_by_name($1).identifier + when /^the profile$/ + '/profile/%s' % User.find_by_id(session[:user]).login + when /^login page$/ '/account/login' @@ -45,6 +48,9 @@ module NavigationHelpers when /^the Control panel$/ '/myprofile/%s' % User.find_by_id(session[:user]).login + when /the environment control panel/ + '/admin' + when /^the search page$/ '/search' diff --git a/lib/noosfero/plugin.rb b/lib/noosfero/plugin.rb new file mode 100644 index 0000000..5a22246 --- /dev/null +++ b/lib/noosfero/plugin.rb @@ -0,0 +1,75 @@ +require 'noosfero' +class Noosfero::Plugin + + attr_accessor :context + + class << self + + def init_system + Dir.glob(File.join(Rails.root, 'config', 'plugins', '*')).select do |entry| + File.directory?(entry) + end.each do |dir| + Rails.configuration.controller_paths << File.join(dir, 'controllers') + Dependencies.load_paths << File.join(dir, 'controllers') + [ Dependencies.load_paths, $:].each do |path| + path << File.join(dir, 'models') + path << File.join(dir, 'lib') + end + + plugin_name = File.basename(dir).camelize + 'Plugin' + plugin_name.constantize # load the plugin + end + end + + def all + @all ||= [] + end + + def inherited(subclass) + all << subclass.to_s unless all.include?(subclass.to_s) + end + + # Here the developer should specify the meta-informations that the plugin can + # inform. + def plugin_name + self.to_s.underscore.humanize + end + + def plugin_description + _("No description informed.") + end + + end + + def expanded_template(original_path, file_path, locals = {}) + while(File.basename(File.dirname(original_path)) != 'plugins') + original_path = File.dirname(original_path) + end + + ERB.new(File.read("#{original_path}/#{file_path}")).result(binding) + end + + # Here the developer should specify the events to which the plugins can + # register to. Must be explicitly defined its returning + # variables. + + # -> Adds buttons to the control panel + # returns = { :title => title, :icon => icon, :url => url } + # title = name that will be displayed. + # icon = css class name (for customized icons include them in a css file). + # url = url or route to which the button will redirect. + def control_panel_buttons + nil + end + + # -> Adds tabs to the profile + # returns = { :title => title, :id => id, :content => content, :start => start } + # title = name that will be displayed. + # id = div id. + # content = content of the tab (use expanded_template method to import content from another file). + # start = boolean that specifies if the tab must come before noosfero tabs (optional). + def profile_tabs + nil + end + +end diff --git a/lib/noosfero/plugin/active_record.rb b/lib/noosfero/plugin/active_record.rb new file mode 100644 index 0000000..b7aa868 --- /dev/null +++ b/lib/noosfero/plugin/active_record.rb @@ -0,0 +1,5 @@ +class Noosfero::Plugin::ActiveRecord < ActiveRecord::Base + def self.inherited(child) + child.table_name = child.name.gsub('::','_').underscore.pluralize + end +end diff --git a/lib/noosfero/plugin/context.rb b/lib/noosfero/plugin/context.rb new file mode 100644 index 0000000..f7c6a76 --- /dev/null +++ b/lib/noosfero/plugin/context.rb @@ -0,0 +1,29 @@ +class Noosfero::Plugin::Context + + def initialize(controller) + @controller = controller + end + + # Here the developer should define the interface to important context + # information from the controller to the plugins to access + def profile + @profile ||= @controller.send(:profile) + end + + def request + @request ||= @controller.send(:request) + end + + def response + @response ||= @controller.send(:response) + end + + def environment + @environment ||= @controller.send(:environment) + end + + def params + @params ||= @controller.send(:params) + end + +end diff --git a/lib/noosfero/plugin/manager.rb b/lib/noosfero/plugin/manager.rb new file mode 100644 index 0000000..142e460 --- /dev/null +++ b/lib/noosfero/plugin/manager.rb @@ -0,0 +1,21 @@ +class Noosfero::Plugin::Manager + + attr_reader :context + + def initialize(controller) + @context = Noosfero::Plugin::Context.new(controller) + end + + def map(event) + enabled_plugins.map { |plugin| plugin.send(event) }.compact.flatten + end + + def enabled_plugins + @enabled_plugins ||= (Noosfero::Plugin.all & context.environment.enabled_plugins).map do |plugin| + p = eval(plugin).new + p.context = context + p + end + end + +end diff --git a/lib/noosfero/plugin/routes.rb b/lib/noosfero/plugin/routes.rb new file mode 100644 index 0000000..b55b627 --- /dev/null +++ b/lib/noosfero/plugin/routes.rb @@ -0,0 +1,7 @@ +Dir.glob(File.join(Rails.root, 'config', 'plugins', '*', 'controllers')) do |dir| + plugin_name = File.basename(File.dirname(dir)) + map.connect 'plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_environment' + map.connect 'profile/:profile/plugins/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_profile' + map.connect 'myprofile/:profile/plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_myprofile' +end + diff --git a/lib/tasks/plugins.rake b/lib/tasks/plugins.rake new file mode 100644 index 0000000..bc10f23 --- /dev/null +++ b/lib/tasks/plugins.rake @@ -0,0 +1,9 @@ +namespace :plugins do + task :migrate do + Dir.glob(File.join(Rails.root, 'config', 'plugins', '*', 'db', 'migrate')).each do |path| + ActiveRecord::Migrator.migrate(path, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) + end + end +end + +task 'db:migrate' => 'plugins:migrate' diff --git a/plugins/mezuro/README b/plugins/mezuro/README new file mode 100644 index 0000000..9147b04 --- /dev/null +++ b/plugins/mezuro/README @@ -0,0 +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. diff --git a/plugins/mezuro/controllers/mezuro_plugin_myprofile_controller.rb b/plugins/mezuro/controllers/mezuro_plugin_myprofile_controller.rb new file mode 100644 index 0000000..ac14b52 --- /dev/null +++ b/plugins/mezuro/controllers/mezuro_plugin_myprofile_controller.rb @@ -0,0 +1,55 @@ +class MezuroPluginMyprofileController < MyProfileController + append_view_path File.join(File.dirname(__FILE__) + '/../views') + + def index + @projects = MezuroPlugin::Project.by_profile(profile) + end + + def new + @project = MezuroPlugin::Project.new + end + + def create + @project = MezuroPlugin::Project.new(params[:project]) + if @project.save + session[:notice] = _('Project successfully registered') + redirect_to :action => 'index' + else + render :action => 'new' + end + end + + def edit + @project = MezuroPlugin::Project.find(params[:id]) + end + + def update + @project = MezuroPlugin::Project.find(params[:id]) + if @project.update_attributes(params[:project]) + session[:notice] = _('Project successfully updated') + redirect_to :action => 'index' + else + render :action => 'edit' + end + end + + def show + @project = MezuroPlugin::Project.find_by_identifier params[:identifier] + @total_metrics = @project.total_metrics if @project != nil + @statistical_metrics = @project.statistical_metrics if @project != nil + @svn_error = @project.svn_error if (@project != nil && @project.svn_error) + end + + def destroy + @project = MezuroPlugin::Project.by_profile(profile).find(params[:id]) + if request.post? + if @project.destroy + session[:notice] = _('Project successfully removed.') + else + session[:notice] = _('Project was not successfully removed.') + end + redirect_to :action => 'index' + end + end + +end diff --git a/plugins/mezuro/db/migrate/20101209151530_create_projects.rb b/plugins/mezuro/db/migrate/20101209151530_create_projects.rb new file mode 100644 index 0000000..4f1e3aa --- /dev/null +++ b/plugins/mezuro/db/migrate/20101209151530_create_projects.rb @@ -0,0 +1,20 @@ +class CreateProjects < ActiveRecord::Migration + def self.up + create_table :mezuro_plugin_projects do |t| + t.string :name + t.string :identifier + t.string :personal_webpage + t.text :description + t.string :repository_url + t.string :svn_error + t.boolean :with_tab + t.references :profile + + t.timestamps + end + end + + def self.down + drop_table :mezuro_plugin_projects + end +end diff --git a/plugins/mezuro/db/migrate/20101209151640_create_metrics.rb b/plugins/mezuro/db/migrate/20101209151640_create_metrics.rb new file mode 100644 index 0000000..deeccc7 --- /dev/null +++ b/plugins/mezuro/db/migrate/20101209151640_create_metrics.rb @@ -0,0 +1,16 @@ +class CreateMetrics < ActiveRecord::Migration + def self.up + create_table :mezuro_plugin_metrics do |t| + t.string :name + t.float :value + t.integer :metricable_id + t.string :metricable_type + + t.timestamps + end + end + + def self.down + drop_table :mezuro_plugin_metrics + end +end diff --git a/plugins/mezuro/init.rb b/plugins/mezuro/init.rb new file mode 100644 index 0000000..64b6709 --- /dev/null +++ b/plugins/mezuro/init.rb @@ -0,0 +1 @@ +require "mezuro_plugin" diff --git a/plugins/mezuro/lib/mezuro_plugin.rb b/plugins/mezuro/lib/mezuro_plugin.rb new file mode 100644 index 0000000..01d8384 --- /dev/null +++ b/plugins/mezuro/lib/mezuro_plugin.rb @@ -0,0 +1,27 @@ +class MezuroPlugin < Noosfero::Plugin + + def self.plugin_name + "Mezuro" + end + + def self.plugin_description + _("A metric analizer plugin.") + end + + def control_panel_buttons + if context.profile.community? + { :title => 'Mezuro projects', :icon => 'mezuro', :url => {:controller => 'mezuro_plugin_myprofile', :action => 'index'} } + end + end + + def profile_tabs + if context.profile.community? && !MezuroPlugin::Project.by_profile(context.profile).blank? + MezuroPlugin::Project.by_profile(context.profile).with_tab.map do |project| + { :title => 'Mezuro ' + project.name, + :id => 'mezuro-project-'+project.identifier, + :content => expanded_template(__FILE__,"views/show.html.erb",{:current_project => project}) } + end + end + end + +end diff --git a/plugins/mezuro/lib/mezuro_plugin/analizo_extractor.rb b/plugins/mezuro/lib/mezuro_plugin/analizo_extractor.rb new file mode 100644 index 0000000..43f1248 --- /dev/null +++ b/plugins/mezuro/lib/mezuro_plugin/analizo_extractor.rb @@ -0,0 +1,43 @@ +class MezuroPlugin::AnalizoExtractor < Noosfero::Plugin::ActiveRecord + attr_reader :string_output, :hash_output + + def initialize project + @project = project + end + + def perform + run_analizo + create_hash + save_metrics + end + + def run_analizo + project_path = "#{RAILS_ROOT}/tmp/#{@project.identifier}" + @string_output = `analizo metrics #{project_path}` + end + + def create_hash + @hash_output = {} + first_line = true + + @string_output.lines.each do |line| + if line =~ /---/ + if first_line + first_line = false + else + break + end + end + + if line =~ /(\S+): (~|(\d+)(\.\d+)?).*/ + @hash_output[$1.to_sym] = $2 + end + end + end + + def save_metrics + @hash_output.each do | key, value | + MezuroPlugin::Metric.create(:name => key.to_s, :value => value.to_f, :metricable => @project) + end + end +end diff --git a/plugins/mezuro/lib/mezuro_plugin/calculate_metrics_job.rb b/plugins/mezuro/lib/mezuro_plugin/calculate_metrics_job.rb new file mode 100644 index 0000000..cb671b3 --- /dev/null +++ b/plugins/mezuro/lib/mezuro_plugin/calculate_metrics_job.rb @@ -0,0 +1,7 @@ +class MezuroPlugin::CalculateMetricsJob < Struct.new(:project_id) + def perform + project = MezuroPlugin::Project.find project_id + project.calculate_metrics + end +end + diff --git a/plugins/mezuro/lib/mezuro_plugin/metric.rb b/plugins/mezuro/lib/mezuro_plugin/metric.rb new file mode 100644 index 0000000..f80f877 --- /dev/null +++ b/plugins/mezuro/lib/mezuro_plugin/metric.rb @@ -0,0 +1,19 @@ +class MezuroPlugin::Metric < Noosfero::Plugin::ActiveRecord + validates_presence_of :name, :metricable_id, :metricable_type + + belongs_to :metricable, :polymorphic => true + before_save :round_value + + def initialize params + params[:value] = nil if params[:value] == '~' + super params + end + + def round_value + if self.value + multiplied = self.value * 100 + rounded = multiplied.round + self.value = rounded / 100.0 + end + end +end diff --git a/plugins/mezuro/lib/mezuro_plugin/project.rb b/plugins/mezuro/lib/mezuro_plugin/project.rb new file mode 100644 index 0000000..8d96da9 --- /dev/null +++ b/plugins/mezuro/lib/mezuro_plugin/project.rb @@ -0,0 +1,74 @@ +class MezuroPlugin::Project < Noosfero::Plugin::ActiveRecord + has_many :metrics, :as => :metricable + + validates_presence_of :name, :repository_url, :identifier + validates_format_of :identifier, :with => /^[a-z0-9|\-|\.]*$/, :message => "Identifier can only have a combination of lower case, number, hyphen and dot!" + validates_uniqueness_of :identifier + + named_scope :with_tab, :conditions => {:with_tab => true} + named_scope :by_profile, lambda {|profile| {:conditions => {:profile_id => profile.id}}} + + + after_create :asynchronous_calculate_metrics + + def calculate_metrics + begin + download_source_code + extractor = MezuroPlugin::AnalizoExtractor.new self + extractor.perform + rescue Svn::Error => error + update_attribute :svn_error, error.error_message + end + end + + def asynchronous_calculate_metrics + Delayed::Job.enqueue MezuroPlugin::CalculateMetricsJob.new(id) + end + + def download_source_code + download_prepare + Svn::Client::Context.new.checkout(repository_url, "#{RAILS_ROOT}/tmp/#{identifier}") + end + + def download_prepare + project_path = "#{RAILS_ROOT}/tmp/#{identifier}" + FileUtils.rm_r project_path if (File.exists? project_path) + end + + def metrics_calculated? + return !metrics.empty? + end + + def total_metrics + total_metrics = metrics.select do |metric| + metric.name.start_with?("total") + end + return total_metrics.sort_by {|metric| metric.name} + end + + def statistical_metrics + statistical_metrics = collect_statistical_metrics + + hash = {} + statistical_metrics.each do |metric| + insert_metric_in_hash metric, hash + end + hash + end + + def collect_statistical_metrics + statistical_metrics = metrics.select do |metric| + not metric.name.start_with?("total") + end + statistical_metrics.sort_by {|metric| metric.name} + end + + def insert_metric_in_hash metric, hash + metric_name, metric_statistic = metric.name.split("_") + unless hash.key?(metric_name) + hash[metric_name] = {metric_statistic => metric.value} + else + hash[metric_name][metric_statistic] = metric.value + end + end +end diff --git a/plugins/mezuro/lib/tasks/cucumber.rake b/plugins/mezuro/lib/tasks/cucumber.rake new file mode 100644 index 0000000..e0bc4ab --- /dev/null +++ b/plugins/mezuro/lib/tasks/cucumber.rake @@ -0,0 +1,47 @@ +# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. +# It is recommended to regenerate this file in the future when you upgrade to a +# newer version of cucumber-rails. Consider adding your own code to a new file +# instead of editing this one. Cucumber will automatically load all features/**/*.rb +# files. + + +unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks + +vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first +$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil? + +begin + require 'cucumber/rake/task' + + namespace :cucumber do + Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t| + t.binary = vendored_cucumber_bin # If nil, the gem's binary is used. + t.fork = true # You may get faster startup if you set this to false + t.profile = 'default' + end + + Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t| + t.binary = vendored_cucumber_bin + t.fork = true # You may get faster startup if you set this to false + t.profile = 'wip' + end + + desc 'Run all features' + task :all => [:ok, :wip] + end + desc 'Alias for cucumber:ok' + task :cucumber => 'cucumber:ok' + + task :default => :cucumber + + task :features => :cucumber do + STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***" + end +rescue LoadError + desc 'cucumber rake task not available (cucumber not installed)' + task :cucumber do + abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin' + end +end + +end diff --git a/plugins/mezuro/lib/tasks/rspec.rake b/plugins/mezuro/lib/tasks/rspec.rake new file mode 100644 index 0000000..dba3ffc --- /dev/null +++ b/plugins/mezuro/lib/tasks/rspec.rake @@ -0,0 +1,144 @@ +gem 'test-unit', '1.2.3' if RUBY_VERSION.to_f >= 1.9 +rspec_gem_dir = nil +Dir["#{RAILS_ROOT}/vendor/gems/*"].each do |subdir| + rspec_gem_dir = subdir if subdir.gsub("#{RAILS_ROOT}/vendor/gems/","") =~ /^(\w+-)?rspec-(\d+)/ && File.exist?("#{subdir}/lib/spec/rake/spectask.rb") +end +rspec_plugin_dir = File.expand_path(File.dirname(__FILE__) + '/../../vendor/plugins/rspec') + +if rspec_gem_dir && (test ?d, rspec_plugin_dir) + 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" +end + +if rspec_gem_dir + $LOAD_PATH.unshift("#{rspec_gem_dir}/lib") +elsif File.exist?(rspec_plugin_dir) + $LOAD_PATH.unshift("#{rspec_plugin_dir}/lib") +end + +# Don't load rspec if running "rake gems:*" +unless ARGV.any? {|a| a =~ /^gems/} + +begin + require 'spec/rake/spectask' +rescue MissingSourceFile + module Spec + module Rake + class SpecTask + def initialize(name) + task name do + # if rspec-rails is a configured gem, this will output helpful material and exit ... + require File.expand_path(File.join(File.dirname(__FILE__),"..","..","config","environment")) + + # ... otherwise, do this: + raise <<-MSG + +#{"*" * 80} +* You are trying to run an rspec rake task defined in +* #{__FILE__}, +* but rspec can not be found in vendor/gems, vendor/plugins or system gems. +#{"*" * 80} +MSG + end + end + end + end + end +end + +Rake.application.instance_variable_get('@tasks').delete('default') + +spec_prereq = File.exist?(File.join(RAILS_ROOT, 'config', 'database.yml')) ? "db:test:prepare" : :noop +task :noop do +end + +task :default => :spec +task :stats => "spec:statsetup" + +desc "Run all specs in spec directory (excluding plugin specs)" +Spec::Rake::SpecTask.new(:spec => spec_prereq) do |t| + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] + t.spec_files = FileList['spec/**/*_spec.rb'] +end + +namespace :spec do + desc "Run all specs in spec directory with RCov (excluding plugin specs)" + Spec::Rake::SpecTask.new(:rcov) do |t| + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] + t.spec_files = FileList['spec/**/*_spec.rb'] + t.rcov = true + t.rcov_opts = lambda do + IO.readlines("#{RAILS_ROOT}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten + end + end + + desc "Print Specdoc for all specs (excluding plugin specs)" + Spec::Rake::SpecTask.new(:doc) do |t| + t.spec_opts = ["--format", "specdoc", "--dry-run"] + t.spec_files = FileList['spec/**/*_spec.rb'] + end + + desc "Print Specdoc for all plugin examples" + Spec::Rake::SpecTask.new(:plugin_doc) do |t| + t.spec_opts = ["--format", "specdoc", "--dry-run"] + t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*') + end + + [:models, :controllers, :views, :helpers, :lib, :integration].each do |sub| + desc "Run the code examples in spec/#{sub}" + Spec::Rake::SpecTask.new(sub => spec_prereq) do |t| + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] + t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"] + end + end + + desc "Run the code examples in vendor/plugins (except RSpec's own)" + Spec::Rake::SpecTask.new(:plugins => spec_prereq) do |t| + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] + t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*').exclude("vendor/plugins/rspec-rails/*") + end + + namespace :plugins do + desc "Runs the examples for rspec_on_rails" + Spec::Rake::SpecTask.new(:rspec_on_rails) do |t| + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] + t.spec_files = FileList['vendor/plugins/rspec-rails/spec/**/*_spec.rb'] + end + end + + # Setup specs for stats + task :statsetup do + require 'code_statistics' + ::STATS_DIRECTORIES << %w(Model\ specs spec/models) if File.exist?('spec/models') + ::STATS_DIRECTORIES << %w(View\ specs spec/views) if File.exist?('spec/views') + ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers) if File.exist?('spec/controllers') + ::STATS_DIRECTORIES << %w(Helper\ specs spec/helpers) if File.exist?('spec/helpers') + ::STATS_DIRECTORIES << %w(Library\ specs spec/lib) if File.exist?('spec/lib') + ::STATS_DIRECTORIES << %w(Routing\ specs spec/routing) if File.exist?('spec/routing') + ::STATS_DIRECTORIES << %w(Integration\ specs spec/integration) if File.exist?('spec/integration') + ::CodeStatistics::TEST_TYPES << "Model specs" if File.exist?('spec/models') + ::CodeStatistics::TEST_TYPES << "View specs" if File.exist?('spec/views') + ::CodeStatistics::TEST_TYPES << "Controller specs" if File.exist?('spec/controllers') + ::CodeStatistics::TEST_TYPES << "Helper specs" if File.exist?('spec/helpers') + ::CodeStatistics::TEST_TYPES << "Library specs" if File.exist?('spec/lib') + ::CodeStatistics::TEST_TYPES << "Routing specs" if File.exist?('spec/routing') + ::CodeStatistics::TEST_TYPES << "Integration specs" if File.exist?('spec/integration') + end + + namespace :db do + namespace :fixtures do + 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." + task :load => :environment do + ActiveRecord::Base.establish_connection(Rails.env) + base_dir = File.join(Rails.root, 'spec', 'fixtures') + fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir + + require 'active_record/fixtures' + (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file| + Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*')) + end + end + end + end +end + +end diff --git a/plugins/mezuro/public/images/mezuro.gif b/plugins/mezuro/public/images/mezuro.gif new file mode 100644 index 0000000..4e841f1 Binary files /dev/null and b/plugins/mezuro/public/images/mezuro.gif differ diff --git a/plugins/mezuro/public/images/mezuro.png b/plugins/mezuro/public/images/mezuro.png new file mode 100644 index 0000000..49aaeac Binary files /dev/null and b/plugins/mezuro/public/images/mezuro.png differ diff --git a/plugins/mezuro/public/images/minus.png b/plugins/mezuro/public/images/minus.png new file mode 100644 index 0000000..6b8361e Binary files /dev/null and b/plugins/mezuro/public/images/minus.png differ diff --git a/plugins/mezuro/public/images/plus.png b/plugins/mezuro/public/images/plus.png new file mode 100644 index 0000000..c541e6e Binary files /dev/null and b/plugins/mezuro/public/images/plus.png differ diff --git a/plugins/mezuro/public/stylesheets/base_layout.css b/plugins/mezuro/public/stylesheets/base_layout.css new file mode 100644 index 0000000..947ac92 --- /dev/null +++ b/plugins/mezuro/public/stylesheets/base_layout.css @@ -0,0 +1,2 @@ +.controller-profile_editor a.control-panel-mezuro {background-image: url(../images/control-panel/mezuro.png)} +.controller-profile_editor .msie6 a.control-panel-edit-location {background-image: url(../images/control-panel/mezuro.gif)} diff --git a/plugins/mezuro/views/mezuro_plugin_myprofile/_form.html.erb b/plugins/mezuro/views/mezuro_plugin_myprofile/_form.html.erb new file mode 100644 index 0000000..47c861e --- /dev/null +++ b/plugins/mezuro/views/mezuro_plugin_myprofile/_form.html.erb @@ -0,0 +1,20 @@ +<%= error_messages_for 'project' %> + +<% labelled_form_for :project, @project, :url => @url do |f| %> + <%= hidden_field_tag 'project[profile_id]', profile.id %> + <%= hidden_field_tag 'id', @project.id %> + + <%= required_fields_message %> + + <%= required f.text_field(:name) %> + <%= required f.text_field(:repository_url) %> + <%= required f.text_field(:identifier) %> + <%= f.check_box(:with_tab) %> + <%= f.text_area(:description, :size => '30x3') %> + + <% button_bar do %> + <%= submit_button(:save, @submit_button, :cancel => {:action => 'index'})%> + <% end %> +<% end %> + +<%= javascript_tag "$('project_name').focus();" %> diff --git a/plugins/mezuro/views/mezuro_plugin_myprofile/edit.html.erb b/plugins/mezuro/views/mezuro_plugin_myprofile/edit.html.erb new file mode 100644 index 0000000..0b218e1 --- /dev/null +++ b/plugins/mezuro/views/mezuro_plugin_myprofile/edit.html.erb @@ -0,0 +1,4 @@ +

<%= _("Edit project") %>

+<% @url = {:action => 'update'} %> +<% @submit_button = _("Update project") %> +<%= render :partial => 'form' %> diff --git a/plugins/mezuro/views/mezuro_plugin_myprofile/index.html.erb b/plugins/mezuro/views/mezuro_plugin_myprofile/index.html.erb new file mode 100644 index 0000000..a3f15ef --- /dev/null +++ b/plugins/mezuro/views/mezuro_plugin_myprofile/index.html.erb @@ -0,0 +1,28 @@ +

<%= _("%s's Mezuro projects") % profile.name %>

+ +<% if @projects.blank? %> + <%= _("%s has no projects registered.") % profile.name %> +<% else %> + + + + + + + <% @projects.each do |project| %> + + + + + <% end %> +
<%= _('Project') %><%= _('Actions') %>
<%= project.name %> + <%= button_without_text :edit, _('Edit'), :action => 'edit', :id => project.id %> + <%= button_without_text :delete, _('Delete'), { :action => 'destroy', :id => project.id }, + :method => :post, + :confirm => _("Are you sure you want to remove this project?") %> +
+<% end %> + +<% button_bar do %> + <%= button :new, _('Register a new project'), :action => 'new' %> +<% end %> diff --git a/plugins/mezuro/views/mezuro_plugin_myprofile/new.html.erb b/plugins/mezuro/views/mezuro_plugin_myprofile/new.html.erb new file mode 100644 index 0000000..c451406 --- /dev/null +++ b/plugins/mezuro/views/mezuro_plugin_myprofile/new.html.erb @@ -0,0 +1,4 @@ +

<%= _("New project") %>

+<% @url = {:action => 'create'} %> +<% @submit_button = _("Register project") %> +<%= render :partial => 'form' %> diff --git a/plugins/mezuro/views/show.html.erb b/plugins/mezuro/views/show.html.erb new file mode 100644 index 0000000..fec090e --- /dev/null +++ b/plugins/mezuro/views/show.html.erb @@ -0,0 +1,81 @@ +<% @project = locals[:current_project] %> +<% @total_metrics = @project.total_metrics if @project != nil %> +<% @statistical_metrics = @project.statistical_metrics if @project != nil %> +<% @svn_error = @project.svn_error if (@project != nil && @project.svn_error) %> + +

<%= @project.name %>'s Info

+ + + + + + <% if (@project.description != nil && @project.description != "" ) %> + + + + + <% end %> + + + + +
<%= _("Name") %><%= @project.name %>
<%= _("Description") %><%= @project.description %>
<%= _("Repository address") %><%= @project.repository_url %>
+ +
+ + +<% if @svn_error %> +

ERROR

+
+ <%= @svn_error %> +
+
+
+

+ <%= _("Possible causes:") %> +

+

+<%else%> +

<%= _("Metric Results") %>

+ <% if @project.metrics_calculated? %> +

<% _("Total Metrics") %>

+ + <% @total_metrics.each_with_index do |metric, index| %> + + + + + <% end %> +
<%= metric.name %> <%= metric.value %>
+ +

<%= _("Statistical Metrics") %>

+ <% @statistical_metrics.each_key do |metric_name| %> +
+ <%= "#{metric_name}_average: #{@statistical_metrics[metric_name]["average"]}" %> + +
+ <% end %> + + <% else %> +
+ <%= _("Wait a moment while the metrics are calculated.
+ Reload the page manually in a few moment. ") %> +
+ <% end %> +<% end %> diff --git a/test/functional/plugins_controller_test.rb b/test/functional/plugins_controller_test.rb new file mode 100644 index 0000000..40b3d62 --- /dev/null +++ b/test/functional/plugins_controller_test.rb @@ -0,0 +1,68 @@ +require File.dirname(__FILE__) + '/../test_helper' +require 'plugins_controller' + +# Re-raise errors caught by the controller. +class PluginsController; def rescue_action(e) raise e end; end + +class PluginsControllerTest < Test::Unit::TestCase + + all_fixtures + def setup + @controller = PluginsController.new + @request = ActionController::TestRequest.new + @request.stubs(:ssl?).returns(true) + @response = ActionController::TestResponse.new + @environment = Environment.default + login_as(create_admin_user(@environment)) + end + attr_reader :environment + + def test_local_files_reference + assert_local_files_reference + end + + def test_valid_xhtml + assert_valid_xhtml + end + + should 'list system active plugins' do + class Plugin1 < Noosfero::Plugin + class << self + def plugin_name + "Plugin1" + end + def plugin_description + "This plugin is from hell!" + end + end + end + + class Plugin2 < Noosfero::Plugin + class << self + def plugin_name + "Plugin2" + end + def plugin_description + "This plugin is from heaven!" + end + end + end + + Noosfero::Plugin.stubs(:all).returns([Plugin1.to_s,Plugin2.to_s]) + + get :index + + assert_tag :tag => 'td', :content => /#{Plugin1.plugin_name}/ + assert_tag :tag => 'td', :content => /#{Plugin1.plugin_description}/ + assert_tag :tag => 'td', :content => /#{Plugin2.plugin_name}/ + assert_tag :tag => 'td', :content => /#{Plugin2.plugin_description}/ + end + + should 'enable or disable plugins' do + assert_not_equal ['Plugin1'], environment.enabled_plugins + post :update, :environment => { :enabled_plugins => ['Plugin1']} + environment.reload + assert_equal ['Plugin1'], environment.enabled_plugins + end + +end diff --git a/test/functional/profile_controller_test.rb b/test/functional/profile_controller_test.rb index 4ef1861..e287bbe 100644 --- a/test/functional/profile_controller_test.rb +++ b/test/functional/profile_controller_test.rb @@ -1113,4 +1113,18 @@ class ProfileControllerTest < Test::Unit::TestCase assert_equal '', @response.body end + should 'display plugins tabs' do + plugin1_tab = {:title => 'Plugin1 tab', :id => 'plugin1_tab', :content => 'Content from plugin1.'} + plugin2_tab = {:title => 'Plugin2 tab', :id => 'plugin2_tab', :content => 'Content from plugin2.'} + tabs = [plugin1_tab, plugin2_tab] + plugins = mock() + plugins.stubs(:map).with(:profile_tabs).returns(tabs) + Noosfero::Plugin::Manager.stubs(:new).returns(plugins) + + get :index, :profile => profile.identifier + + assert_tag :tag => 'a', :content => /#{plugin1_tab[:title]}/, :attributes => {:href => /#{plugin1_tab[:id]}/} + assert_tag :tag => 'div', :content => /#{plugin1_tab[:content]}/, :attributes => {:id => /#{plugin1_tab[:id]}/} + end + end diff --git a/test/functional/profile_editor_controller_test.rb b/test/functional/profile_editor_controller_test.rb index f936fef..9cbfdf1 100644 --- a/test/functional/profile_editor_controller_test.rb +++ b/test/functional/profile_editor_controller_test.rb @@ -858,4 +858,18 @@ class ProfileEditorControllerTest < Test::Unit::TestCase end end + should 'display plugins buttons on the control panel' do + plugin1_button = {:title => "Plugin1 button", :icon => 'plugin1_icon', :url => 'plugin1_url'} + plugin2_button = {:title => "Plugin2 button", :icon => 'plugin2_icon', :url => 'plugin2_url'} + buttons = [plugin1_button, plugin2_button] + plugins = mock() + plugins.stubs(:map).with(:control_panel_buttons).returns(buttons) + Noosfero::Plugin::Manager.stubs(:new).returns(plugins) + + get :index, :profile => profile.identifier + + assert_tag :tag => 'a', :content => plugin1_button[:title], :attributes => {:class => /#{plugin1_button[:icon]}/, :href => /#{plugin1_button[:url]}/} + assert_tag :tag => 'a', :content => plugin2_button[:title], :attributes => {:class => /#{plugin2_button[:icon]}/, :href => /#{plugin2_button[:url]}/} + end + end diff --git a/test/unit/manager_test.rb b/test/unit/manager_test.rb new file mode 100644 index 0000000..eb8ef6d --- /dev/null +++ b/test/unit/manager_test.rb @@ -0,0 +1,58 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class ManagerTest < Test::Unit::TestCase + + def setup + @environment = Environment.default + @controller = mock() + @controller.stubs(:profile).returns() + @controller.stubs(:request).returns() + @controller.stubs(:response).returns() + @controller.stubs(:environment).returns(@environment) + @controller.stubs(:params).returns() + @manager = Noosfero::Plugin::Manager.new(@controller) + end + attr_reader :environment + attr_reader :manager + + should 'return the intersection between environment\'s enabled plugins and system available plugins' do + class Plugin1 < Noosfero::Plugin; end; + class Plugin2 < Noosfero::Plugin; end; + class Plugin3 < Noosfero::Plugin; end; + class Plugin4 < Noosfero::Plugin; end; + environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s, Plugin4.to_s]) + Noosfero::Plugin.stubs(:all).returns([Plugin1.to_s, Plugin3.to_s, Plugin4.to_s]) + plugins = manager.enabled_plugins.map { |instance| instance.class.to_s } + assert_equal [Plugin1.to_s, Plugin4.to_s], plugins + end + + should 'map events to registered plugins' do + + class Noosfero::Plugin + def random_event + nil + end + end + + class Plugin1 < Noosfero::Plugin + def random_event + 'Plugin 1 action.' + end + end + + class Plugin2 < Noosfero::Plugin + def random_event + 'Plugin 2 action.' + end + end + + environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s]) + + p1 = Plugin1.new + p2 = Plugin2.new + + assert_equal [p1.random_event, p2.random_event], manager.map(:random_event) + end + +end + diff --git a/test/unit/plugin_test.rb b/test/unit/plugin_test.rb new file mode 100644 index 0000000..cd4fcf6 --- /dev/null +++ b/test/unit/plugin_test.rb @@ -0,0 +1,23 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class PluginTest < Test::Unit::TestCase + + def setup + @environment = Environment.default + end + attr_reader :environment + + should 'keep the list of all loaded subclasses' do + class Plugin1 < Noosfero::Plugin + end + + class Plugin2 < Noosfero::Plugin + end + + assert_includes Noosfero::Plugin.all, Plugin1.to_s + assert_includes Noosfero::Plugin.all, Plugin1.to_s + end + +end + + -- libgit2 0.21.2