merges with master

This commit is contained in:
kikito
2016-03-29 17:44:16 +02:00
20 changed files with 308 additions and 43 deletions

View File

@@ -42,6 +42,7 @@
//= require suggest //= require suggest
//= require forms //= require forms
//= require valuation_spending_proposal_form //= require valuation_spending_proposal_form
//= require embed_video
var initialize_modules = function() { var initialize_modules = function() {
App.Comments.initialize(); App.Comments.initialize();
@@ -59,6 +60,7 @@ var initialize_modules = function() {
App.Suggest.initialize(); App.Suggest.initialize();
App.Forms.initialize(); App.Forms.initialize();
App.ValuationSpendingProposalForm.initialize(); App.ValuationSpendingProposalForm.initialize();
App.EmbedVideo.initialize();
}; };
$(function(){ $(function(){

View File

@@ -0,0 +1,7 @@
App.EmbedVideo =
initialize: ->
$('#js-embedded-video').each ->
code = $(this).data("video-code")
$('#js-embedded-video').html(code)

View File

@@ -274,7 +274,7 @@
.debate-new, .debate-edit, .debate-new, .debate-edit,
.proposal-new, .proposal-edit, .proposal-new, .proposal-edit,
.spending-proposal-new, .spending-proposal-edit { .spending-proposal-new, .spending-proposal-edit {
.back { .back {
@include back; @include back;
@@ -546,7 +546,7 @@
} }
} }
.debate, .proposal { .debate, .proposal, .investment-project {
margin-bottom: 0; margin-bottom: 0;
margin-top: 0; margin-top: 0;
@@ -565,7 +565,7 @@
padding-bottom: rem-calc(12); padding-bottom: rem-calc(12);
} }
.label-debate, .label-proposal { .label-debate, .label-proposal, .label-investment-project {
background: none; background: none;
clear: both; clear: both;
display: block; display: block;
@@ -587,6 +587,10 @@
color: $proposals; color: $proposals;
} }
.label-investment-project {
color: $budget;
}
h3 { h3 {
font-weight: bold; font-weight: bold;
margin: 0; margin: 0;
@@ -596,7 +600,7 @@
} }
} }
.debate-content, .proposal-content { .debate-content, .proposal-content, .investment-project-content {
margin: 0; margin: 0;
min-height: rem-calc(180); min-height: rem-calc(180);
position: relative; position: relative;
@@ -606,7 +610,7 @@
} }
} }
.icon-debates, .icon-proposals { .icon-debates, .icon-proposals, .icon-budget {
font-size: rem-calc(18); font-size: rem-calc(18);
line-height: $line-height; line-height: $line-height;
position: absolute; position: absolute;
@@ -623,7 +627,13 @@
left: rem-calc(72); 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; color: $text-medium;
font-size: $small-font-size; font-size: $small-font-size;
margin: rem-calc(6) 0 0; margin: rem-calc(6) 0 0;
@@ -638,7 +648,7 @@
} }
} }
.debate-description, .proposal-description { .debate-description, .proposal-description, .investment-project-description {
color: $text; color: $text;
font-size: rem-calc(13); font-size: rem-calc(13);
height: rem-calc(72); 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 { .proposals-summary {
.panel { .panel {

View File

@@ -7,7 +7,7 @@ class Admin::SpendingProposalsController < Admin::BaseController
load_and_authorize_resource load_and_authorize_resource
def index 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 end
def show def show

View File

@@ -65,8 +65,7 @@ module CommentableActions
end end
end end
def map
def map
@resource = resource_model.new @resource = resource_model.new
@tag_cloud = tag_cloud @tag_cloud = tag_cloud
end end

View File

@@ -12,6 +12,8 @@ class SpendingProposalsController < ApplicationController
respond_to :html, :js respond_to :html, :js
def index def index
@spending_proposals = @search_terms.present? ? SpendingProposal.search(@search_terms) : SpendingProposal.all
@spending_proposals = @spending_proposals.page(params[:page]).for_render
end end
def new def new

View File

@@ -10,7 +10,7 @@ class Valuation::SpendingProposalsController < Valuation::BaseController
def index def index
if current_user.valuator? 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 else
@spending_proposals = SpendingProposal.none.page(params[:page]) @spending_proposals = SpendingProposal.none.page(params[:page])
end end

View File

@@ -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]
'<iframe src="' + src + match[2] + '" frameborder="0" allowfullscreen></iframe>'
else
''
end
end
end

View File

@@ -25,12 +25,15 @@ class SpendingProposal < ActiveRecord::Base
scope :managed, -> { valuation_open.where(valuation_assignments_count: 0).where("administrator_id IS NOT ?", nil) } 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 :valuating, -> { valuation_open.where("valuation_assignments_count > 0 AND valuation_finished = ?", false) }
scope :valuation_finished, -> { where(valuation_finished: true) } 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_admin, -> (admin) { where(administrator_id: admin.presence) }
scope :by_tag, -> (tag_name) { tagged_with(tag_name) } scope :by_tag, -> (tag_name) { tagged_with(tag_name) }
scope :by_valuator, -> (valuator) { where("valuation_assignments.valuator_id = ?", valuator.presence).joins(:valuation_assignments) } 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 def description
super.try :html_safe 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 } params.select{|x,_| %w{geozone_id administrator_id tag_name valuator_id}.include? x.to_s }
end end
def self.search(params, current_filter) def self.scoped_filter(params, current_filter)
results = self results = self
results = results.by_geozone(params[:geozone_id]) if params[:geozone_id].present? 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_admin(params[:administrator_id]) if params[:administrator_id].present?
results = results.by_tag(params[:tag_name]) if params[:tag_name].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.by_valuator(params[:valuator_id]) if params[:valuator_id].present?
results = results.send(current_filter) if current_filter.present? results = results.send(current_filter) if current_filter.present?
results.for_render results.includes(:geozone, administrator: :user, valuators: :user)
end end
def self.by_geozone(geozone) def self.by_geozone(geozone)

View File

@@ -2,7 +2,6 @@
<section class="row-full comments"> <section class="row-full comments">
<div class="row"> <div class="row">
<div id="comments" class="small-12 column"> <div id="comments" class="small-12 column">
<h2> <h2>
<%= t("proposals.show.comments_title") %> <%= t("proposals.show.comments_title") %>
<span class="js-comments-count">(<%= @proposal.comments_count %>)</span> <span class="js-comments-count">(<%= @proposal.comments_count %>)</span>

View File

@@ -62,7 +62,8 @@
<div class="video-link"> <div class="video-link">
<%= text_with_links @proposal.video_url %> <%= text_with_links @proposal.video_url %>
</div> </div>
<% end %> <div id="js-embedded-video" data-video-code="<%= embedded_video_code %>"></div>
<% end %>
<h4><%= @proposal.question %></h4> <h4><%= @proposal.question %></h4>

View File

@@ -0,0 +1,11 @@
<div class="sidebar-divider"></div>
<h3 class="sidebar-title"><%= t("spending_proposals.index.sidebar.geozones") %></h3>
<br>
<div class="geozone">
<% Geozone.names.each do |geozone| %>
<%= link_to geozone, spending_proposals_path(search: geozone) %>
<% end %>
</div>
<div class="sidebar-divider"></div>
<h3 class="sidebar-title"><%= t("spending_proposals.index.sidebar.feasibility") %></h3>
<br>

View File

@@ -0,0 +1,53 @@
<div id="<%= dom_id(spending_proposal) %>" class="investment-project clear">
<div class="panel">
<div class="row">
<div class="small-12 medium-9 column">
<div class="investment-project-content">
<% cache [locale_and_user_status(spending_proposal), 'index', spending_proposal, spending_proposal.author] do %>
<span class="label-investment-project float-left"><%= t("spending_proposals.spending_proposal.spending_proposal") %></span>
<span class="icon-budget"></span>
<h3><%= link_to spending_proposal.title, spending_proposal_path(spending_proposal) %></h3>
<p class="investment-project-info">
<%= l spending_proposal.created_at.to_date %>
<% if spending_proposal.author.hidden? || spending_proposal.author.erased? %>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<span class="author">
<%= t("spending_proposals.show.author_deleted") %>
</span>
<% else %>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<span class="author">
<%= spending_proposal.author.name %>
</span>
<% if spending_proposal.author.official? %>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<span class="label round level-<%= spending_proposal.author.official_level %>">
<%= spending_proposal.author.official_position %>
</span>
<% end %>
<% end %>
<% if spending_proposal.author.verified_organization? %>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<span class="label round is-association">
<%= t("shared.collective") %>
</span>
<% end %>
</p>
<div class="investment-project-description">
<p><%= link_to spending_proposal.description, spending_proposal_path(spending_proposal) %></p>
<div class="truncate"></div>
</div>
<% end %>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,16 +1,35 @@
<% provide :title do %><%= t('spending_proposals.index.title') %><% end %> <% provide :title do %><%= t('spending_proposals.index.title') %><% end %>
<div class="page row-full"> <% content_for :header_addon do %>
<div class="row"> <%= render "shared/search_form",
<div class="more-information text small-12 medium-8 column"> search_path: spending_proposals_path(page: 1),
<h1><%= t('spending_proposals.index.title') %></h1> i18n_namespace: "spending_proposals.index.search_form" %>
<% end %>
<p><%= t('spending_proposals.index.text_html') %></p> <main>
<div class="wrap row">
<div id="investment-projects" class="investment-projects-list small-12 medium-9 column">
<% if can? :create, SpendingProposal %> <div class="small-12 search-results">
<%= link_to t('spending_proposals.index.create_link'), new_spending_proposal_path, class: 'button' %> <% if @search_terms %>
<% else %> <h2>
<p><%= t('spending_proposals.index.verified_only', verify_account: link_to(t('spending_proposals.index.verify_account'), verification_path)).html_safe %></p> <%= page_entries_info @spending_proposals %>
<% end %> <%= t("spending_proposals.index.search_results", count: @spending_proposals.size, search_term: @search_terms) %>
</h2>
<% end %>
</div>
<div class="show-for-small-only">
<%= link_to t("spending_proposals.index.start_spending_proposal"), new_spending_proposal_path, class: 'button expanded' %>
</div>
<%= render partial: 'spending_proposals/spending_proposal', collection: @spending_proposals %>
<%= paginate @spending_proposals %>
</div> </div>
<div class="small-12 medium-3 column">
<aside class="margin-bottom">
<%= render 'sidebar' %>
</aside>
</div>
</div> </div>
</div> </main>

View File

@@ -131,6 +131,7 @@ ignore_unused:
- 'proposals.index.select_order' - 'proposals.index.select_order'
- 'proposals.index.orders.*' - 'proposals.index.orders.*'
- 'proposals.index.search_form.*' - 'proposals.index.search_form.*'
- 'spending_proposals.index.search_form.*'
- 'notifications.index.comments_on*' - 'notifications.index.comments_on*'
- 'notifications.index.replies_to*' - 'notifications.index.replies_to*'
- 'helpers.page_entries_info.*' # kaminari - 'helpers.page_entries_info.*' # kaminari

View File

@@ -412,11 +412,18 @@ en:
new: Create new: Create
title: Spending proposal title title: Spending proposal title
index: 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 title: Participatory budgeting
verified_only: Only verified users can create spending proposals, %{verify_account}. search_form:
verify_account: verify your account 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: new:
more_info: How do participatory budgeting works? more_info: How do participatory budgeting works?
recommendation_one: It's mandatory that the proposal makes reference to a budgetable action. 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 recommendations_title: How to create a spending proposal
start_new: Create spending proposal start_new: Create spending proposal
back_link: Back back_link: Back
show:
author_deleted: User deleted
spending_proposal:
spending_proposal: Investment project
wrong_price_format: Only integer numbers wrong_price_format: Only integer numbers
spending_proposal: spending_proposal:
already_supported: You have already supported this. Share it! already_supported: You have already supported this. Share it!

View File

@@ -412,11 +412,18 @@ es:
new: Crear new: Crear
title: Título de la propuesta de gasto title: Título de la propuesta de gasto
index: 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.<br>El requisito principal es que sean propuestas presupuestables.
title: Presupuestos participativos title: Presupuestos participativos
verified_only: Sólo los usuarios verificados pueden crear propuestas de gasto, %{verify_account}. search_form:
verify_account: verifica tu cuenta 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: new:
more_info: "¿Cómo funcionan los presupuestos participativos?" more_info: "¿Cómo funcionan los presupuestos participativos?"
recommendation_one: Es fundamental que haga referencia a una actuación presupuestable. 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 recommendations_title: Cómo crear una propuesta de gasto
start_new: Crear una propuesta de gasto start_new: Crear una propuesta de gasto
back_link: Volver 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 wrong_price_format: Solo puede incluir caracteres numéricos
spending_proposal: spending_proposal:
already_supported: Ya has apoyado este proyecto. Compartelo! already_supported: Ya has apoyado este proyecto. Compartelo!

View File

@@ -87,6 +87,29 @@ feature 'Proposals' do
end end
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 scenario 'Social Media Cards' do
proposal = create(:proposal) proposal = create(:proposal)

View File

@@ -5,17 +5,17 @@ feature 'Spending proposals' do
let(:author) { create(:user, :level_two, username: 'Isabel') } let(:author) { create(:user, :level_two, username: 'Isabel') }
scenario 'Index' do scenario 'Index' do
visit spending_proposals_path spending_proposals = [create(:spending_proposal), create(:spending_proposal), create(:spending_proposal)]
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)
visit spending_proposals_path visit spending_proposals_path
expect(page).to have_link('Create spending proposal', href: new_spending_proposal_path) expect(page).to have_selector('#investment-projects .investment-project', count: 3)
expect(page).to_not have_link('verify your account') 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 end
scenario 'Create' do scenario 'Create' do

View File

@@ -227,6 +227,38 @@ describe SpendingProposal do
expect(valuation_finished.first).to eq(spending_proposal3) expect(valuation_finished.first).to eq(spending_proposal3)
end end
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
end end