From 29249040654c0a8948afeb1748ddd3f3e6819042 Mon Sep 17 00:00:00 2001 From: Daniela Feitosa Date: Thu, 10 Sep 2015 13:10:36 -0300 Subject: [PATCH] Add Organization Ratings Plugin --- lib/noosfero/plugin.rb | 10 ++++++++++ plugins/organization_ratings/controllers/organization_ratings_plugin_admin_controller.rb | 19 +++++++++++++++++++ plugins/organization_ratings/controllers/organization_ratings_plugin_profile_controller.rb | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/db/migrate/20150830225546_create_organization_ratings.rb | 12 ++++++++++++ plugins/organization_ratings/db/migrate/20150830225733_create_organization_ratings_config.rb | 14 ++++++++++++++ plugins/organization_ratings/db/migrate/20150830230047_add_comments_count_to_profile.rb | 11 +++++++++++ plugins/organization_ratings/features/rate_community.feature | 30 ++++++++++++++++++++++++++++++ plugins/organization_ratings/lib/average_rating_block.rb | 30 ++++++++++++++++++++++++++++++ plugins/organization_ratings/lib/create_organization_rating_comment.rb | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/lib/ext/comments.rb | 6 ++++++ plugins/organization_ratings/lib/ext/environment.rb | 5 +++++ plugins/organization_ratings/lib/ext/organization.rb | 7 +++++++ plugins/organization_ratings/lib/ext/person.rb | 5 +++++ plugins/organization_ratings/lib/organization_rating.rb | 25 +++++++++++++++++++++++++ plugins/organization_ratings/lib/organization_ratings_block.rb | 30 ++++++++++++++++++++++++++++++ plugins/organization_ratings/lib/organization_ratings_config.rb | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/lib/organization_ratings_plugin.rb | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/lib/ratings_helper.rb | 15 +++++++++++++++ plugins/organization_ratings/public/images/small-star-negative.png | Bin 0 -> 529 bytes plugins/organization_ratings/public/images/small-star-positive.png | Bin 0 -> 509 bytes plugins/organization_ratings/public/images/star-negative-medium.png | Bin 0 -> 642 bytes plugins/organization_ratings/public/images/star-negative.png | Bin 0 -> 642 bytes plugins/organization_ratings/public/images/star-positive-medium.png | Bin 0 -> 637 bytes plugins/organization_ratings/public/images/star-positive.png | Bin 0 -> 637 bytes plugins/organization_ratings/public/images/user-not-logged.png | Bin 0 -> 2535 bytes plugins/organization_ratings/public/organization_rating_management.js | 35 +++++++++++++++++++++++++++++++++++ plugins/organization_ratings/public/rate.js | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/style.css | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/test/functional/organization_ratings_plugin_admin_controller_test.rb | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/test/functional/organization_ratings_plugin_profile_controller_test.rb | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/test/unit/organization_rating_config_test.rb | 43 +++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/test/unit/organization_rating_test.rb | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/test/unit/ratings_helper_test.rb | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/views/blocks/display_organization_average_rating.html.erb | 25 +++++++++++++++++++++++++ plugins/organization_ratings/views/blocks/organization_ratings_block.html.erb | 21 +++++++++++++++++++++ plugins/organization_ratings/views/organization_ratings_plugin_admin/index.html.erb | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/views/organization_ratings_plugin_profile/_new_rating_fields.html.erb | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/organization_ratings/views/organization_ratings_plugin_profile/new_rating.html.erb | 18 ++++++++++++++++++ plugins/organization_ratings/views/shared/_make_report_block.html.erb | 28 ++++++++++++++++++++++++++++ plugins/organization_ratings/views/shared/_rating_button.html.erb | 10 ++++++++++ plugins/organization_ratings/views/shared/_user_rating_container.html.erb | 33 +++++++++++++++++++++++++++++++++ plugins/organization_ratings/views/tasks/_create_organization_rating_comment_accept_details.html.erb | 4 ++++ plugins/statistics/lib/statistics_block.rb | 5 +++-- 43 files changed, 1598 insertions(+), 2 deletions(-) create mode 100644 plugins/organization_ratings/controllers/organization_ratings_plugin_admin_controller.rb create mode 100644 plugins/organization_ratings/controllers/organization_ratings_plugin_profile_controller.rb create mode 100644 plugins/organization_ratings/db/migrate/20150830225546_create_organization_ratings.rb create mode 100644 plugins/organization_ratings/db/migrate/20150830225733_create_organization_ratings_config.rb create mode 100644 plugins/organization_ratings/db/migrate/20150830230047_add_comments_count_to_profile.rb create mode 100644 plugins/organization_ratings/features/rate_community.feature create mode 100644 plugins/organization_ratings/lib/average_rating_block.rb create mode 100644 plugins/organization_ratings/lib/create_organization_rating_comment.rb create mode 100644 plugins/organization_ratings/lib/ext/comments.rb create mode 100644 plugins/organization_ratings/lib/ext/environment.rb create mode 100644 plugins/organization_ratings/lib/ext/organization.rb create mode 100644 plugins/organization_ratings/lib/ext/person.rb create mode 100644 plugins/organization_ratings/lib/organization_rating.rb create mode 100644 plugins/organization_ratings/lib/organization_ratings_block.rb create mode 100644 plugins/organization_ratings/lib/organization_ratings_config.rb create mode 100644 plugins/organization_ratings/lib/organization_ratings_plugin.rb create mode 100644 plugins/organization_ratings/lib/ratings_helper.rb create mode 100644 plugins/organization_ratings/public/images/small-star-negative.png create mode 100644 plugins/organization_ratings/public/images/small-star-positive.png create mode 100644 plugins/organization_ratings/public/images/star-negative-medium.png create mode 100644 plugins/organization_ratings/public/images/star-negative.png create mode 100644 plugins/organization_ratings/public/images/star-positive-medium.png create mode 100644 plugins/organization_ratings/public/images/star-positive.png create mode 100644 plugins/organization_ratings/public/images/user-not-logged.png create mode 100644 plugins/organization_ratings/public/organization_rating_management.js create mode 100644 plugins/organization_ratings/public/rate.js create mode 100644 plugins/organization_ratings/style.css create mode 100644 plugins/organization_ratings/test/functional/organization_ratings_plugin_admin_controller_test.rb create mode 100644 plugins/organization_ratings/test/functional/organization_ratings_plugin_profile_controller_test.rb create mode 100644 plugins/organization_ratings/test/unit/organization_rating_config_test.rb create mode 100644 plugins/organization_ratings/test/unit/organization_rating_test.rb create mode 100644 plugins/organization_ratings/test/unit/ratings_helper_test.rb create mode 100644 plugins/organization_ratings/views/blocks/display_organization_average_rating.html.erb create mode 100644 plugins/organization_ratings/views/blocks/organization_ratings_block.html.erb create mode 100644 plugins/organization_ratings/views/organization_ratings_plugin_admin/index.html.erb create mode 100644 plugins/organization_ratings/views/organization_ratings_plugin_profile/_new_rating_fields.html.erb create mode 100644 plugins/organization_ratings/views/organization_ratings_plugin_profile/new_rating.html.erb create mode 100644 plugins/organization_ratings/views/shared/_make_report_block.html.erb create mode 100644 plugins/organization_ratings/views/shared/_rating_button.html.erb create mode 100644 plugins/organization_ratings/views/shared/_user_rating_container.html.erb create mode 100644 plugins/organization_ratings/views/tasks/_create_organization_rating_comment_accept_details.html.erb diff --git a/lib/noosfero/plugin.rb b/lib/noosfero/plugin.rb index 105e189..1c273fd 100644 --- a/lib/noosfero/plugin.rb +++ b/lib/noosfero/plugin.rb @@ -241,6 +241,16 @@ class Noosfero::Plugin nil end + # -> Customize the way comments are counted for Profiles and Environment + # considering more than just articles comments + # Used on statistic block + # Ex: a plugin may want that Communities receive comments themselves + # as evaluations + # returns = the number of comments to be sum on the statistics + def more_comments_count owner + nil + end + # -> Adds tabs to the profile # returns = { :title => title, :id => id, :content => content, :start => start } # title = name that will be displayed. diff --git a/plugins/organization_ratings/controllers/organization_ratings_plugin_admin_controller.rb b/plugins/organization_ratings/controllers/organization_ratings_plugin_admin_controller.rb new file mode 100644 index 0000000..7e9c356 --- /dev/null +++ b/plugins/organization_ratings/controllers/organization_ratings_plugin_admin_controller.rb @@ -0,0 +1,19 @@ +class OrganizationRatingsPluginAdminController < PluginAdminController + + include RatingsHelper + helper :ratings + append_view_path File.join(File.dirname(__FILE__) + '/../views') + + def index + end + + def update + if env_organization_ratings_config.update_attributes(params[:organization_ratings_config]) + session[:notice] = _('Configuration updated successfully.') + else + session[:notice] = _('Configuration could not be saved.') + end + render :action => 'index' + end + +end \ No newline at end of file diff --git a/plugins/organization_ratings/controllers/organization_ratings_plugin_profile_controller.rb b/plugins/organization_ratings/controllers/organization_ratings_plugin_profile_controller.rb new file mode 100644 index 0000000..6381c53 --- /dev/null +++ b/plugins/organization_ratings/controllers/organization_ratings_plugin_profile_controller.rb @@ -0,0 +1,76 @@ +class OrganizationRatingsPluginProfileController < ProfileController + include RatingsHelper + helper :ratings + + def new_rating + @rating_available = user_can_rate_now? + @users_ratings = get_ratings(profile.id).paginate( + :per_page => env_organization_ratings_config.per_page, + :page => params[:npage] + ) + if request.post? + if @rating_available + create_new_rate + else + session[:notice] = _("You can not vote on this %s") % profile.class.name + end + end + end + + private + + def user_can_rate_now? + return false unless user + ratings = OrganizationRating.where( + :organization_id => profile.id, + :person_id => user.id + ) + + return false if (!ratings.empty? && env_organization_ratings_config.vote_once) + + if ratings.empty? + true + else + elapsed_time_since_last_rating = Time.zone.now - ratings.last.created_at + elapsed_time_since_last_rating > env_organization_ratings_config.cooldown.hours + end + end + + def create_new_rate + rating = OrganizationRating.new(params[:organization_rating]) + rating.person = current_user.person + rating.organization = profile + rating.value = params[:organization_rating_value] if params[:organization_rating_value] + + if rating.save + create_rating_comment(rating) + session[:notice] = _("%s successfully rated!") % profile.name + else + session[:notice] = _("Sorry, there were problems rating this profile.") + end + + redirect_to :controller => 'profile', :action => 'index' + end + + def create_rating_comment(rating) + if params[:comments] + comment_task = CreateOrganizationRatingComment.create!( + params[:comments].merge( + :requestor => rating.person, + :organization_rating_id => rating.id, + :target => rating.organization + ) + ) + comment_task.finish if can_perform?(params) + end + end + + def can_perform? (params) + (params[:comments][:body].blank? || + !env_organization_ratings_config.are_moderated) + end + + def permission + :manage_memberships + end +end diff --git a/plugins/organization_ratings/db/migrate/20150830225546_create_organization_ratings.rb b/plugins/organization_ratings/db/migrate/20150830225546_create_organization_ratings.rb new file mode 100644 index 0000000..96dcd0c --- /dev/null +++ b/plugins/organization_ratings/db/migrate/20150830225546_create_organization_ratings.rb @@ -0,0 +1,12 @@ +class CreateOrganizationRatings < ActiveRecord::Migration + def change + create_table :organization_ratings do |t| + t.belongs_to :organization + t.belongs_to :person + t.belongs_to :comment + t.integer :value + + t.timestamps + end + end +end diff --git a/plugins/organization_ratings/db/migrate/20150830225733_create_organization_ratings_config.rb b/plugins/organization_ratings/db/migrate/20150830225733_create_organization_ratings_config.rb new file mode 100644 index 0000000..fb1af9b --- /dev/null +++ b/plugins/organization_ratings/db/migrate/20150830225733_create_organization_ratings_config.rb @@ -0,0 +1,14 @@ +class CreateOrganizationRatingsConfig < ActiveRecord::Migration + + def change + create_table :organization_ratings_configs do |t| + t.belongs_to :environment + t.integer :cooldown, :integer, :default => 24 + t.integer :default_rating, :integer, :default => 1 + t.string :order, :string, :default => "recent" + t.integer :per_page, :integer, :default => 10 + t.boolean :vote_once, :boolean, :default => false + t.boolean :are_moderated, :boolean, :default => true + end + end +end diff --git a/plugins/organization_ratings/db/migrate/20150830230047_add_comments_count_to_profile.rb b/plugins/organization_ratings/db/migrate/20150830230047_add_comments_count_to_profile.rb new file mode 100644 index 0000000..f672b81 --- /dev/null +++ b/plugins/organization_ratings/db/migrate/20150830230047_add_comments_count_to_profile.rb @@ -0,0 +1,11 @@ +class AddCommentsCountToProfile < ActiveRecord::Migration + def self.up + change_table :profiles do |t| + t.integer :comments_count + end + end + + def self.down + remove_column :profiles, :comments_count + end +end \ No newline at end of file diff --git a/plugins/organization_ratings/features/rate_community.feature b/plugins/organization_ratings/features/rate_community.feature new file mode 100644 index 0000000..40f46b4 --- /dev/null +++ b/plugins/organization_ratings/features/rate_community.feature @@ -0,0 +1,30 @@ +Feature: rate_community + As a user + I want to be able rate a community + So that users can see my feedback about that community + + Background: + Given plugin "OrganizationRatings" is enabled on environment + Given the following user + | login | name | + | joaosilva | Joao Silva | + And the following community + | identifier | name | + | mycommunity | My Community | + And the following blocks + | owner | type | + | mycommunity | AverageRatingBlock | + | mycommunity | OrganizationRatingsBlock | + And the environment domain is "localhost" + And I am logged in as "joaosilva" + + @selenium + Scenario: display rate button inside average block + Given I am on mycommunity's homepage + Then I should see "Rate this Community" within ".average-rating-block" + And I should see "Be the first to rate" within ".average-rating-block" + + @selenium + Scenario: display rate button inside communities ratings block + Given I am on mycommunity's homepage + Then I should see "Rate Community" within ".make-report-block" diff --git a/plugins/organization_ratings/lib/average_rating_block.rb b/plugins/organization_ratings/lib/average_rating_block.rb new file mode 100644 index 0000000..5e0e378 --- /dev/null +++ b/plugins/organization_ratings/lib/average_rating_block.rb @@ -0,0 +1,30 @@ +class AverageRatingBlock < Block + include RatingsHelper + + def self.description + _('Organization Average Rating') + end + + def help + _('This block displays the organization average rating.') + end + + def content(args = {}) + profile_identifier = self.owner.identifier + average_rating = OrganizationRating.average_rating self.owner.id + + proc do + render( + :file => 'blocks/display_organization_average_rating', + :locals => { + :profile_identifier => profile_identifier, + :average_rating => average_rating + } + ) + end + end + + def cacheable? + false + end +end diff --git a/plugins/organization_ratings/lib/create_organization_rating_comment.rb b/plugins/organization_ratings/lib/create_organization_rating_comment.rb new file mode 100644 index 0000000..90a0dc0 --- /dev/null +++ b/plugins/organization_ratings/lib/create_organization_rating_comment.rb @@ -0,0 +1,131 @@ +class CreateOrganizationRatingComment < Task + include Rails.application.routes.url_helpers + + validates_presence_of :requestor_id, :organization_rating_id, :target_id + + settings_items :organization_rating_id, :type => Integer, :default => nil + settings_items :organization_rating_comment_id, :type => Integer, :default => nil + + attr_accessible :organization_rating_id, :body, :requestor + attr_accessible :reject_explanation, :target + + before_save :update_comment_body + + DATA_FIELDS = ['body'] + DATA_FIELDS.each do |field| + settings_items field.to_sym + end + + def update_comment_body + if self.organization_rating_comment_id.nil? + create_comment + else + comment = Comment.find_by_id(self.organization_rating_comment_id) + comment.body = get_comment_message + comment.save + end + end + + def create_comment + if (self.body && !self.body.blank?) + comment_body = _("Comment waiting for approval") + comment = Comment.create!(:source => self.target, :body => comment_body, :author => self.requestor) + + + self.organization_rating_comment_id = comment.id + link_comment_with_its_rating(comment) + end + end + + def link_comment_with_its_rating(user_comment) + rating = OrganizationRating.find(self.organization_rating_id) + rating.comment = user_comment + rating.save + end + + def get_comment_message + if self.status == Status::CANCELLED + _("Comment rejected") + elsif self.status == Status::FINISHED + self.body + else + _("No comment") + end + end + + def accept_details + true + end + + def title + _("New Comment") + end + + def information + message = _("%{requestor} wants to create a comment in this %{target_class}") % + {:requestor_url => url_for(self.requestor.url), :requestor => self.requestor.name, :target_class => self.target.class.name.downcase} + + {:message => message} + end + + def reject_details + true + end + + def icon + {:type => :profile_image, :profile => requestor, :url => requestor.url} + end + + # tells if this request was rejected + def rejected? + self.status == Task::Status::CANCELLED + end + + # tells if this request was appoved + def approved? + self.status == Task::Status::FINISHED + end + + def target_notification_description + _("%{requestor} wants to create a comment in this \"%{target}\"") % + {:requestor => self.requestor.name, :target => self.target.class.name.downcase } + end + + def target_notification_message + _("User \"%{user}\" just requested to create a comment in the %{target_class} + \"%{target_name}\". + You have to approve or reject it through the \"Pending Validations\" + section in your control panel.\n") % + { :user => self.requestor.name, :target_class => self.target.class.name.downcase, :target_name => self.target.name } + end + + def task_created_message + _("Your request for commenting at %{target} was + just sent. Environment administrator will receive it and will approve or + reject your request according to his methods and criteria. + You will be notified as soon as environment administrator has a position + about your request.") % + { :target => self.target.name } + end + + def task_cancelled_message + _("Your request for commenting at %{target} was + not approved by the environment administrator. The following explanation + was given: \n\n%{explanation}") % + { :target => self.target.name, + :explanation => self.reject_explanation } + end + + def task_finished_message + _('Your request for commenting was approved. + You can access %{url} to see your comment.') % + { :url => ratings_url } + end + + private + + def ratings_url + url = url_for(self.target.public_profile_url) + "/plugin/organization_ratings/new_rating" + end + +end diff --git a/plugins/organization_ratings/lib/ext/comments.rb b/plugins/organization_ratings/lib/ext/comments.rb new file mode 100644 index 0000000..65451fa --- /dev/null +++ b/plugins/organization_ratings/lib/ext/comments.rb @@ -0,0 +1,6 @@ +require_dependency "comment" + +Comment.class_eval do + + has_one :organization_rating +end diff --git a/plugins/organization_ratings/lib/ext/environment.rb b/plugins/organization_ratings/lib/ext/environment.rb new file mode 100644 index 0000000..1ca4f2c --- /dev/null +++ b/plugins/organization_ratings/lib/ext/environment.rb @@ -0,0 +1,5 @@ +require_dependency "environment" + +class Environment + has_one :organization_ratings_config +end diff --git a/plugins/organization_ratings/lib/ext/organization.rb b/plugins/organization_ratings/lib/ext/organization.rb new file mode 100644 index 0000000..e9af89b --- /dev/null +++ b/plugins/organization_ratings/lib/ext/organization.rb @@ -0,0 +1,7 @@ +require_dependency 'organization' + +Organization.class_eval do + has_many :organization_ratings + + has_many :comments, :class_name => 'Comment', :foreign_key => 'source_id', :dependent => :destroy, :order => 'created_at asc' +end diff --git a/plugins/organization_ratings/lib/ext/person.rb b/plugins/organization_ratings/lib/ext/person.rb new file mode 100644 index 0000000..5b6061d --- /dev/null +++ b/plugins/organization_ratings/lib/ext/person.rb @@ -0,0 +1,5 @@ +require_dependency 'person' + +Person.class_eval do + has_many :organization_ratings +end diff --git a/plugins/organization_ratings/lib/organization_rating.rb b/plugins/organization_ratings/lib/organization_rating.rb new file mode 100644 index 0000000..3bdeba1 --- /dev/null +++ b/plugins/organization_ratings/lib/organization_rating.rb @@ -0,0 +1,25 @@ +class OrganizationRating < ActiveRecord::Base + belongs_to :person + belongs_to :organization + belongs_to :comment + + attr_accessible :value, :person, :organization, :comment + + validates :value, + :presence => true, :inclusion => { + in: 1..5, message: _("must be between 1 and 5") + } + + validates :organization_id, :person_id, + :presence => true + + + def self.average_rating organization_id + average = OrganizationRating.where(organization_id: organization_id).average(:value) + + if average + (average - average.truncate) >= 0.5 ? average.ceil : average.floor + end + end + +end diff --git a/plugins/organization_ratings/lib/organization_ratings_block.rb b/plugins/organization_ratings/lib/organization_ratings_block.rb new file mode 100644 index 0000000..9975eb9 --- /dev/null +++ b/plugins/organization_ratings/lib/organization_ratings_block.rb @@ -0,0 +1,30 @@ +class OrganizationRatingsBlock < Block + include RatingsHelper + + def self.description + _('Organization Ratings') + end + + def help + _('This block displays the community ratings.') + end + + def content(args = {}) + block = self + + proc do + render( + :file => 'blocks/organization_ratings_block', + :locals => {:block => block} + ) + end + end + + def limit_number_of_ratings + env_organization_ratings_config.per_page + end + + def cacheable? + false + end +end diff --git a/plugins/organization_ratings/lib/organization_ratings_config.rb b/plugins/organization_ratings/lib/organization_ratings_config.rb new file mode 100644 index 0000000..2d89f40 --- /dev/null +++ b/plugins/organization_ratings/lib/organization_ratings_config.rb @@ -0,0 +1,53 @@ +class OrganizationRatingsConfig < ActiveRecord::Base + + belongs_to :environment + + attr_accessible :cooldown, :default_rating, :order, :per_page + attr_accessible :vote_once, :are_moderated, :environment_id + + ORDER_OPTIONS = {recent: _('More Recent'), best: _('Best Ratings')} + + MINIMUM_RATING = 1 + MAX_COOLDOWN = 1000 + + validates :default_rating, + :presence => true, :numericality => { + greater_than_or_equal_to: MINIMUM_RATING, + less_than_or_equal_to: 5 + } + + validates :cooldown, + :presence => true, :numericality => { + greater_than_or_equal_to: 0, + less_than_or_equal_to: MAX_COOLDOWN + } + + validates :per_page, + :presence => true, :numericality => { + :greater_than_or_equal_to => 5, + :less_than_or_equal_to => 20 + } + + + def order_options + ORDER_OPTIONS + end + + def minimum_ratings + MINIMUM_RATING + end + + def max_cooldown + MAX_COOLDOWN + end + + class << self + def instance + environment = Environment.default + environment.organization_ratings_config || create(environment_id: environment.id) + end + + private :new + end + +end diff --git a/plugins/organization_ratings/lib/organization_ratings_plugin.rb b/plugins/organization_ratings/lib/organization_ratings_plugin.rb new file mode 100644 index 0000000..7ddff56 --- /dev/null +++ b/plugins/organization_ratings/lib/organization_ratings_plugin.rb @@ -0,0 +1,73 @@ +class OrganizationRatingsPlugin < Noosfero::Plugin + include Noosfero::Plugin::HotSpot + + def self.plugin_name + "Organization Ratings" + end + + def self.plugin_description + _("A plugin that allows you to rate a organization and comment about it.") + end + + module Hotspots + def organization_ratings_plugin_comments_extra_fields + nil + end + + def organization_ratings_title + nil + end + + def organization_ratings_plugin_star_message + nil + end + + def organization_ratings_plugin_extra_fields_show_data user_rating + nil + end + end + + # Plugin Hotspot to display the average rating + def display_organization_average_rating organization + unless organization.nil? + average_rating = OrganizationRating.average_rating organization.id + + Proc::new { + render :file => 'blocks/display_organization_average_rating', + :locals => { + :profile_identifier => organization.identifier, + :average_rating => average_rating + } + } + end + end + + def more_comments_count owner + if owner.kind_of?(Environment) then + owner.profiles.sum(:comments_count) + elsif owner.kind_of?(Profile) then + owner.comments_count + else + 0 + end + end + + def self.extra_blocks + { + OrganizationRatingsBlock => {:type => [Enterprise, Community], :position => ['1']}, + AverageRatingBlock => {:type => [Enterprise, Community]} + } + end + + def stylesheet? + true + end + + def js_files + %w( + public/rate.js + public/comunities_rating_management.js + ) + end + +end diff --git a/plugins/organization_ratings/lib/ratings_helper.rb b/plugins/organization_ratings/lib/ratings_helper.rb new file mode 100644 index 0000000..c915341 --- /dev/null +++ b/plugins/organization_ratings/lib/ratings_helper.rb @@ -0,0 +1,15 @@ +module RatingsHelper + + def env_organization_ratings_config + OrganizationRatingsConfig.instance + end + + def get_ratings (profile_id) + order_options = env_organization_ratings_config.order_options + if env_organization_ratings_config.order.downcase == order_options[:recent] + ratings = OrganizationRating.where(organization_id: profile_id).order("value DESC") + else + ratings = OrganizationRating.where(organization_id: profile_id).order("created_at DESC") + end + end +end \ No newline at end of file diff --git a/plugins/organization_ratings/public/images/small-star-negative.png b/plugins/organization_ratings/public/images/small-star-negative.png new file mode 100644 index 0000000..eabea40 Binary files /dev/null and b/plugins/organization_ratings/public/images/small-star-negative.png differ diff --git a/plugins/organization_ratings/public/images/small-star-positive.png b/plugins/organization_ratings/public/images/small-star-positive.png new file mode 100644 index 0000000..febf0de Binary files /dev/null and b/plugins/organization_ratings/public/images/small-star-positive.png differ diff --git a/plugins/organization_ratings/public/images/star-negative-medium.png b/plugins/organization_ratings/public/images/star-negative-medium.png new file mode 100644 index 0000000..41973e7 Binary files /dev/null and b/plugins/organization_ratings/public/images/star-negative-medium.png differ diff --git a/plugins/organization_ratings/public/images/star-negative.png b/plugins/organization_ratings/public/images/star-negative.png new file mode 100644 index 0000000..41973e7 Binary files /dev/null and b/plugins/organization_ratings/public/images/star-negative.png differ diff --git a/plugins/organization_ratings/public/images/star-positive-medium.png b/plugins/organization_ratings/public/images/star-positive-medium.png new file mode 100644 index 0000000..ca15335 Binary files /dev/null and b/plugins/organization_ratings/public/images/star-positive-medium.png differ diff --git a/plugins/organization_ratings/public/images/star-positive.png b/plugins/organization_ratings/public/images/star-positive.png new file mode 100644 index 0000000..ca15335 Binary files /dev/null and b/plugins/organization_ratings/public/images/star-positive.png differ diff --git a/plugins/organization_ratings/public/images/user-not-logged.png b/plugins/organization_ratings/public/images/user-not-logged.png new file mode 100644 index 0000000..7b433a4 Binary files /dev/null and b/plugins/organization_ratings/public/images/user-not-logged.png differ diff --git a/plugins/organization_ratings/public/organization_rating_management.js b/plugins/organization_ratings/public/organization_rating_management.js new file mode 100644 index 0000000..f4ad7ba --- /dev/null +++ b/plugins/organization_ratings/public/organization_rating_management.js @@ -0,0 +1,35 @@ +(function($) { + "use strict"; + + var VoteOnce = { + init: function() { + this.cacheDom(); + this.setEvents(); + }, + + + cacheDom: function() { + this.$vote_once_checkbox = $("#environment_organization_ratings_vote_once"); + this.$hours_timer_input = $("#environment_organization_ratings_cooldown"); + }, + + + setEvents: function() { + this.$vote_once_checkbox.on("click", this.verifyHoursTimerDisable.bind(this)); + }, + + + verifyHoursTimerDisable: function() { + if (this.$vote_once_checkbox.is(":checked")) { + this.$hours_timer_input.attr("disabled", "disabled"); + } else { + this.$hours_timer_input.removeAttr("disabled"); + } + } + } + + + $(document).ready(function() { + VoteOnce.init(); + }); +}) (jQuery); diff --git a/plugins/organization_ratings/public/rate.js b/plugins/organization_ratings/public/rate.js new file mode 100644 index 0000000..10f40e4 --- /dev/null +++ b/plugins/organization_ratings/public/rate.js @@ -0,0 +1,122 @@ +;(function($, undefined) { + "use strict"; + + /* + * All global data that are used in the stars feature. + */ + var DATA = { + selected_rate: 0, // The actual selected star when the user click on a star + maximum_stars: 5, // (const) The maximum number of allowed stars + minimum_stars: 1, // (const) The minimum number of allowed stars + DATA_RATE_ATTRIBUTE: "data-star-rate", // (const) The data attribute with the star rate + NOT_SELECTED_VALUE: 0 // (const) The value when there is no selected rate + } + + + /* + * Prepare the global data that are variable. + * If the user already rated the organization, set the selected_rate as the rated value + */ + function set_global_data() { + var selected_rate = parseInt($("#selected-star-rate").val()); + var minimum_stars = parseInt($("#minimum_stars").val()); + DATA.selected_rate = selected_rate; + DATA.minimum_stars = minimum_stars; + } + + + /* + * Given a start rate, an end rate and the elements, it makes a regex that filter + * the elements by the given range and returns it. + */ + function star_filter(start, end, elements) { + var test_regex = undefined; + + // Verify if it is a valid range and makes its range regex: /[start-end]/ + if (end >= start) { + test_regex = new RegExp("["+(start)+"-"+(end)+"]"); + } else { + // If the range is invalid, make a regex that will return no element + test_regex = new RegExp("[]"); + } + + // Filter the elements that are in the given range + var result = elements.filter(function(i, element) { + var rate = parseInt(element.getAttribute(DATA.DATA_RATE_ATTRIBUTE)); + + return test_regex.test(rate); + }); + + return result; + } + + + /* + * Show or hide the stars depending on the mouse position and the limit rate. + * Given the mouseover rate, the limit rate and the css classes to be swapped, + * + * It verify if the user already selected a star rate: + * If true: + * It swap the css classes from the selected star up to the given limit rate + * If false: + * It swap the css classes from the minimum rate up to the mouseover rate + */ + function change_stars_class(rate_mouseover, limit_rate, remove_class, add_class) { + var previous_stars = undefined; + + // The default not selected rate value is 0 and minimum is 1. + if (DATA.selected_rate >= DATA.minimum_stars) { + previous_stars = star_filter(DATA.selected_rate+1, limit_rate, $("."+remove_class)); + } else { + previous_stars = star_filter(DATA.minimum_stars, rate_mouseover, $("."+remove_class)); + } + + previous_stars.switchClass(remove_class, add_class); + } + + + /* + * Sets the stars mouse events. + */ + function set_star_hover_actions() { + $(".star-negative, .star-positive") + .on("mouseover", function() { // On mouse over, show the current rate star + var rate_mouseover = parseInt(this.getAttribute(DATA.DATA_RATE_ATTRIBUTE)); + + change_stars_class(rate_mouseover, rate_mouseover, "star-negative", "star-positive"); + }) + + .on("mouseout", function() { // On mouse out, hide the stars + var rate_mouseover = parseInt(this.getAttribute(DATA.DATA_RATE_ATTRIBUTE)); + + change_stars_class(rate_mouseover, DATA.maximum_stars, "star-positive", "star-negative"); + }) + + .on("click", function() { // On mouse click, set the selected star rate + var rate_mouseover = parseInt(this.getAttribute(DATA.DATA_RATE_ATTRIBUTE)); + + // If the new rate is different from actual, update it + if (rate_mouseover !== DATA.selected_rate && rate_mouseover > DATA.minimum_stars) { + DATA.selected_rate = rate_mouseover; + } else { // or else, uncheck it + DATA.selected_rate = DATA.minimum_stars; + } + + // Mark the selected_rate + $("#selected-star-rate").val(DATA.selected_rate); + + var star_notice = $(".star-notice"); + star_notice.find("span").html(DATA.selected_rate); + star_notice.removeClass("star-hide"); + }); + } + + + /* + * When the page DOM is ready, set all the stars events + */ + $(document).ready(function() { + set_global_data(); + set_star_hover_actions(); + }); +}) (jQuery); diff --git a/plugins/organization_ratings/style.css b/plugins/organization_ratings/style.css new file mode 100644 index 0000000..408f0e9 --- /dev/null +++ b/plugins/organization_ratings/style.css @@ -0,0 +1,200 @@ +.star-container { + width: 100%; + height: 20px; +} + +.star-negative, .star-positive { + width: 20px; + height: 20px; + background-repeat: no-repeat; + margin-right: 2px; + position: relative; + float: left; + cursor: pointer; +} + +.star-negative { + background-image: url('public/images/star-negative.png'); +} + +.star-positive { + background-image: url('public/images/star-positive.png'); +} + +.small-star-negative, .small-star-positive { + background-repeat: no-repeat; + float: left; + height: 15px; + margin-right: 2px; + position: relative; + width: 15px; +} + +.small-star-negative { + background-image: url('public/images/small-star-negative.png'); +} + +.small-star-positive { + background-image: url('public/images/small-star-positive.png'); +} + +.medium-star-negative, .medium-star-positive { + background-repeat: no-repeat; + float: left; + height: 20px; + margin-right: 2px; + position: relative; + width: 20px; +} + +.medium-star-positive { + background-image: url('public/images/star-positive-medium.png'); +} + +.medium-star-negative { + background-image: url('public/images/star-negative-medium.png'); +} + +.star-hide { + display: none; +} + +.organization-average-rating-container { + border-top: 1px dotted #D3D6DE; + margin-top: 20px; + padding-top: 10px; +} + +.organization-average-rating-container .star-rate-text { + float: left; + margin-right: 10px; + padding-top: 5px; +} + +.organization-average-rating-container .rating-invitation { + font-size: 14px; + float: left; + margin-right: 10px; + padding-top: 3px; +} + +.organization-average-rating-container .star-container { + float: left; + width: 120px; +} + +.organization-average-rating-container .rate-this-organization { + border-left: 1px dotted #D3D6DE; + float: left; + padding: 4px 0px 2px 10px; +} + +.star-rate-data { + width: 100%; + padding-top: 20px; + position: relative; + overflow: auto; +} + +.star-profile-information, .star-rate-form { + display: table-cell; + vertical-align: top; + width: 362px; +} + +.star-profile-information { + width: 134px; +} + +.star-rate-form { + display: table-cell; + vertical-align: top; +} + +.star-profile-image, .star-profile-name { + text-align: center; +} + +.star-profile-name { + word-break: break-word; + margin: auto; + margin-top: 5px; + width: 66px; +} + +.star-rate-data .star-rate-form .star-comment-container .formfield textarea { + width: 361px; +} + +/************* Users ratings list ****************/ + +.ratings-list .user-rating-block, +.ratings-list .make-report-block { + border-top: 1px solid #D3D6DE; + margin-top: 25px; + padding-top: 20px; +} + +.ratings-list .make-report-block { + padding-bottom: 25px; +} + +.ratings-list .see-more{ + border-top: 1px solid #D3D6DE; +} +.ratings-list .user-rating-block .user-testimony-container { + display: table-cell; + padding-left: 20px; +} + +.ratings-list .make-report-block .make-report-container { + display: table-cell; +} + +.ratings-list .user-rating-block .user-testimony-container .star-container { + display: table-cell; +} + +.ratings-list .user-rating-block .user-testimony-container .testimony-rate-date { + display: table-cell; + max-width: 95px; + min-width: 95px; + padding-right: 160px; + white-space: nowrap; +} + +.ratings-list .user-rating-block .user-testimony-container .user-testimony { + margin-top: 10px; + word-break: break-word; +} + +.ratings-list .make-report-block .make-report-container .make-report-message { + font-size: 14px; + font-style: italic; + word-break: break-word; +} + +.ratings-list .make-report-block .make-report-container .button-bar { + overflow: auto; + padding-top: 15px; +} + +.ratings-list .user-rating-block .star-profile-information { + border-right: 1px dotted #D3D6DE; +} + +.ratings-list .icon-arrow-right-p { + background: url(/designs/themes/base/imgs/arrow-right-p.png) 100% 50% no-repeat; + display: block; + float: right; + margin-top: 20px; + padding-right: 15px; +} + +.task_information .comment { + padding-left: 60px; +} + +.average-rating-block { + height: 55px; +} diff --git a/plugins/organization_ratings/test/functional/organization_ratings_plugin_admin_controller_test.rb b/plugins/organization_ratings/test/functional/organization_ratings_plugin_admin_controller_test.rb new file mode 100644 index 0000000..c635d37 --- /dev/null +++ b/plugins/organization_ratings/test/functional/organization_ratings_plugin_admin_controller_test.rb @@ -0,0 +1,49 @@ +require File.expand_path(File.dirname(__FILE__)) + '/../../../../test/test_helper' +require File.expand_path(File.dirname(__FILE__)) + '/../../controllers/organization_ratings_plugin_admin_controller' + +# Re-raise errors caught by the controller. +class OrganizationRatingsPluginAdminController; def rescue_action(e) raise e end; end + +class OrganizationRatingsPluginAdminControllerTest < ActionController::TestCase + + def setup + @controller = OrganizationRatingsPluginAdminController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + + @environment = Environment.default + @environment.enabled_plugins = ['OrganizationRatingsPlugin'] + @environment.save + + @community = Community.create(:name => "TestCommunity") + + login_as(create_admin_user(@environment)) + end + + test "should update organization rating plugin configuration" do + post :update, :organization_ratings_config => { :default_rating => 5, + :cooldown => 12, + :order => "recent", + :per_page => 10, + :vote_once => true } + + assert :success + @environment.reload + assert_equal 5, @environment.organization_ratings_config.default_rating + assert_equal "Configuration updated successfully.", session[:notice] + end + + test "should not update organization rating plugin configuration with negative cooldown time" do + post :update, :organization_ratings_config => { :default_rating => 5, + :cooldown => -50, + :order => "recent", + :per_page => 10, + :vote_once => true } + + assert :success + @environment.reload + assert_equal 24, @environment.organization_ratings_config.cooldown + assert_equal "Configuration could not be saved.", session[:notice] + end +end + diff --git a/plugins/organization_ratings/test/functional/organization_ratings_plugin_profile_controller_test.rb b/plugins/organization_ratings/test/functional/organization_ratings_plugin_profile_controller_test.rb new file mode 100644 index 0000000..3385ea0 --- /dev/null +++ b/plugins/organization_ratings/test/functional/organization_ratings_plugin_profile_controller_test.rb @@ -0,0 +1,104 @@ +require File.expand_path(File.dirname(__FILE__)) + '/../../../../test/test_helper' +require File.expand_path(File.dirname(__FILE__)) + '/../../controllers/organization_ratings_plugin_profile_controller' + +# Re-raise errors caught by the controller. +class OrganizationRatingsPluginProfileController; def rescue_action(e) raise e end; end + +class OrganizationRatingsPluginProfileControllerTest < ActionController::TestCase + + def setup + @controller = OrganizationRatingsPluginProfileController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + + @environment = Environment.default + @environment.enabled_plugins = ['OrganizationRatingsPlugin'] + @environment.save + + @person = create_user('testuser').person + @community = Community.create(:name => "TestCommunity") + @enterprise = fast_create(Enterprise) + @config = OrganizationRatingsConfig.instance + login_as(@person.identifier) + @controller.stubs(:logged_in?).returns(true) + @controller.stubs(:current_user).returns(@person.user) + end + + test "should add new comment to community" do + post :new_rating, profile: @community.identifier, :comments => {:body => "This is a test"}, :organization_rating_value => 4 + assert_equal "#{@community.name} successfully rated!", session[:notice] + end + + test "Create community_rating without comment body" do + post :new_rating, profile: @community.identifier, :comments => {:body => ""}, :organization_rating_value => 2 + + assert_equal "#{@community.name} successfully rated!", session[:notice] + end + + test "Do not create community_rating without a rate value" do + post :new_rating, profile: @community.identifier, :comments => {:body => ""}, :organization_rating_value => nil + + assert_equal "Sorry, there were problems rating this profile.", session[:notice] + end + + test "do not create two ratings on Community when vote once config is true" do + post :new_rating, profile: @community.identifier, :comments => {:body => "This is a test"}, :organization_rating_value => 3 + + assert_equal "#{@community.name} successfully rated!", session[:notice] + + @environment.organization_ratings_config.vote_once = true + @environment.save + + post :new_rating, profile: @community.identifier, :comments => {:body => "This is a test 2"}, :organization_rating_value => 3 + assert_equal "You can not vote on this Community", session[:notice] + end + + test "do not create two ratings on Enterprise when vote once config is true" do + post :new_rating, profile: @enterprise.identifier, :comments => {:body => "This is a test"}, :organization_rating_value => 3 + + assert_equal "#{@enterprise.name} successfully rated!", session[:notice] + + @environment.organization_ratings_config.vote_once = true + @environment.save + + post :new_rating, profile: @enterprise.identifier, :comments => {:body => "This is a test 2"}, :organization_rating_value => 3 + assert_equal "You can not vote on this Enterprise", session[:notice] + end + + test "should count organization ratings on statistic block when block owner = Environment" do + block = StatisticsBlock.new + enterprise = fast_create(Enterprise) + post :new_rating, profile: enterprise.identifier, :comments => {:body => "body board"}, :organization_rating_value => 1 + enterprise.reload + @environment.reload + block.expects(:owner).at_least_once.returns(@environment) + assert_equal 1, block.comments + end + + + test "should count organization ratings on statistic block when block owner = Profile" do + @config.cooldown = 0 + @config.save + + block = StatisticsBlock.new + + post :new_rating, profile: @community.identifier, :comments => {:body => "body board"}, :organization_rating_value => 1 + post :new_rating, profile: @community.identifier, :comments => {:body => "body surf"}, :organization_rating_value => 5 + + block.expects(:owner).at_least_once.returns(@community) + @community.reload + assert_equal 2, block.comments + end + + test "Display unavailable rating message for users that must wait the rating cooldown time" do + post :new_rating, profile: @community.identifier, :comments => {:body => ""}, :organization_rating_value => 3 + assert_not_match(/The administrators set the minimum time of/, @response.body) + valid_rating = OrganizationRating.last + + post :new_rating, profile: @community.identifier, :comments => {:body => ""}, :organization_rating_value => 3 + assert_match(/The administrators set the minimum time of/, @response.body) + new_rating = OrganizationRating.last + + assert_equal valid_rating.id, new_rating.id + end +end diff --git a/plugins/organization_ratings/test/unit/organization_rating_config_test.rb b/plugins/organization_ratings/test/unit/organization_rating_config_test.rb new file mode 100644 index 0000000..9833c06 --- /dev/null +++ b/plugins/organization_ratings/test/unit/organization_rating_config_test.rb @@ -0,0 +1,43 @@ +require File.expand_path(File.dirname(__FILE__)) + '/../../../../test/test_helper' + +class OrganizationRatingConfigTest < ActiveSupport::TestCase + + def setup + @environment = Environment.default + @environment.enabled_plugins = ['OrganizationRatingsPlugin'] + @environment.save + @organization_ratings_config = OrganizationRatingsConfig.instance + end + + test "Community ratings config default rating validation" do + @organization_ratings_config.default_rating = 0 + @organization_ratings_config.save + + assert_equal false, @organization_ratings_config.valid? + assert_equal "must be greater than or equal to 1", @organization_ratings_config.errors[:default_rating].first + + @organization_ratings_config.default_rating = 6 + assert_equal false, @organization_ratings_config.valid? + + assert_equal "must be less than or equal to 5", @organization_ratings_config.errors[:default_rating].first + end + + test "Communities ratings config cooldown validation" do + @organization_ratings_config.cooldown = -1 + assert_equal false, @organization_ratings_config.valid? + + assert_equal "must be greater than or equal to 0", @organization_ratings_config.errors[:cooldown].first + end + + # test "communities ratings per page validation" do + # environment = Environment.new :communities_ratings_per_page => 4 + # environment.valid? + + # assert_equal "must be greater than or equal to 5", environment.errors[:communities_ratings_per_page].first + + # environment.communities_ratings_per_page = 21 + # environment.valid? + + # assert_equal "must be less than or equal to 20", environment.errors[:communities_ratings_per_page].first + # end +end diff --git a/plugins/organization_ratings/test/unit/organization_rating_test.rb b/plugins/organization_ratings/test/unit/organization_rating_test.rb new file mode 100644 index 0000000..89f4fe8 --- /dev/null +++ b/plugins/organization_ratings/test/unit/organization_rating_test.rb @@ -0,0 +1,154 @@ +require File.expand_path(File.dirname(__FILE__)) + '/../../../../test/test_helper' + +class OrganizationRatingTest < ActiveSupport::TestCase + test "The value must be between 1 and 5" do + organization_rating1 = OrganizationRating.new :value => -1 + organization_rating2 = OrganizationRating.new :value => 6 + + assert_equal false, organization_rating1.valid? + assert_equal false, organization_rating2.valid? + + assert_equal true, organization_rating1.errors[:value].include?("must be between 1 and 5") + assert_equal true, organization_rating2.errors[:value].include?("must be between 1 and 5") + + organization_rating1.value = 1 + organization_rating1.valid? + + organization_rating2.value = 5 + organization_rating2.valid? + + assert_equal false, organization_rating1.errors[:value].include?("must be between 1 and 5") + assert_equal false, organization_rating2.errors[:value].include?("must be between 1 and 5") + end + + test "Create task for create a rating comment" do + person = create_user('molly').person + person.email = "person@email.com" + person.save! + + community = fast_create(Community) + community.add_admin(person) + + organization_rating = OrganizationRating.create!( + :value => 3, + :person => person, + :organization => community + ) + + create_organization_rating_comment = CreateOrganizationRatingComment.create!( + :requestor => person, + :organization_rating_id => organization_rating.id, + :target => community + ) + + assert community.tasks.include?(create_organization_rating_comment) + end + + test "Check comment message when Task status = ACTIVE" do + person = create_user('molly').person + person.email = "person@email.com" + person.save! + + community = fast_create(Community) + community.add_admin(person) + + + organization_rating = OrganizationRating.create!( + :value => 3, + :person => person, + :organization => community + ) + + create_organization_rating_comment = CreateOrganizationRatingComment.create!( + :requestor => person, + :organization_rating_id => organization_rating.id, + :target => community, + :body => "sample comment" + ) + assert_equal 1, create_organization_rating_comment.status + message = "Comment waiting for approval" + comment = Comment.find_by_id(create_organization_rating_comment.organization_rating_comment_id) + assert_equal message, comment.body + end + + test "Check comment message when Task status = CANCELLED" do + person = create_user('molly').person + person.email = "person@email.com" + person.save! + + community = fast_create(Community) + community.add_admin(person) + + + organization_rating = OrganizationRating.create!( + :value => 3, + :person => person, + :organization => community + ) + + create_organization_rating_comment = CreateOrganizationRatingComment.create!( + :requestor => person, + :organization_rating_id => organization_rating.id, + :target => community, + :body => "sample comment" + ) + create_organization_rating_comment.cancel + assert_equal 2, create_organization_rating_comment.status + message = "Comment rejected" + comment = Comment.find_by_id(create_organization_rating_comment.organization_rating_comment_id) + assert_equal message, comment.body + end + + test "Check comment message when Task status = FINISHED" do + person = create_user('molly').person + person.email = "person@email.com" + person.save! + + community = fast_create(Community) + community.add_admin(person) + + comment = Comment.create!(source: community, + body: "regular comment", + author: person) + + organization_rating = OrganizationRating.create!( + :value => 3, + :person => person, + :organization => community, + :comment => comment + ) + + create_organization_rating_comment = CreateOrganizationRatingComment.create!( + :body => comment.body, + :requestor => organization_rating.person, + :organization_rating_id => organization_rating.id, + :target => organization_rating.organization, + :body => "sample comment" + ) + + create_organization_rating_comment.finish + assert_equal 3, create_organization_rating_comment.status + message = "sample comment" + comment = Comment.find_by_id(create_organization_rating_comment.organization_rating_comment_id) + assert_equal message, comment.body + end + + + test "Should calculate community's rating average" do + community = fast_create Community + p1 = fast_create Person, :name=>"Person 1" + p2 = fast_create Person, :name=>"Person 2" + p3 = fast_create Person, :name=>"Person 3" + + OrganizationRating.create! :value => 2, :organization => community, :person => p1 + OrganizationRating.create! :value => 3, :organization => community, :person => p2 + OrganizationRating.create! :value => 5, :organization => community, :person => p3 + + assert_equal 3, OrganizationRating.average_rating(community) + + p4 = fast_create Person, :name=>"Person 4" + OrganizationRating.create! :value => 4, :organization => community, :person => p4 + + assert_equal 4, OrganizationRating.average_rating(community) + end +end diff --git a/plugins/organization_ratings/test/unit/ratings_helper_test.rb b/plugins/organization_ratings/test/unit/ratings_helper_test.rb new file mode 100644 index 0000000..b408309 --- /dev/null +++ b/plugins/organization_ratings/test/unit/ratings_helper_test.rb @@ -0,0 +1,63 @@ +require File.expand_path(File.dirname(__FILE__)) + '/../../../../test/test_helper' +require 'ratings_helper' + +class RatingsHelperTest < ActiveSupport::TestCase + include RatingsHelper + + def setup + + @environment = Environment.default + @environment.enabled_plugins = ['OrganizationRatingsPlugin'] + @environment.save + @person = create_user('testuser').person + @community = Community.create(:name => "TestCommunity") + @organization_ratings_config = OrganizationRatingsConfig.instance + end + + should "get the ratings of a community ordered by most recent ratings" do + ratings_array = [] + + first_rating = OrganizationRating.new + first_rating.organization = @community + first_rating.person = @person + first_rating.value = 3 + first_rating.save + + most_recent_rating = OrganizationRating.new + most_recent_rating.organization = @community + most_recent_rating.person = @person + most_recent_rating.value = 5 + sleep 2 + most_recent_rating.save + + ratings_array << most_recent_rating + ratings_array << first_rating + + assert_equal @organization_ratings_config.order, "recent" + assert_equal ratings_array, get_ratings(@community.id) + end + + should "get the ratings of a community ordered by best ratings" do + ratings_array = [] + @organization_ratings_config = "best" + @environment.save + + first_rating = OrganizationRating.new + first_rating.organization = @community + first_rating.person = @person + first_rating.value = 3 + first_rating.save + + second_rating = OrganizationRating.new + second_rating.organization = @community + second_rating.person = @person + second_rating.value = 5 + sleep 2 + second_rating.save + + ratings_array << second_rating + ratings_array << first_rating + + assert_equal ratings_array, get_ratings(@community.id) + end +end diff --git a/plugins/organization_ratings/views/blocks/display_organization_average_rating.html.erb b/plugins/organization_ratings/views/blocks/display_organization_average_rating.html.erb new file mode 100644 index 0000000..2d4da83 --- /dev/null +++ b/plugins/organization_ratings/views/blocks/display_organization_average_rating.html.erb @@ -0,0 +1,25 @@ +
+ <% if average_rating %> +
+ <%= _("Rating: ") %> +
+ +
+ <% (1..5).each do |star_number| %> + <% if star_number <= average_rating %> +
+ <% else %> +
+ <% end %> + <% end %> +
+ <% else %> +
+ <%= _("Be the first to rate!") %> +
+ <% end %> + +
+ <%= link_to _("Rate this %s" % profile.class.name), url_for(:controller => "organization_ratings_plugin_profile", :action => "new_rating", :profile=>profile_identifier) %> +
+
\ No newline at end of file diff --git a/plugins/organization_ratings/views/blocks/organization_ratings_block.html.erb b/plugins/organization_ratings/views/blocks/organization_ratings_block.html.erb new file mode 100644 index 0000000..2c51625 --- /dev/null +++ b/plugins/organization_ratings/views/blocks/organization_ratings_block.html.erb @@ -0,0 +1,21 @@ +
+ <%= block_title(block.title) %> + <% if block.get_ratings(block.owner.id).empty? %> +
+ <%= render :partial => 'shared/make_report_block' %> +
+ <% else %> +
+ <% block.get_ratings(block.owner.id).each_with_index do |r, index| %> + <% break if index >= block.limit_number_of_ratings %> + <%= render :partial => "shared/user_rating_container", :locals => {:user_rate => r} %> + <% end %> + + <%= render :partial => 'shared/make_report_block' %> + +
+ <%= link_to _('See more'), url_for(:controller => 'organization_ratings_plugin_profile', :action => 'new_rating'), :class => 'icon-arrow-right-p' %> +
+
+ <% end %> +
diff --git a/plugins/organization_ratings/views/organization_ratings_plugin_admin/index.html.erb b/plugins/organization_ratings/views/organization_ratings_plugin_admin/index.html.erb new file mode 100644 index 0000000..212e377 --- /dev/null +++ b/plugins/organization_ratings/views/organization_ratings_plugin_admin/index.html.erb @@ -0,0 +1,54 @@ +<% config = env_organization_ratings_config %> + +

