Commit 2fd621c3d7332efcdb65b6e7045269ada32ad58c
Committed by
Tallys Martins
1 parent
d841eedf
Exists in
master
and in
5 other branches
Add software statistic block
Signed-off-by: brenddongontijo <brenddongontijo@msn.com> Signed-off-by: Fabio Teixeira <fabio1079@gmail.com> Signed-off-by: Pedro de Lyra <pedrodelyra@gmail.com> Signed-off-by: Luciano Prestes Cavalcanti <lucianopcbr@gmail.com> Signed-off-by: Simiao Carvalho <simiaosimis@gmail.com> Signed-off-by: Victor Matias Navarro <victor.matias.navarro@gmail.com>
Showing
13 changed files
with
383 additions
and
12 deletions
Show diff stats
controllers/software_communities_plugin_profile_controller.rb
0 → 100644
| @@ -0,0 +1,49 @@ | @@ -0,0 +1,49 @@ | ||
| 1 | +class SoftwareCommunitiesPluginProfileController < ProfileController | ||
| 2 | + append_view_path File.join(File.dirname(__FILE__) + '/../views') | ||
| 3 | + | ||
| 4 | + before_filter :validate_download_params, only: [:download_file] | ||
| 5 | + | ||
| 6 | + ERROR_MESSAGES = { | ||
| 7 | + :not_found => _("Could not find the download file"), | ||
| 8 | + :invalid_params => _("Invalid download params") | ||
| 9 | + } | ||
| 10 | + | ||
| 11 | + def download_file | ||
| 12 | + download_block = DownloadBlock.find_by_id params[:block] | ||
| 13 | + index = params[:download_index].to_i | ||
| 14 | + | ||
| 15 | + if download_block and (index < download_block.downloads.size) | ||
| 16 | + download = Download.new(download_block.downloads[index]) | ||
| 17 | + | ||
| 18 | + download.total_downloads += 1 | ||
| 19 | + download_block.downloads[index] = download.to_hash | ||
| 20 | + download_block.save | ||
| 21 | + | ||
| 22 | + redirect_to download.link | ||
| 23 | + else | ||
| 24 | + session[:notice] = ERROR_MESSAGES[:not_found] | ||
| 25 | + render_not_found | ||
| 26 | + end | ||
| 27 | + end | ||
| 28 | + | ||
| 29 | + private | ||
| 30 | + | ||
| 31 | + def validate_download_params | ||
| 32 | + valid_block = (!params[:block].nil?) and (params[:block].to_i > 0) | ||
| 33 | + valid_index = params[:download_index].to_i >= 0 | ||
| 34 | + | ||
| 35 | + if !valid_block or !valid_index | ||
| 36 | + session[:notice] = ERROR_MESSAGES[:invalid_params] | ||
| 37 | + safe_redirect_back | ||
| 38 | + end | ||
| 39 | + end | ||
| 40 | + | ||
| 41 | + def safe_redirect_back | ||
| 42 | + begin | ||
| 43 | + redirect_to :back | ||
| 44 | + rescue ActionController::RedirectBackError | ||
| 45 | + # There is no :back if it is a copied url | ||
| 46 | + render_not_found | ||
| 47 | + end | ||
| 48 | + end | ||
| 49 | +end |
| @@ -0,0 +1,51 @@ | @@ -0,0 +1,51 @@ | ||
| 1 | +#FIX ME: Turn this into a proper model(next release) | ||
| 2 | +class Download | ||
| 3 | + def initialize data | ||
| 4 | + @name = data[:name] | ||
| 5 | + @link = data[:link] | ||
| 6 | + @software_description = data[:software_description] | ||
| 7 | + @minimum_requirements = data[:minimum_requirements] | ||
| 8 | + @size = data[:size] | ||
| 9 | + | ||
| 10 | + @total_downloads = if data[:total_downloads] | ||
| 11 | + data[:total_downloads] | ||
| 12 | + else | ||
| 13 | + 0 | ||
| 14 | + end | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | + def self.validate_download_list download_list | ||
| 18 | + download_list.select! do |download| | ||
| 19 | + not download[:name].blank? | ||
| 20 | + end | ||
| 21 | + | ||
| 22 | + download_list.map do |download| | ||
| 23 | + Download.new(download).to_hash | ||
| 24 | + end | ||
| 25 | + end | ||
| 26 | + | ||
| 27 | + def to_hash | ||
| 28 | + { | ||
| 29 | + :name => @name, | ||
| 30 | + :link => @link, | ||
| 31 | + :software_description => @software_description, | ||
| 32 | + :minimum_requirements => @minimum_requirements, | ||
| 33 | + :size => @size, | ||
| 34 | + :total_downloads => @total_downloads | ||
| 35 | + } | ||
| 36 | + end | ||
| 37 | + | ||
| 38 | + def total_downloads= value | ||
| 39 | + if value.is_a? Integer | ||
| 40 | + @total_downloads = value | ||
| 41 | + end | ||
| 42 | + end | ||
| 43 | + | ||
| 44 | + def total_downloads | ||
| 45 | + @total_downloads | ||
| 46 | + end | ||
| 47 | + | ||
| 48 | + def link | ||
| 49 | + @link | ||
| 50 | + end | ||
| 51 | +end |
lib/download_block.rb
| @@ -8,11 +8,7 @@ class DownloadBlock < Block | @@ -8,11 +8,7 @@ class DownloadBlock < Block | ||
| 8 | validate :download_values | 8 | validate :download_values |
| 9 | 9 | ||
| 10 | def download_values | 10 | def download_values |
| 11 | - self.downloads.each do |download| | ||
| 12 | - if download[:name] == "" | ||
| 13 | - downloads.delete(download) | ||
| 14 | - end | ||
| 15 | - end | 11 | + self.downloads = Download.validate_download_list(self.downloads) |
| 16 | end | 12 | end |
| 17 | 13 | ||
| 18 | def self.description | 14 | def self.description |
lib/ext/community.rb
| @@ -12,6 +12,8 @@ class Community | @@ -12,6 +12,8 @@ class Community | ||
| 12 | 12 | ||
| 13 | has_one :software_info, :dependent=>:destroy | 13 | has_one :software_info, :dependent=>:destroy |
| 14 | 14 | ||
| 15 | + settings_items :hits, :type => :integer, :default => 0 | ||
| 16 | + | ||
| 15 | def self.create_after_moderation(requestor, attributes = {}) | 17 | def self.create_after_moderation(requestor, attributes = {}) |
| 16 | community = Community.new(attributes) | 18 | community = Community.new(attributes) |
| 17 | 19 | ||
| @@ -55,4 +57,10 @@ class Community | @@ -55,4 +57,10 @@ class Community | ||
| 55 | def remove_of_community_search_software? | 57 | def remove_of_community_search_software? |
| 56 | return software? | 58 | return software? |
| 57 | end | 59 | end |
| 60 | + | ||
| 61 | + def hit | ||
| 62 | + self.hits += 1 | ||
| 63 | + self.save! | ||
| 64 | + end | ||
| 65 | + | ||
| 58 | end | 66 | end |
lib/ext/profile_controller.rb
| @@ -2,6 +2,8 @@ require_dependency 'profile_controller' | @@ -2,6 +2,8 @@ require_dependency 'profile_controller' | ||
| 2 | 2 | ||
| 3 | class ProfileController | 3 | class ProfileController |
| 4 | 4 | ||
| 5 | + before_filter :hit_view_page | ||
| 6 | + | ||
| 5 | def communities | 7 | def communities |
| 6 | type = [] | 8 | type = [] |
| 7 | params[:type].downcase! unless params[:type].nil? | 9 | params[:type].downcase! unless params[:type].nil? |
| @@ -37,4 +39,33 @@ class ProfileController | @@ -37,4 +39,33 @@ class ProfileController | ||
| 37 | end | 39 | end |
| 38 | end | 40 | end |
| 39 | 41 | ||
| 42 | + def user_is_a_bot? | ||
| 43 | + user_agent= request.env["HTTP_USER_AGENT"] | ||
| 44 | + user_agent.blank? || | ||
| 45 | + user_agent.match(/bot/) || | ||
| 46 | + user_agent.match(/spider/) || | ||
| 47 | + user_agent.match(/crawler/) || | ||
| 48 | + user_agent.match(/\(.*https?:\/\/.*\)/) | ||
| 49 | + end | ||
| 50 | + | ||
| 51 | + def already_visited?(element) | ||
| 52 | + user_id = if user.nil? then -1 else current_user.id end | ||
| 53 | + user_id = "#{user_id}_#{element.id}_#{element.class}" | ||
| 54 | + | ||
| 55 | + if cookies.signed[:visited] == user_id | ||
| 56 | + return true | ||
| 57 | + else | ||
| 58 | + cookies.permanent.signed[:visited] = user_id | ||
| 59 | + return false | ||
| 60 | + end | ||
| 61 | + end | ||
| 62 | + | ||
| 63 | + def hit_view_page | ||
| 64 | + if profile | ||
| 65 | + community = profile | ||
| 66 | + community.hit unless user_is_a_bot? || | ||
| 67 | + already_visited?(community) || | ||
| 68 | + community.class != Community | ||
| 69 | + end | ||
| 70 | + end | ||
| 40 | end | 71 | end |
lib/software_communities_plugin.rb
| @@ -42,7 +42,8 @@ class SoftwareCommunitiesPlugin < Noosfero::Plugin | @@ -42,7 +42,8 @@ class SoftwareCommunitiesPlugin < Noosfero::Plugin | ||
| 42 | SearchCatalogBlock => { :type => [Environment] }, | 42 | SearchCatalogBlock => { :type => [Environment] }, |
| 43 | SoftwareHighlightsBlock => { :type => [Environment] }, | 43 | SoftwareHighlightsBlock => { :type => [Environment] }, |
| 44 | SoftwareTabDataBlock => {:type => [Community], :position => 1}, | 44 | SoftwareTabDataBlock => {:type => [Community], :position => 1}, |
| 45 | - WikiBlock => {:type => [Community]} | 45 | + WikiBlock => {:type => [Community]}, |
| 46 | + StatisticBlock => { :type => [Community] } | ||
| 46 | } | 47 | } |
| 47 | end | 48 | end |
| 48 | 49 |
| @@ -0,0 +1,52 @@ | @@ -0,0 +1,52 @@ | ||
| 1 | +class StatisticBlock < Block | ||
| 2 | + | ||
| 3 | + settings_items :benefited_people, :type => :integer, :default => 0 | ||
| 4 | + settings_items :saved_resources, :type => :float, :default => 0.0 | ||
| 5 | + | ||
| 6 | + attr_accessible :benefited_people, :saved_resources | ||
| 7 | + | ||
| 8 | + def self.description | ||
| 9 | + _('Software Statistics') | ||
| 10 | + end | ||
| 11 | + | ||
| 12 | + def help | ||
| 13 | + _('This block displays software statistics.') | ||
| 14 | + end | ||
| 15 | + | ||
| 16 | + def content(args={}) | ||
| 17 | + download_blocks = get_profile_download_blocks(self.owner) | ||
| 18 | + downloads = download_blocks.map do |download_block| | ||
| 19 | + get_downloads_from_block(download_block) | ||
| 20 | + end | ||
| 21 | + | ||
| 22 | + block = self | ||
| 23 | + | ||
| 24 | + lambda do |object| | ||
| 25 | + render( | ||
| 26 | + :file => 'blocks/software_statistics', | ||
| 27 | + :locals => { | ||
| 28 | + :block => block, | ||
| 29 | + :total_downloads => downloads.sum | ||
| 30 | + } | ||
| 31 | + ) | ||
| 32 | + end | ||
| 33 | + end | ||
| 34 | + | ||
| 35 | + def cacheable? | ||
| 36 | + false | ||
| 37 | + end | ||
| 38 | + | ||
| 39 | + private | ||
| 40 | + | ||
| 41 | + def get_profile_download_blocks profile | ||
| 42 | + DownloadBlock.joins(:box).where("boxes.owner_id = ?", profile.id) | ||
| 43 | + end | ||
| 44 | + | ||
| 45 | + def get_downloads_from_block download_block | ||
| 46 | + downloads = download_block.downloads.map do |download| | ||
| 47 | + download[:total_downloads] unless download[:total_downloads].nil? | ||
| 48 | + end | ||
| 49 | + downloads.select! {|value| not value.nil? } | ||
| 50 | + downloads.sum | ||
| 51 | + end | ||
| 52 | +end |
| @@ -0,0 +1,40 @@ | @@ -0,0 +1,40 @@ | ||
| 1 | +require "test_helper" | ||
| 2 | +require 'profile_controller' | ||
| 3 | + | ||
| 4 | +# Re-raise errors caught by the controller. | ||
| 5 | +class ProfileController; def rescue_action(e) raise e end; end | ||
| 6 | + | ||
| 7 | +class ProfileControllerTest < ActionController::TestCase | ||
| 8 | + def setup | ||
| 9 | + Environment.default.enable('products_for_enterprises') | ||
| 10 | + @profile = create_user('testuser').person | ||
| 11 | + end | ||
| 12 | + attr_reader :profile | ||
| 13 | + | ||
| 14 | + should 'not count a visit twice for the same user' do | ||
| 15 | + profile = create_user('someone').person | ||
| 16 | + login_as(@profile.identifier) | ||
| 17 | + community = fast_create('Community') | ||
| 18 | + | ||
| 19 | + get :index, :profile => community.identifier | ||
| 20 | + community.reload | ||
| 21 | + assert_equal 1, community.hits | ||
| 22 | + | ||
| 23 | + get :index, :profile => community.identifier | ||
| 24 | + community.reload | ||
| 25 | + assert_equal 1, community.hits | ||
| 26 | + end | ||
| 27 | + | ||
| 28 | + should 'not count a visit twice for unlogged users' do | ||
| 29 | + profile = create_user('someone').person | ||
| 30 | + community = fast_create('Community') | ||
| 31 | + get :index, :profile => community.identifier | ||
| 32 | + community.reload | ||
| 33 | + assert_equal 1, community.hits | ||
| 34 | + | ||
| 35 | + get :index, :profile => community.identifier | ||
| 36 | + community.reload | ||
| 37 | + assert_equal 1, community.hits | ||
| 38 | + end | ||
| 39 | +end | ||
| 40 | + |
test/functional/software_communities_plugin_profile_controller_test.rb
0 → 100644
| @@ -0,0 +1,67 @@ | @@ -0,0 +1,67 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | ||
| 2 | +require File.dirname(__FILE__) + '/../helpers/software_test_helper' | ||
| 3 | +require File.dirname(__FILE__) + '/../../controllers/software_communities_plugin_profile_controller' | ||
| 4 | + | ||
| 5 | +class SoftwareCommunitiesPluginProfileController; def rescue_action(e) raise e end; end | ||
| 6 | + | ||
| 7 | +class SoftwareCommunitiesPluginProfileControllerTest < ActionController::TestCase | ||
| 8 | + include SoftwareTestHelper | ||
| 9 | + | ||
| 10 | + def setup | ||
| 11 | + @controller = SoftwareCommunitiesPluginProfileController.new | ||
| 12 | + @request = ActionController::TestRequest.new | ||
| 13 | + @response = ActionController::TestResponse.new | ||
| 14 | + | ||
| 15 | + @environment = Environment.default | ||
| 16 | + @environment.enable_plugin('SoftwareCommunitiesPlugin') | ||
| 17 | + @environment.save! | ||
| 18 | + | ||
| 19 | + LicenseInfo.create( | ||
| 20 | + :version=>"CC-GPL-V2", | ||
| 21 | + :link=>"http://creativecommons.org/licenses/GPL/2.0/legalcode.pt" | ||
| 22 | + ) | ||
| 23 | + @download_data = { | ||
| 24 | + :name=>"Google", | ||
| 25 | + :link=>"http://google.com", | ||
| 26 | + :software_description=>"all", | ||
| 27 | + :minimum_requirements=>"none", | ||
| 28 | + :size=>"?", | ||
| 29 | + :total_downloads=>0 | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @software = create_software(software_fields) | ||
| 33 | + @software.save! | ||
| 34 | + | ||
| 35 | + download_block = DownloadBlock.new | ||
| 36 | + download_block.downloads = Download.validate_download_list([@download_data]) | ||
| 37 | + download_block.save! | ||
| 38 | + | ||
| 39 | + @software.community.blocks << download_block | ||
| 40 | + @software.community.save! | ||
| 41 | + end | ||
| 42 | + | ||
| 43 | + should 'redirect to download link with correct params' do | ||
| 44 | + download_block = DownloadBlock.last | ||
| 45 | + get :download_file, :profile=>@software.community.identifier, | ||
| 46 | + :block => download_block.id, :download_index => 0 | ||
| 47 | + | ||
| 48 | + assert_equal nil, session[:notice] | ||
| 49 | + assert_redirected_to download_block.downloads[0][:link] | ||
| 50 | + end | ||
| 51 | + | ||
| 52 | + should "notice when the download was not found" do | ||
| 53 | + download_block = DownloadBlock.last | ||
| 54 | + get :download_file, :profile=>@software.community.identifier, | ||
| 55 | + :block => 123, :download_index => 0 | ||
| 56 | + | ||
| 57 | + assert_equal "Could not find the download file", session[:notice] | ||
| 58 | + end | ||
| 59 | + | ||
| 60 | + should "notice when given invalid download params" do | ||
| 61 | + download_block = DownloadBlock.last | ||
| 62 | + get :download_file, :profile=>@software.community.identifier, | ||
| 63 | + :block => download_block.id, :download_index => -5 | ||
| 64 | + | ||
| 65 | + assert_equal "Invalid download params", session[:notice] | ||
| 66 | + end | ||
| 67 | +end |
| @@ -0,0 +1,53 @@ | @@ -0,0 +1,53 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | ||
| 2 | +require File.dirname(__FILE__) + '/../helpers/plugin_test_helper' | ||
| 3 | + | ||
| 4 | +class DownloadTest < ActiveSupport::TestCase | ||
| 5 | + include PluginTestHelper | ||
| 6 | + | ||
| 7 | + def setup | ||
| 8 | + @downloads_sample_data = [ | ||
| 9 | + { | ||
| 10 | + :name=>"Sample data A", | ||
| 11 | + :link=>"http://some.url.com", | ||
| 12 | + :software_description=>"all", | ||
| 13 | + :minimum_requirements=>"none", | ||
| 14 | + :size=>"10 mb", | ||
| 15 | + :total_downloads=>0 | ||
| 16 | + }, | ||
| 17 | + { | ||
| 18 | + :name=>"Sample data B", | ||
| 19 | + :link=>"http://other.url", | ||
| 20 | + :software_description=>"Linux", | ||
| 21 | + :minimum_requirements=>"500 mb Ram", | ||
| 22 | + :size=>"3 GB", | ||
| 23 | + :total_downloads=>123 | ||
| 24 | + } | ||
| 25 | + ] | ||
| 26 | + end | ||
| 27 | + | ||
| 28 | + should "Set as 0(zero) total_downloads if it is not given" do | ||
| 29 | + without_total_downloads = Download.new({}) | ||
| 30 | + with_total_downloads = Download.new(@downloads_sample_data.last) | ||
| 31 | + | ||
| 32 | + assert_equal 0, without_total_downloads.total_downloads | ||
| 33 | + assert_equal @downloads_sample_data.last[:total_downloads], with_total_downloads.total_downloads | ||
| 34 | + end | ||
| 35 | + | ||
| 36 | + should "Remove downloads without a name" do | ||
| 37 | + @downloads_sample_data[1] = @downloads_sample_data[1].slice! :name | ||
| 38 | + downloads = Download.validate_download_list @downloads_sample_data | ||
| 39 | + | ||
| 40 | + assert_equal 1, downloads.size | ||
| 41 | + assert_equal @downloads_sample_data[0][:name], downloads[0][:name] | ||
| 42 | + end | ||
| 43 | + | ||
| 44 | + should "Only set total_downloads if the value is integer" do | ||
| 45 | + download = Download.new(@downloads_sample_data.last) | ||
| 46 | + | ||
| 47 | + download.total_downloads = "456" | ||
| 48 | + assert_not_equal 456, download.total_downloads | ||
| 49 | + | ||
| 50 | + download.total_downloads = 456 | ||
| 51 | + assert_equal 456, download.total_downloads | ||
| 52 | + end | ||
| 53 | +end |
views/blocks/download.html.erb
| @@ -4,12 +4,19 @@ | @@ -4,12 +4,19 @@ | ||
| 4 | <h3 class="block-title"> <%= _("Download #{block.owner.software_info.community.name}") %> </h3> | 4 | <h3 class="block-title"> <%= _("Download #{block.owner.software_info.community.name}") %> </h3> |
| 5 | <ul class="download-list"> | 5 | <ul class="download-list"> |
| 6 | <% block.downloads.each_with_index do |download, index| %> | 6 | <% block.downloads.each_with_index do |download, index| %> |
| 7 | - <li id="download-item-<%=(index+1)%>"> | ||
| 8 | - <div class="download-button"> | ||
| 9 | - <%= link_to download[:link], title: _("Download the software") do %> | ||
| 10 | - <span class="download-image"></span> | ||
| 11 | - <span class="download-size"><%= download[:size] %></span> | ||
| 12 | - <% end %> | 7 | + <div id="download-info-<%=(index+1)%>"> |
| 8 | + <div id="version01"> | ||
| 9 | + <%= link_to :controller => 'software_communities_plugin_profile', :action=> 'download_file', :block=>block.id, :download_index=> index , title: _("Download the software") do %> | ||
| 10 | + <span id="image-download"></span> | ||
| 11 | + <span id="size-download"><%= download[:size] %></span> | ||
| 12 | + <% end %> | ||
| 13 | + </div> | ||
| 14 | + | ||
| 15 | + <div id="info-software-download"> | ||
| 16 | + <span class="download-name"><%= _("#{download[:name]}") %></span> | ||
| 17 | + <span class="download-system"><%= _("Platform:#{download[:software_description]}") %> </span> | ||
| 18 | + <span class="req_min_spb"><%= link_to _("Minimum Requirements"), download[:minimum_requirements] %></span> | ||
| 19 | + </div> | ||
| 13 | </div> | 20 | </div> |
| 14 | <div class="download-info"> | 21 | <div class="download-info"> |
| 15 | <span class="download-name"><%= _("#{download[:name]}") %></span> | 22 | <span class="download-name"><%= _("#{download[:name]}") %></span> |
| @@ -0,0 +1,11 @@ | @@ -0,0 +1,11 @@ | ||
| 1 | +<div class="software-metrics-block"> | ||
| 2 | + <ul class="metrics-list"> | ||
| 3 | + <li><span class="arrow-globe-icon"></span><a href="#"><%= pluralize(profile.hits, 'visita', 'visitas') %></a></li> | ||
| 4 | + <li><span class="downloads-icon"></span><a href="#"><%= pluralize(total_downloads, 'download', 'downloads') %></a></li> | ||
| 5 | + <li><span class="face-icon"></span><a href="#"><%= block.benefited_people.to_s + _(' benefited people*') %></a></li> | ||
| 6 | + <li><span class="pig-safe-icon"></span><a href="#"><strong class="saved-money"><%= number_to_currency(block.saved_resources, unit: 'R$ ', separator: ',', delimiter: '.') %></strong> <%= _(' saved resources*') %></a></li> | ||
| 7 | + </ul> | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + <div class="admin-estimation">* <%= _("Data estimated by the software administrator.") %></div> | ||
| 11 | +</div> |