diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 7fc81e03e..a688ae0b3 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -42,6 +42,7 @@
//= require suggest
//= require forms
//= require valuation_spending_proposal_form
+//= require embed_video
var initialize_modules = function() {
App.Comments.initialize();
@@ -59,6 +60,7 @@ var initialize_modules = function() {
App.Suggest.initialize();
App.Forms.initialize();
App.ValuationSpendingProposalForm.initialize();
+ App.EmbedVideo.initialize();
};
$(function(){
diff --git a/app/assets/javascripts/embed_video.js.coffee b/app/assets/javascripts/embed_video.js.coffee
new file mode 100644
index 000000000..85c18ac8e
--- /dev/null
+++ b/app/assets/javascripts/embed_video.js.coffee
@@ -0,0 +1,7 @@
+App.EmbedVideo =
+
+ initialize: ->
+ $('#js-embedded-video').each ->
+ code = $(this).data("video-code")
+ $('#js-embedded-video').html(code)
+
\ No newline at end of file
diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss
index 6a12477eb..eb2945deb 100644
--- a/app/assets/stylesheets/participation.scss
+++ b/app/assets/stylesheets/participation.scss
@@ -274,7 +274,7 @@
.debate-new, .debate-edit,
.proposal-new, .proposal-edit,
-.spending-proposal-new, .spending-proposal-edit {
+.spending-proposal-new, .spending-proposal-edit {
.back {
@include back;
@@ -546,7 +546,7 @@
}
}
-.debate, .proposal {
+.debate, .proposal, .investment-project {
margin-bottom: 0;
margin-top: 0;
@@ -565,7 +565,7 @@
padding-bottom: rem-calc(12);
}
- .label-debate, .label-proposal {
+ .label-debate, .label-proposal, .label-investment-project {
background: none;
clear: both;
display: block;
@@ -587,6 +587,10 @@
color: $proposals;
}
+ .label-investment-project {
+ color: $budget;
+ }
+
h3 {
font-weight: bold;
margin: 0;
@@ -596,7 +600,7 @@
}
}
- .debate-content, .proposal-content {
+ .debate-content, .proposal-content, .investment-project-content {
margin: 0;
min-height: rem-calc(180);
position: relative;
@@ -606,7 +610,7 @@
}
}
- .icon-debates, .icon-proposals {
+ .icon-debates, .icon-proposals, .icon-budget {
font-size: rem-calc(18);
line-height: $line-height;
position: absolute;
@@ -623,7 +627,13 @@
left: rem-calc(72);
}
- .debate-info, .proposal-info {
+ .icon-budget {
+ color: $budget;
+ font-size: $small-font-size;
+ left: rem-calc(122);
+ }
+
+ .debate-info, .proposal-info, .investment-project-info {
color: $text-medium;
font-size: $small-font-size;
margin: rem-calc(6) 0 0;
@@ -638,7 +648,7 @@
}
}
- .debate-description, .proposal-description {
+ .debate-description, .proposal-description, .investment-project-description {
color: $text;
font-size: rem-calc(13);
height: rem-calc(72);
@@ -799,6 +809,57 @@
}
}
+.investment-project {
+
+ .supports {
+ @include supports;
+ background: none;
+ border: 0;
+ border-left: 1px solid $border;
+ margin: 0 rem-calc(-12);
+ min-height: rem-calc(180);
+ padding-top: $line-height*2;
+
+ &:after {
+ content: none;
+ }
+
+ .button-support {
+ background: $budget;
+ color: white;
+
+ &:hover {
+ background: $proposals-border;
+ color: white;
+ cursor: pointer;
+ }
+
+ &:active {
+ opacity: .75;
+ }
+ }
+
+ .total-supports {
+ color: $budget;
+ font-size: $base-font-size;
+ font-weight: bold;
+ }
+
+ .not-logged,
+ .organizations-votes,
+ .anonymous-votes {
+ background: rgba(69,67,114,.96);
+ color: white;
+ padding: rem-calc(12);
+ }
+
+ .anonymous-votes p, .anonymous-votes a,
+ .organizations-votes p {
+ color: white;
+ }
+ }
+}
+
.proposals-summary {
.panel {
diff --git a/app/controllers/admin/spending_proposals_controller.rb b/app/controllers/admin/spending_proposals_controller.rb
index 25707e66e..61f9aa78d 100644
--- a/app/controllers/admin/spending_proposals_controller.rb
+++ b/app/controllers/admin/spending_proposals_controller.rb
@@ -7,7 +7,7 @@ class Admin::SpendingProposalsController < Admin::BaseController
load_and_authorize_resource
def index
- @spending_proposals = SpendingProposal.search(params, @current_filter).order(created_at: :desc).page(params[:page])
+ @spending_proposals = SpendingProposal.scoped_filter(params, @current_filter).order(created_at: :desc).page(params[:page])
end
def show
diff --git a/app/controllers/concerns/commentable_actions.rb b/app/controllers/concerns/commentable_actions.rb
index 855ad1de5..01066c8ad 100644
--- a/app/controllers/concerns/commentable_actions.rb
+++ b/app/controllers/concerns/commentable_actions.rb
@@ -65,8 +65,7 @@ module CommentableActions
end
end
-
- def map
+ def map
@resource = resource_model.new
@tag_cloud = tag_cloud
end
diff --git a/app/controllers/spending_proposals_controller.rb b/app/controllers/spending_proposals_controller.rb
index 20add5c24..22557f9cd 100644
--- a/app/controllers/spending_proposals_controller.rb
+++ b/app/controllers/spending_proposals_controller.rb
@@ -12,6 +12,8 @@ class SpendingProposalsController < ApplicationController
respond_to :html, :js
def index
+ @spending_proposals = @search_terms.present? ? SpendingProposal.search(@search_terms) : SpendingProposal.all
+ @spending_proposals = @spending_proposals.page(params[:page]).for_render
end
def new
diff --git a/app/controllers/valuation/spending_proposals_controller.rb b/app/controllers/valuation/spending_proposals_controller.rb
index 364671d09..c5f822b40 100644
--- a/app/controllers/valuation/spending_proposals_controller.rb
+++ b/app/controllers/valuation/spending_proposals_controller.rb
@@ -10,7 +10,7 @@ class Valuation::SpendingProposalsController < Valuation::BaseController
def index
if current_user.valuator?
- @spending_proposals = SpendingProposal.search(params_for_current_valuator, @current_filter).order(created_at: :desc).page(params[:page])
+ @spending_proposals = SpendingProposal.scoped_filter(params_for_current_valuator, @current_filter).order(created_at: :desc).page(params[:page])
else
@spending_proposals = SpendingProposal.none.page(params[:page])
end
diff --git a/app/helpers/embed_videos_helper.rb b/app/helpers/embed_videos_helper.rb
new file mode 100644
index 000000000..b53aeef21
--- /dev/null
+++ b/app/helpers/embed_videos_helper.rb
@@ -0,0 +1,30 @@
+module EmbedVideosHelper
+
+ def embedded_video_code
+ link = @proposal.video_url
+ if link.match(/vimeo.*/)
+ server = "Vimeo"
+ elsif link.match(/youtu*.*/)
+ server = "YouTube"
+ end
+
+ if server == "Vimeo"
+ regExp = /vimeo.*(staffpicks\/|channels\/|videos\/|video\/|\/)([^#\&\?]*).*/
+ src = "https://player.vimeo.com/video/"
+ elsif server == "YouTube"
+ regExp = /youtu.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
+ src = "https://www.youtube.com/embed/"
+ end
+
+ if regExp
+ match = link.match(regExp)
+ end
+
+ if match and match[2]
+ ''
+ else
+ ''
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/app/models/spending_proposal.rb b/app/models/spending_proposal.rb
index 561e6ab52..534ce9533 100644
--- a/app/models/spending_proposal.rb
+++ b/app/models/spending_proposal.rb
@@ -25,12 +25,15 @@ class SpendingProposal < ActiveRecord::Base
scope :managed, -> { valuation_open.where(valuation_assignments_count: 0).where("administrator_id IS NOT ?", nil) }
scope :valuating, -> { valuation_open.where("valuation_assignments_count > 0 AND valuation_finished = ?", false) }
scope :valuation_finished, -> { where(valuation_finished: true) }
+ scope :feasible, -> { where(feasible: true) }
+ scope :unfeasible, -> { where(feasible: false) }
+ scope :not_unfeasible, -> { where("feasible IS ? OR feasible = ?", nil, true) }
scope :by_admin, -> (admin) { where(administrator_id: admin.presence) }
scope :by_tag, -> (tag_name) { tagged_with(tag_name) }
scope :by_valuator, -> (valuator) { where("valuation_assignments.valuator_id = ?", valuator.presence).joins(:valuation_assignments) }
- scope :for_render, -> { includes(:geozone, administrator: :user, valuators: :user) }
+ scope :for_render, -> { includes(:geozone) }
def description
super.try :html_safe
@@ -40,14 +43,14 @@ class SpendingProposal < ActiveRecord::Base
params.select{|x,_| %w{geozone_id administrator_id tag_name valuator_id}.include? x.to_s }
end
- def self.search(params, current_filter)
+ def self.scoped_filter(params, current_filter)
results = self
results = results.by_geozone(params[:geozone_id]) if params[:geozone_id].present?
results = results.by_admin(params[:administrator_id]) if params[:administrator_id].present?
results = results.by_tag(params[:tag_name]) if params[:tag_name].present?
results = results.by_valuator(params[:valuator_id]) if params[:valuator_id].present?
results = results.send(current_filter) if current_filter.present?
- results.for_render
+ results.includes(:geozone, administrator: :user, valuators: :user)
end
def self.by_geozone(geozone)
diff --git a/app/views/proposals/_comments.html.erb b/app/views/proposals/_comments.html.erb
index 4a0bd2aea..31f1cb8ba 100644
--- a/app/views/proposals/_comments.html.erb
+++ b/app/views/proposals/_comments.html.erb
@@ -2,7 +2,6 @@
<%= t("proposals.show.comments_title") %> (<%= @proposal.comments_count %>) diff --git a/app/views/proposals/show.html.erb b/app/views/proposals/show.html.erb index 5231439be..76a344b87 100644 --- a/app/views/proposals/show.html.erb +++ b/app/views/proposals/show.html.erb @@ -62,7 +62,8 @@
<%= text_with_links @proposal.video_url %>
- <% end %>
+
+ <% end %>
+ <% Geozone.names.each do |geozone| %>
+ <%= link_to geozone, spending_proposals_path(search: geozone) %>
+ <% end %>
+
+
+
+
+
+
+
+
+
+ <% cache [locale_and_user_status(spending_proposal), 'index', spending_proposal, spending_proposal.author] do %>
+ <%= t("spending_proposals.spending_proposal.spending_proposal") %>
+
+
+
+ <% end %>
+
+
+
+
+
+
+
+
diff --git a/app/views/spending_proposals/index.html.erb b/app/views/spending_proposals/index.html.erb
index 00a1b6442..499948691 100644
--- a/app/views/spending_proposals/index.html.erb
+++ b/app/views/spending_proposals/index.html.erb
@@ -1,16 +1,35 @@
<% provide :title do %><%= t('spending_proposals.index.title') %><% end %>
-
-
-
-
+
+
- <% if can? :create, SpendingProposal %>
- <%= link_to t('spending_proposals.index.create_link'), new_spending_proposal_path, class: 'button' %>
- <% else %>
-
+ <% if @search_terms %>
+
+
+
+ <%= link_to t("spending_proposals.index.start_spending_proposal"), new_spending_proposal_path, class: 'button expanded' %>
+
+ <%= render partial: 'spending_proposals/spending_proposal', collection: @spending_proposals %>
+ <%= paginate @spending_proposals %>
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml
index c5e88d8dd..40086aab9 100644
--- a/config/i18n-tasks.yml
+++ b/config/i18n-tasks.yml
@@ -131,6 +131,7 @@ ignore_unused:
- 'proposals.index.select_order'
- 'proposals.index.orders.*'
- 'proposals.index.search_form.*'
+ - 'spending_proposals.index.search_form.*'
- 'notifications.index.comments_on*'
- 'notifications.index.replies_to*'
- 'helpers.page_entries_info.*' # kaminari
diff --git a/config/locales/en.yml b/config/locales/en.yml
index c320ec9f2..ec1158d52 100755
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -412,11 +412,18 @@ en:
new: Create
title: Spending proposal title
index:
- create_link: Create spending proposal
- text_html: Here you can send spending proposals to be considered in the frame of the annual participatory budgeting.
title: Participatory budgeting
- verified_only: Only verified users can create spending proposals, %{verify_account}.
- verify_account: verify your account
+ search_form:
+ button: Search
+ placeholder: Investment projects...
+ title: Search
+ search_results:
+ one: " containing the term '%{search_term}'"
+ other: " containing the term '%{search_term}'"
+ sidebar:
+ geozones: Scope of operation
+ feasibility: Feasibility
+ start_spending_proposal: Create an investment project
new:
more_info: How do participatory budgeting works?
recommendation_one: It's mandatory that the proposal makes reference to a budgetable action.
@@ -425,6 +432,10 @@ en:
recommendations_title: How to create a spending proposal
start_new: Create spending proposal
back_link: Back
+ show:
+ author_deleted: User deleted
+ spending_proposal:
+ spending_proposal: Investment project
wrong_price_format: Only integer numbers
spending_proposal:
already_supported: You have already supported this. Share it!
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 5a6149f2a..101e7f026 100755
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -412,11 +412,18 @@ es:
new: Crear
title: Título de la propuesta de gasto
index:
- create_link: Enviar propuesta de gasto
- text_html: Desde esta sección podrás sugerir propuestas de gasto que irán asociadas a las partidas de presupuestos ciudadanos.
<%= @proposal.question %>
diff --git a/app/views/spending_proposals/_sidebar.html.erb b/app/views/spending_proposals/_sidebar.html.erb new file mode 100644 index 000000000..7ab1ab525 --- /dev/null +++ b/app/views/spending_proposals/_sidebar.html.erb @@ -0,0 +1,11 @@ + +<%= t("spending_proposals.index.sidebar.geozones") %>
++
<%= t("spending_proposals.index.sidebar.feasibility") %>
+\ No newline at end of file diff --git a/app/views/spending_proposals/_spending_proposal.html.erb b/app/views/spending_proposals/_spending_proposal.html.erb new file mode 100644 index 000000000..4437fba0b --- /dev/null +++ b/app/views/spending_proposals/_spending_proposal.html.erb @@ -0,0 +1,53 @@ +
<%= link_to spending_proposal.title, spending_proposal_path(spending_proposal) %>
++ + <%= l spending_proposal.created_at.to_date %> + + <% if spending_proposal.author.hidden? || spending_proposal.author.erased? %> + • + + <%= t("spending_proposals.show.author_deleted") %> + + <% else %> + • + + <%= spending_proposal.author.name %> + + <% if spending_proposal.author.official? %> + • + + <%= spending_proposal.author.official_position %> + + <% end %> + <% end %> + + <% if spending_proposal.author.verified_organization? %> + • + + <%= t("shared.collective") %> + + <% end %> +
+<%= link_to spending_proposal.description, spending_proposal_path(spending_proposal) %>
+ +<%= t('spending_proposals.index.title') %>
+<% content_for :header_addon do %> + <%= render "shared/search_form", + search_path: spending_proposals_path(page: 1), + i18n_namespace: "spending_proposals.index.search_form" %> +<% end %> -<%= t('spending_proposals.index.text_html') %>
+<%= t('spending_proposals.index.verified_only', verify_account: link_to(t('spending_proposals.index.verify_account'), verification_path)).html_safe %>
- <% end %> ++ <%= page_entries_info @spending_proposals %> + <%= t("spending_proposals.index.search_results", count: @spending_proposals.size, search_term: @search_terms) %> +
+ <% end %> +El requisito principal es que sean propuestas presupuestables. title: Presupuestos participativos - verified_only: Sólo los usuarios verificados pueden crear propuestas de gasto, %{verify_account}. - verify_account: verifica tu cuenta + search_form: + button: Buscar + placeholder: Propuestas de inversión... + title: Buscar + search_results: + one: " que contiene '%{search_term}'" + other: " que contienen '%{search_term}'" + sidebar: + geozones: Ámbitos de actuación + feasibility: Viabilidad + start_spending_proposal: Crea una propuesta de inversión new: more_info: "¿Cómo funcionan los presupuestos participativos?" recommendation_one: Es fundamental que haga referencia a una actuación presupuestable. @@ -425,6 +432,10 @@ es: recommendations_title: Cómo crear una propuesta de gasto start_new: Crear una propuesta de gasto back_link: Volver + show: + author_deleted: Usuario eliminado + spending_proposal: + spending_proposal: Propuesta de inversión wrong_price_format: Solo puede incluir caracteres numéricos spending_proposal: already_supported: Ya has apoyado este proyecto. Compartelo! diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb index 2d2804233..bde574935 100644 --- a/spec/features/proposals_spec.rb +++ b/spec/features/proposals_spec.rb @@ -87,6 +87,29 @@ feature 'Proposals' do end end + context "Embedded video" do + scenario "Show YouTube video" do + proposal = create(:proposal, video_url: "http://www.youtube.com/watch?v=a7UFm6ErMPU") + visit proposal_path(proposal) + expect(page).to have_selector("div[id='js-embedded-video']") + expect(page.html).to include 'https://www.youtube.com/embed/a7UFm6ErMPU' + end + + scenario "Show Vimeo video" do + proposal = create(:proposal, video_url: "https://vimeo.com/7232823" ) + visit proposal_path(proposal) + expect(page).to have_selector("div[id='js-embedded-video']") + expect(page.html).to include 'https://player.vimeo.com/video/7232823' + end + + scenario "Dont show video" do + proposal = create(:proposal, video_url: nil) + + visit proposal_path(proposal) + expect(page).to_not have_selector("div[id='js-embedded-video']") + end + end + scenario 'Social Media Cards' do proposal = create(:proposal) diff --git a/spec/features/spending_proposals_spec.rb b/spec/features/spending_proposals_spec.rb index 8462cc77f..d63b00f30 100644 --- a/spec/features/spending_proposals_spec.rb +++ b/spec/features/spending_proposals_spec.rb @@ -5,17 +5,17 @@ feature 'Spending proposals' do let(:author) { create(:user, :level_two, username: 'Isabel') } scenario 'Index' do - visit spending_proposals_path - - expect(page).to_not have_link('Create spending proposal', href: new_spending_proposal_path) - expect(page).to have_link('verify your account') - - login_as(author) + spending_proposals = [create(:spending_proposal), create(:spending_proposal), create(:spending_proposal)] visit spending_proposals_path - expect(page).to have_link('Create spending proposal', href: new_spending_proposal_path) - expect(page).to_not have_link('verify your account') + expect(page).to have_selector('#investment-projects .investment-project', count: 3) + spending_proposals.each do |spending_proposal| + within('#investment-projects') do + expect(page).to have_content spending_proposal.title + expect(page).to have_css("a[href='#{spending_proposal_path(spending_proposal)}']", text: spending_proposal.title) + end + end end scenario 'Create' do diff --git a/spec/models/spending_proposal_spec.rb b/spec/models/spending_proposal_spec.rb index 4d320a8d7..ebe3573a4 100644 --- a/spec/models/spending_proposal_spec.rb +++ b/spec/models/spending_proposal_spec.rb @@ -227,6 +227,38 @@ describe SpendingProposal do expect(valuation_finished.first).to eq(spending_proposal3) end end + + describe "feasible" do + it "should return all feasible spending proposals" do + feasible_spending_proposal = create(:spending_proposal, feasible: true) + create(:spending_proposal) + + expect(SpendingProposal.feasible).to eq [feasible_spending_proposal] + end + end + + describe "unfeasible" do + it "should return all unfeasible spending proposals" do + unfeasible_spending_proposal = create(:spending_proposal, feasible: false) + create(:spending_proposal, feasible: true) + + expect(SpendingProposal.unfeasible).to eq [unfeasible_spending_proposal] + end + end + + describe "not_unfeasible" do + it "should return all not unfeasible spending proposals" do + not_unfeasible_spending_proposal_1 = create(:spending_proposal, feasible: true) + not_unfeasible_spending_proposal_2 = create(:spending_proposal) + create(:spending_proposal, feasible: false) + + not_unfeasibles = SpendingProposal.not_unfeasible + + expect(not_unfeasibles.size).to eq(2) + expect(not_unfeasibles.include?(not_unfeasible_spending_proposal_1)).to eq(true) + expect(not_unfeasibles.include?(not_unfeasible_spending_proposal_2)).to eq(true) + end + end end end