<%= _("Organization Rating Management") %>

+ +<%= labelled_form_for(:organization_ratings_config, :url => {:action => 'update'}) do |f| %> + <%= labelled_fields_for(:organization_ratings_config, config) do |c| %> + + + + + + + + + + + + + + + + + + + + <% hours_options = {size: 2} %> + <% hours_options[:disabled] = "disabled" if config.vote_once %> + + + + + <% order_options = [] %> + <% config.order_options.select{|k,v| order_options << v } %> + + + + + + +
<%= c_('Configuration') %><%= _('Value') %>
<%= _('Default amount of stars marked on evaluations') %><%= c.select :default_rating, (config.minimum_ratings)..5 %>
<%= _('Can rate an organization only once') %><%= c.check_box :vote_once %>
<%= _('The comments are moderated') %><%= c.check_box :are_moderated %>
+ <%= _('Time in hours between evaluations from the same user.') %> + (?) + <%= c.text_field :cooldown, hours_options %> +
<%= _('Order ratings by') %><%= c.select :order, order_options %>
<%= _('Ratings per page') %> + <%= c.select :per_page, 5..20 %> +
+
+ <% button_bar do %> + <%= submit_button('save', c_('Save changes')) %> + <%= button :back, _('Back'), :controller => 'plugins' %> + <% end %> +
+ <% end %> +<% end %> diff --git a/plugins/organization_ratings/views/organization_ratings_plugin_profile/_new_rating_fields.html.erb b/plugins/organization_ratings/views/organization_ratings_plugin_profile/_new_rating_fields.html.erb new file mode 100644 index 0000000..c8ac200 --- /dev/null +++ b/plugins/organization_ratings/views/organization_ratings_plugin_profile/_new_rating_fields.html.erb @@ -0,0 +1,80 @@ +<% min_rate = env_organization_ratings_config.minimum_ratings %> +<% default_rating = env_organization_ratings_config.default_rating %> + +
+ <%= @plugins.dispatch(:organization_ratings_title).collect { |content| instance_exec(&content) }.join("") %> +
+ +
+ +
+
+ <%= profile_image(current_user.person, :portrait) %> +
+ +
+ <%= current_user.name %> +
+
+ + <% if @rating_available %> +
+
"rate" %>> +
+ <%= @plugins.dispatch(:organization_ratings_plugin_star_message).collect { |content| instance_exec(&content) }.join("") %> +
+ +
+ + <% (1..5).each do |rate_number| %> + <% if rate_number <= default_rating %> +
+ <% else %> +
+ <% end %> + <% end %> +
+ +
+ <%= _("Rated as") %> <%= _("stars") %> +
+
+ +
+ <%= form_for :comments do |c| %> +
+ <%= c.label :body, _('Comment (Optional):'), :class => "formlabel" %> + <%= c.text_area :body %> +
+ + <%= @plugins.dispatch(:organization_ratings_plugin_comments_extra_fields).collect { |content| instance_exec(&content) }.join("") %> + +
+ <%= submit_button(:save, _('Save'), :cancel => {controller: 'profile', action: 'index'}) %> +
+ + + + <% end %> +
+ + <% elsif env_organization_ratings_config.vote_once %> +
+ <%= _("Hi, %s! The administrators set that you can vote") % current_user.name %> + <%= _("only once") %> + <%= _("for this %s.") % profile.class.name.downcase %> + <%= render :partial => 'shared/rating_button', :locals => { :disabled => true } %> +
+ <% else %> +
+ <%= _("Hi, %s! The administrators set the minimum time of") % current_user.name %> + <%= _("%s hour(s)" % env_organization_ratings_config.cooldown) %> + <%= _("between each evaluation.") %> + + <%= render :partial => 'shared/rating_button', :locals => { :disabled => true } %> +
+ <% end %> + + +
+
\ No newline at end of file diff --git a/plugins/organization_ratings/views/organization_ratings_plugin_profile/new_rating.html.erb b/plugins/organization_ratings/views/organization_ratings_plugin_profile/new_rating.html.erb new file mode 100644 index 0000000..c65a902 --- /dev/null +++ b/plugins/organization_ratings/views/organization_ratings_plugin_profile/new_rating.html.erb @@ -0,0 +1,18 @@ +<% config = env_organization_ratings_config %> +<% if logged_in? %> + <%= render :partial => "new_rating_fields" %> +<% else %> +
+ <%= render :partial => "shared/make_report_block" %> +
+<% end %> + +
+ <% @users_ratings.each do |user_rate| %> + <%= render :partial => "shared/user_rating_container", :locals => {:user_rate => user_rate} %> + <% end %> +
+ +
+ <%= pagination_links @users_ratings, :param_name => 'npage' %> +
\ No newline at end of file diff --git a/plugins/organization_ratings/views/shared/_make_report_block.html.erb b/plugins/organization_ratings/views/shared/_make_report_block.html.erb new file mode 100644 index 0000000..d608af9 --- /dev/null +++ b/plugins/organization_ratings/views/shared/_make_report_block.html.erb @@ -0,0 +1,28 @@ +<% logged_in_image = profile_image(current_user.person, :portrait) if current_user %> +<% logged_out_image = image_tag('plugins/organization_ratings/public/images/user-not-logged.png') %> + +
+
+
+ <%= logged_in? ? logged_in_image : logged_out_image %> +
+ +
+ <%= logged_in? ? current_user.person.name : _('User not logged *') %> +
+
+ +
+
+ <%= _('Report your experiences.') %> +
+ + <%= render :partial => 'shared/rating_button', :locals => { :disabled => false } %> + + <% unless logged_in? %> +
+ <%= _('* You must be logged in to submit a report.') %> +
+ <% end %> +
+
\ No newline at end of file diff --git a/plugins/organization_ratings/views/shared/_rating_button.html.erb b/plugins/organization_ratings/views/shared/_rating_button.html.erb new file mode 100644 index 0000000..67cf037 --- /dev/null +++ b/plugins/organization_ratings/views/shared/_rating_button.html.erb @@ -0,0 +1,10 @@ +<% button_bar do %> + <% if logged_in? %> + <%= button(:new,_("Rate %s ") % profile.class.name, + {:controller => "organization_ratings_plugin_profile", + :action => "new_rating"}) %> + <% else %> + <%= button(:login,_("Log in") , {:controller => 'account', + :action => 'login'}) %> + <% end %> +<% end %> diff --git a/plugins/organization_ratings/views/shared/_user_rating_container.html.erb b/plugins/organization_ratings/views/shared/_user_rating_container.html.erb new file mode 100644 index 0000000..0123818 --- /dev/null +++ b/plugins/organization_ratings/views/shared/_user_rating_container.html.erb @@ -0,0 +1,33 @@ +
+
+
+ <%= profile_image(user_rate.person, :portrait) %> +
+ +
+ <%= user_rate.person.name %> +
+
+ +
+
+ <%= time_ago_in_words(user_rate.created_at) %> +
+ +
+ <% (1..5).each do |rate_number| %> + <% if rate_number <= user_rate.value %> +
+ <% else %> +
+ <% end %> + <% end %> +
+ +
+ <%= user_rate.comment.nil? ? _("No comment") : user_rate.comment.body %> +
+ + <%= @plugins.dispatch(:organization_ratings_plugin_extra_fields_show_data, user_rate).collect { |content| instance_exec(&content) }.join("") %> +
+
diff --git a/plugins/organization_ratings/views/tasks/_create_organization_rating_comment_accept_details.html.erb b/plugins/organization_ratings/views/tasks/_create_organization_rating_comment_accept_details.html.erb new file mode 100644 index 0000000..0632597 --- /dev/null +++ b/plugins/organization_ratings/views/tasks/_create_organization_rating_comment_accept_details.html.erb @@ -0,0 +1,4 @@ +
+ <%= _("Comment:")%> + <%= "\"#{task.body}\""%> +
diff --git a/plugins/statistics/lib/statistics_block.rb b/plugins/statistics/lib/statistics_block.rb index 65d5554..026d63b 100644 --- a/plugins/statistics/lib/statistics_block.rb +++ b/plugins/statistics/lib/statistics_block.rb @@ -128,11 +128,12 @@ class StatisticsBlock < Block end end + include Noosfero::Plugin::HotSpot def comments if owner.kind_of?(Environment) then - owner.profiles.joins(:articles).sum(:comments_count).to_i + owner.articles.sum(:comments_count).to_i + plugins.dispatch(:more_comments_count, owner).first.to_i elsif owner.kind_of?(Profile) then - owner.articles.sum(:comments_count) + owner.articles.sum(:comments_count) + plugins.dispatch(:more_comments_count, owner).first.to_i else 0 end -- libgit2 0.21.2