Merge branch 'proposals' of github.com:AyuntamientoMadrid/participacion into proposals
This commit is contained in:
@@ -1,9 +1,20 @@
|
||||
class ProposalsController < ApplicationController
|
||||
before_action :parse_order, only: :index
|
||||
before_action :parse_tag_filter, only: :index
|
||||
before_action :parse_search_terms, only: :index
|
||||
before_action :authenticate_user!, except: [:index, :show]
|
||||
|
||||
load_and_authorize_resource
|
||||
respond_to :html, :js
|
||||
|
||||
def index
|
||||
@proposals = @search_terms.present? ? Proposal.search(@search_terms) : Proposal.all
|
||||
@proposals = @proposals.tagged_with(@tag_filter) if @tag_filter
|
||||
@proposals = @proposals.page(params[:page]).for_render.send("sort_by_#{@order}")
|
||||
@tag_cloud = Proposal.tag_counts.order(taggings_count: :desc, name: :asc).limit(20)
|
||||
set_proposal_votes(@proposals)
|
||||
end
|
||||
|
||||
def show
|
||||
set_proposal_votes(@proposal)
|
||||
@commentable = @proposal
|
||||
@@ -55,4 +66,19 @@ class ProposalsController < ApplicationController
|
||||
def load_featured_tags
|
||||
@featured_tags = ActsAsTaggableOn::Tag.where(featured: true)
|
||||
end
|
||||
|
||||
def parse_order
|
||||
@valid_orders = ['confidence_score', 'hot_score', 'created_at', 'most_commented', 'random']
|
||||
@order = @valid_orders.include?(params[:order]) ? params[:order] : @valid_orders.first
|
||||
end
|
||||
|
||||
def parse_tag_filter
|
||||
if params[:tag].present?
|
||||
@tag_filter = params[:tag] if ActsAsTaggableOn::Tag.where(name: params[:tag]).exists?
|
||||
end
|
||||
end
|
||||
|
||||
def parse_search_terms
|
||||
@search_terms = params[:search] if params[:search].present?
|
||||
end
|
||||
end
|
||||
@@ -9,7 +9,7 @@ class Ability
|
||||
|
||||
# Not logged in users
|
||||
can :read, Debate
|
||||
|
||||
can :read, Proposal
|
||||
|
||||
if user # logged-in users
|
||||
can [:read, :update], User, id: user.id
|
||||
@@ -34,11 +34,18 @@ class Ability
|
||||
can [:flag, :unflag], Debate
|
||||
cannot [:flag, :unflag], Debate, author_id: user.id
|
||||
|
||||
can [:flag, :unflag], Proposal
|
||||
cannot [:flag, :unflag], Proposal, author_id: user.id
|
||||
|
||||
unless user.organization?
|
||||
can :vote, Debate
|
||||
can :vote, Comment
|
||||
end
|
||||
|
||||
if user.level_two_verified?
|
||||
can :vote, Proposal
|
||||
end
|
||||
|
||||
if user.moderator? || user.administrator?
|
||||
can :read, Organization
|
||||
can(:verify, Organization){ |o| !o.verified? }
|
||||
@@ -58,12 +65,18 @@ class Ability
|
||||
can :ignore_flag, Debate, ignored_flag_at: nil, hidden_at: nil
|
||||
cannot :ignore_flag, Debate, author_id: user.id
|
||||
|
||||
can :hide, Proposal, hidden_at: nil
|
||||
cannot :hide, Proposal, author_id: user.id
|
||||
|
||||
can :ignore_flag, Proposal, ignored_flag_at: nil, hidden_at: nil
|
||||
cannot :ignore_flag, Proposal, author_id: user.id
|
||||
|
||||
can :hide, User
|
||||
cannot :hide, User, id: user.id
|
||||
end
|
||||
|
||||
if user.moderator?
|
||||
can :comment_as_moderator, [Debate, Comment]
|
||||
can :comment_as_moderator, [Debate, Comment, Proposal]
|
||||
end
|
||||
|
||||
if user.administrator?
|
||||
@@ -73,6 +86,9 @@ class Ability
|
||||
can :restore, Debate
|
||||
cannot :restore, Debate, hidden_at: nil
|
||||
|
||||
can :restore, Proposal
|
||||
cannot :restore, Proposal, hidden_at: nil
|
||||
|
||||
can :restore, User
|
||||
cannot :restore, User, hidden_at: nil
|
||||
|
||||
@@ -82,10 +98,13 @@ class Ability
|
||||
can :confirm_hide, Debate
|
||||
cannot :confirm_hide, Debate, hidden_at: nil
|
||||
|
||||
can :confirm_hide, Proposal
|
||||
cannot :confirm_hide, Proposal, hidden_at: nil
|
||||
|
||||
can :confirm_hide, User
|
||||
cannot :confirm_hide, User, hidden_at: nil
|
||||
|
||||
can :comment_as_administrator, [Debate, Comment]
|
||||
can :comment_as_administrator, [Debate, Comment, Proposal]
|
||||
|
||||
can :manage, Moderator
|
||||
end
|
||||
|
||||
@@ -21,6 +21,13 @@ class Proposal < ActiveRecord::Base
|
||||
before_validation :sanitize_description
|
||||
before_validation :sanitize_tag_list
|
||||
|
||||
scope :for_render, -> { includes(:tags) }
|
||||
scope :sort_by_hot_score , -> { order(hot_score: :desc) }
|
||||
scope :sort_by_confidence_score , -> { order(confidence_score: :desc) }
|
||||
scope :sort_by_created_at, -> { order(created_at: :desc) }
|
||||
scope :sort_by_most_commented, -> { order(comments_count: :desc) }
|
||||
scope :sort_by_random, -> { order("RANDOM()") }
|
||||
|
||||
def total_votes
|
||||
cached_votes_up
|
||||
end
|
||||
@@ -60,7 +67,7 @@ class Proposal < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def editable_by?(user)
|
||||
editable? && author == user
|
||||
editable? && author_id == user.id
|
||||
end
|
||||
|
||||
def votable_by?(user)
|
||||
|
||||
30
app/views/proposals/_proposal.html.erb
Normal file
30
app/views/proposals/_proposal.html.erb
Normal file
@@ -0,0 +1,30 @@
|
||||
<div id="<%= dom_id(proposal) %>" class="proposal clear">
|
||||
<div class="panel">
|
||||
<div class="row">
|
||||
|
||||
<div class="small-12 medium-9 column">
|
||||
<div class="proposal-content">
|
||||
<span class="label left"><%= t("proposals.proposal.proposal") %></span>
|
||||
<i class="icon-proposals"></i>
|
||||
<h3><%= link_to proposal.title, proposal %></h3>
|
||||
<p class="proposal-info">
|
||||
<i class="icon-comments"></i>
|
||||
<%= link_to t("proposals.proposal.comments", count: proposal.comments_count), proposal_path(proposal, anchor: "comments") %>
|
||||
<span class="bullet"> • </span>
|
||||
<%= l proposal.created_at.to_date %>
|
||||
</p>
|
||||
<div class="proposal-description">
|
||||
<%= link_to proposal.description, proposal %>
|
||||
<div class="truncate"></div>
|
||||
</div>
|
||||
<%= render "shared/tags", proposal: proposal, limit: 5 %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="<%= dom_id(proposal) %>_votes" class="small-12 medium-3 column text-center">
|
||||
<%= render 'proposals/votes', proposal: proposal %>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
55
app/views/proposals/index.html.erb
Normal file
55
app/views/proposals/index.html.erb
Normal file
@@ -0,0 +1,55 @@
|
||||
<section role="main">
|
||||
|
||||
<div class="wrap row">
|
||||
<div id="Proposals" class="Proposals-list small-12 medium-9 column js-order-<%= @order.dasherize %>">
|
||||
<div class="filters">
|
||||
<div class="small-12 medium-7 column">
|
||||
<% if @search_terms %>
|
||||
<h2 class="margin-top">
|
||||
<%= page_entries_info @proposals %>
|
||||
<%= t("proposals.index.search_results", count: @proposals.size, search_term: @search_terms) %>
|
||||
</h2>
|
||||
<% elsif @tag_filter %>
|
||||
<h2 class="margin-top">
|
||||
<%= page_entries_info @proposals %>
|
||||
<%= t("proposals.index.filter_topic", count: @proposals.size, topic: @tag_filter) %>
|
||||
</h2>
|
||||
<% end %>
|
||||
</div>
|
||||
<% if @tag_filter || @search_terms %>
|
||||
<div class="small-12 medium-5 margin-top inline-block proposals-order">
|
||||
<h6 class="inline-block">
|
||||
<%= t("proposals.index.select_order") %>
|
||||
</h6>
|
||||
<% else %>
|
||||
<div class="small-12 column margin-top inline-block">
|
||||
<h2 class="inline-block">
|
||||
<%= t("proposals.index.select_order_long") %>
|
||||
</h2>
|
||||
<% end %>
|
||||
<form class="inline-block">
|
||||
<select class="js-location-changer" name="order-selector">
|
||||
<% @valid_orders.each do |order| %>
|
||||
<option <%= 'selected' if order == @order %>
|
||||
value='<%= current_path_with_query_params(order: order, page: 1) %>'>
|
||||
<%= t("proposals.index.orders.#{order}") %>
|
||||
</option>
|
||||
<% end %>
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="show-for-small-only">
|
||||
<%= link_to t("proposals.index.start_proposal"), new_proposal_path, class: 'button radius expand' %>
|
||||
</div>
|
||||
<%= render @proposals %>
|
||||
<%= paginate @proposals %>
|
||||
</div>
|
||||
<div class="small-12 medium-3 column">
|
||||
<aside class="sidebar" role="complementary">
|
||||
<%= link_to t("proposals.index.start_proposal"), new_proposal_path, class: 'button radius expand' %>
|
||||
<%= render "shared/tag_cloud" %>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -4,7 +4,7 @@
|
||||
<% if taggable.tags.any? %>
|
||||
<span class='tags'>
|
||||
<% taggable.tag_list_with_limit(limit).each do |tag| %>
|
||||
<%= link_to sanitize(tag.name), send("#{taggable.class.to_s.downcase}_path", tag: tag.name) %>
|
||||
<%= link_to sanitize(tag.name), send("#{taggable.class.to_s.downcase.pluralize}_path", tag: tag.name) %>
|
||||
<% end %>
|
||||
|
||||
<% if taggable.tags_count_out_of_limit(limit) > 0 %>
|
||||
|
||||
@@ -3,8 +3,17 @@ require 'cancan/matchers'
|
||||
|
||||
describe Ability do
|
||||
subject(:ability) { Ability.new(user) }
|
||||
let(:debate) { Debate.new }
|
||||
let(:debate) { create(:debate) }
|
||||
let(:comment) { create(:comment) }
|
||||
let(:proposal) { create(:proposal) }
|
||||
|
||||
let(:own_debate) { create(:debate, author: user) }
|
||||
let(:own_comment) { create(:comment, author: user) }
|
||||
let(:own_proposal) { create(:proposal, author: user) }
|
||||
|
||||
let(:hidden_debate) { create(:debate, :hidden) }
|
||||
let(:hidden_comment) { create(:comment, :hidden) }
|
||||
let(:hidden_proposal) { create(:proposal, :hidden) }
|
||||
|
||||
describe "Non-logged in user" do
|
||||
let(:user) { nil }
|
||||
@@ -13,6 +22,15 @@ describe Ability do
|
||||
it { should be_able_to(:show, debate) }
|
||||
it { should_not be_able_to(:edit, Debate) }
|
||||
it { should_not be_able_to(:vote, Debate) }
|
||||
it { should_not be_able_to(:flag, Debate) }
|
||||
it { should_not be_able_to(:unflag, Debate) }
|
||||
|
||||
it { should be_able_to(:index, Proposal) }
|
||||
it { should be_able_to(:show, proposal) }
|
||||
it { should_not be_able_to(:edit, Proposal) }
|
||||
it { should_not be_able_to(:vote, Proposal) }
|
||||
it { should_not be_able_to(:flag, Proposal) }
|
||||
it { should_not be_able_to(:unflag, Proposal) }
|
||||
end
|
||||
|
||||
describe "Citizen" do
|
||||
@@ -28,8 +46,14 @@ describe Ability do
|
||||
it { should be_able_to(:create, Comment) }
|
||||
it { should be_able_to(:vote, Comment) }
|
||||
|
||||
it { should be_able_to(:index, Proposal) }
|
||||
it { should be_able_to(:show, proposal) }
|
||||
it { should_not be_able_to(:vote, Proposal) }
|
||||
|
||||
it { should_not be_able_to(:comment_as_administrator, debate) }
|
||||
it { should_not be_able_to(:comment_as_moderator, debate) }
|
||||
it { should_not be_able_to(:comment_as_administrator, proposal) }
|
||||
it { should_not be_able_to(:comment_as_moderator, proposal) }
|
||||
|
||||
describe 'flagging content' do
|
||||
it { should be_able_to(:flag, debate) }
|
||||
@@ -38,18 +62,18 @@ describe Ability do
|
||||
it { should be_able_to(:flag, comment) }
|
||||
it { should be_able_to(:unflag, comment) }
|
||||
|
||||
describe "own comments" do
|
||||
let(:own_comment) { create(:comment, author: user) }
|
||||
it { should be_able_to(:flag, proposal) }
|
||||
it { should be_able_to(:unflag, proposal) }
|
||||
|
||||
describe "own content" do
|
||||
it { should_not be_able_to(:flag, own_comment) }
|
||||
it { should_not be_able_to(:unflag, own_comment) }
|
||||
end
|
||||
|
||||
describe "own debates" do
|
||||
let(:own_debate) { create(:debate, author: user) }
|
||||
|
||||
it { should_not be_able_to(:flag, own_debate) }
|
||||
it { should_not be_able_to(:unflag, own_debate) }
|
||||
|
||||
it { should_not be_able_to(:flag, own_proposal) }
|
||||
it { should_not be_able_to(:unflag, own_proposal) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -60,15 +84,28 @@ describe Ability do
|
||||
end
|
||||
|
||||
describe "editing debates" do
|
||||
let(:own_debate) { create(:debate, author: user) }
|
||||
let(:own_debate_non_editable) { create(:debate, author: user) }
|
||||
|
||||
before { allow(own_debate_non_editable).to receive(:editable?).and_return(false) }
|
||||
|
||||
it { should be_able_to(:edit, own_debate) }
|
||||
it { should_not be_able_to(:edit, debate) } # Not his
|
||||
it { should_not be_able_to(:edit, own_debate_non_editable) }
|
||||
end
|
||||
|
||||
describe "editing proposals" do
|
||||
let(:own_proposal_non_editable) { create(:proposal, author: user) }
|
||||
before { allow(own_proposal_non_editable).to receive(:editable?).and_return(false) }
|
||||
|
||||
it { should be_able_to(:edit, own_proposal) }
|
||||
it { should_not be_able_to(:edit, proposal) } # Not his
|
||||
it { should_not be_able_to(:edit, own_proposal_non_editable) }
|
||||
end
|
||||
|
||||
describe "when level 2 verified" do
|
||||
before{ user.update(residence_verified_at: Time.now, confirmed_phone: "1") }
|
||||
|
||||
it { should be_able_to(:vote, Proposal) }
|
||||
end
|
||||
end
|
||||
|
||||
describe "Organization" do
|
||||
@@ -82,6 +119,10 @@ describe Ability do
|
||||
it { should be_able_to(:show, debate) }
|
||||
it { should_not be_able_to(:vote, debate) }
|
||||
|
||||
it { should be_able_to(:index, Proposal) }
|
||||
it { should be_able_to(:show, proposal) }
|
||||
it { should_not be_able_to(:vote, Proposal) }
|
||||
|
||||
it { should be_able_to(:create, Comment) }
|
||||
it { should_not be_able_to(:vote, Comment) }
|
||||
end
|
||||
@@ -96,6 +137,9 @@ describe Ability do
|
||||
it { should be_able_to(:show, debate) }
|
||||
it { should be_able_to(:vote, debate) }
|
||||
|
||||
it { should be_able_to(:index, Proposal) }
|
||||
it { should be_able_to(:show, proposal) }
|
||||
|
||||
it { should be_able_to(:read, Organization) }
|
||||
|
||||
describe "organizations" do
|
||||
@@ -114,12 +158,9 @@ describe Ability do
|
||||
end
|
||||
|
||||
describe "hiding, reviewing and restoring" do
|
||||
let(:own_comment) { create(:comment, author: user) }
|
||||
let(:own_debate) { create(:debate, author: user) }
|
||||
let(:hidden_comment) { create(:comment, :hidden) }
|
||||
let(:hidden_debate) { create(:debate, :hidden) }
|
||||
let(:ignored_comment) { create(:comment, :with_ignored_flag) }
|
||||
let(:ignored_debate) { create(:debate, :with_ignored_flag) }
|
||||
let(:ignored_proposal) { create(:proposal,:with_ignored_flag) }
|
||||
|
||||
it { should be_able_to(:hide, comment) }
|
||||
it { should be_able_to(:hide_in_moderation_screen, comment) }
|
||||
@@ -131,6 +172,11 @@ describe Ability do
|
||||
it { should_not be_able_to(:hide, hidden_debate) }
|
||||
it { should_not be_able_to(:hide, own_debate) }
|
||||
|
||||
it { should be_able_to(:hide, proposal) }
|
||||
it { should be_able_to(:hide_in_moderation_screen, proposal) }
|
||||
it { should_not be_able_to(:hide, hidden_proposal) }
|
||||
it { should_not be_able_to(:hide, own_proposal) }
|
||||
|
||||
it { should be_able_to(:ignore_flag, comment) }
|
||||
it { should_not be_able_to(:ignore_flag, hidden_comment) }
|
||||
it { should_not be_able_to(:ignore_flag, ignored_comment) }
|
||||
@@ -141,15 +187,23 @@ describe Ability do
|
||||
it { should_not be_able_to(:ignore_flag, ignored_debate) }
|
||||
it { should_not be_able_to(:ignore_flag, own_debate) }
|
||||
|
||||
it { should be_able_to(:ignore_flag, proposal) }
|
||||
it { should_not be_able_to(:ignore_flag, hidden_proposal) }
|
||||
it { should_not be_able_to(:ignore_flag, ignored_proposal) }
|
||||
it { should_not be_able_to(:ignore_flag, own_proposal) }
|
||||
|
||||
it { should_not be_able_to(:hide, user) }
|
||||
it { should be_able_to(:hide, other_user) }
|
||||
|
||||
it { should_not be_able_to(:restore, comment) }
|
||||
it { should_not be_able_to(:restore, debate) }
|
||||
it { should_not be_able_to(:restore, proposal) }
|
||||
it { should_not be_able_to(:restore, other_user) }
|
||||
|
||||
it { should be_able_to(:comment_as_moderator, debate) }
|
||||
it { should be_able_to(:comment_as_moderator, proposal) }
|
||||
it { should_not be_able_to(:comment_as_administrator, debate) }
|
||||
it { should_not be_able_to(:comment_as_administrator, proposal) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -160,32 +214,37 @@ describe Ability do
|
||||
let(:other_user) { create(:user) }
|
||||
let(:hidden_user) { create(:user, :hidden) }
|
||||
|
||||
let(:hidden_debate) { create(:debate, :hidden) }
|
||||
let(:hidden_comment) { create(:comment, :hidden) }
|
||||
let(:own_debate) { create(:debate, author: user)}
|
||||
let(:own_comment) { create(:comment, author: user)}
|
||||
|
||||
it { should be_able_to(:index, Debate) }
|
||||
it { should be_able_to(:show, debate) }
|
||||
it { should be_able_to(:vote, debate) }
|
||||
|
||||
it { should be_able_to(:index, Proposal) }
|
||||
it { should be_able_to(:show, proposal) }
|
||||
|
||||
it { should_not be_able_to(:restore, comment) }
|
||||
it { should_not be_able_to(:restore, debate) }
|
||||
it { should_not be_able_to(:restore, proposal) }
|
||||
it { should_not be_able_to(:restore, other_user) }
|
||||
|
||||
it { should be_able_to(:restore, hidden_comment) }
|
||||
it { should be_able_to(:restore, hidden_debate) }
|
||||
it { should be_able_to(:restore, hidden_proposal) }
|
||||
it { should be_able_to(:restore, hidden_user) }
|
||||
|
||||
it { should_not be_able_to(:confirm_hide, comment) }
|
||||
it { should_not be_able_to(:confirm_hide, debate) }
|
||||
it { should_not be_able_to(:confirm_hide, proposal) }
|
||||
it { should_not be_able_to(:confirm_hide, other_user) }
|
||||
|
||||
it { should be_able_to(:confirm_hide, hidden_comment) }
|
||||
it { should be_able_to(:confirm_hide, hidden_debate) }
|
||||
it { should be_able_to(:confirm_hide, hidden_proposal) }
|
||||
it { should be_able_to(:confirm_hide, hidden_user) }
|
||||
|
||||
it { should be_able_to(:comment_as_administrator, debate) }
|
||||
it { should_not be_able_to(:comment_as_moderator, debate) }
|
||||
|
||||
it { should be_able_to(:comment_as_administrator, proposal) }
|
||||
it { should_not be_able_to(:comment_as_moderator, proposal) }
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user