Merge remote-tracking branch 'upstream/master' into admin_password_expired

This commit is contained in:
Marcia
2016-09-27 13:26:08 +02:00
53 changed files with 959 additions and 144 deletions

View File

@@ -19,6 +19,9 @@ gem 'jquery-ui-rails'
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks'
# Fix sprockets on the
gem 'sprockets', '~> 3.6.3'
gem 'devise', '~> 3.5.7'
gem 'devise_security_extension'
# Use ActiveModel has_secure_password
@@ -39,7 +42,7 @@ gem 'ckeditor', '~> 4.2.0'
gem 'invisible_captcha', '~> 0.9.1'
gem 'cancancan'
gem 'social-share-button'
gem 'initialjs-rails', '0.2.0.1'
gem 'initialjs-rails', '0.2.0.4'
gem 'unicorn', '~> 5.1.0'
gem 'paranoia'
gem 'rinku', require: 'rails_rinku'

View File

@@ -199,8 +199,8 @@ GEM
parser (>= 2.2.3.0)
term-ansicolor (>= 1.3.2)
terminal-table (>= 1.5.1)
initialjs-rails (0.2.0.1)
railties (>= 3.1, < 5.0)
initialjs-rails (0.2.0.4)
railties (>= 3.1, < 6.0)
invisible_captcha (0.9.1)
rails
jquery-rails (4.1.1)
@@ -378,7 +378,7 @@ GEM
spring (1.7.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
sprockets (3.7.0)
sprockets (3.6.3)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-es6 (0.9.0)
@@ -472,7 +472,7 @@ DEPENDENCIES
fuubar
groupdate
i18n-tasks
initialjs-rails (= 0.2.0.1)
initialjs-rails (= 0.2.0.4)
invisible_captcha (~> 0.9.1)
jquery-rails
jquery-ui-rails
@@ -500,6 +500,7 @@ DEPENDENCIES
social-share-button
spring
spring-commands-rspec
sprockets (~> 3.6.3)
tolk
turbolinks
turnout

View File

@@ -24,6 +24,8 @@ Frontend tools used include [SCSS](http://sass-lang.com/) over [Foundation](http
## Configuration for development and test environments
**NOTE**: For more detailed instructions check the [docs](https://github.com/consul/consul/tree/master/doc/en/dev_test_setup.md)
Prerequisites: install git, Ruby 2.2.3, bundler gem, ghostscript and PostgreSQL (>=9.4).
```

View File

@@ -23,6 +23,8 @@ Las herramientas utilizadas para el frontend no están cerradas aún. Los estilo
## Configuración para desarrollo y tests
**NOTA**: para unas instrucciones más detalladas consulta la [documentación](https://github.com/consul/consul/tree/master/doc/es/dev_test_setup.md)
Prerequisitos: tener instalado git, Ruby 2.2.3, la gema `bundler`, ghostscript y PostgreSQL (9.4 o superior).
```

Binary file not shown.

View File

@@ -54,4 +54,8 @@
<glyph glyph-name="arrow-down" unicode="&#82;" d="M402 311c0-5-2-9-5-13l-128-128c-4-4-8-5-13-5-5 0-9 1-13 5l-128 128c-3 4-5 8-5 13 0 5 2 9 5 13 4 3 8 5 13 5l256 0c5 0 9-2 13-5 3-4 5-8 5-13z"/>
<glyph glyph-name="arrow-left" unicode="&#84;" d="M347 384l0-256c0-5-1-9-5-13-4-3-8-5-13-5-5 0-9 2-13 5l-128 128c-3 4-5 8-5 13 0 5 2 9 5 13l128 128c4 3 8 5 13 5 5 0 9-2 13-5 4-4 5-8 5-13z"/>
<glyph glyph-name="arrow-right" unicode="&#85;" d="M329 256c0-5-2-9-5-13l-128-128c-4-3-8-5-13-5-5 0-9 2-13 5-4 4-5 8-5 13l0 256c0 5 1 9 5 13 4 3 8 5 13 5 5 0 9-2 13-5l128-128c3-4 5-8 5-13z"/>
<glyph glyph-name="check-circle" unicode="&#86;" d="M256 480c-124 0-224-100-224-224 0-124 100-224 224-224 124 0 224 100 224 224 0 124-100 224-224 224z m115-149l-139-179c-1-1-3-3-5-3-3 0-4 1-5 3-2 1-79 76-79 76l-2 1c0 1-1 2-1 3 0 2 1 3 1 4 1 0 1 0 1 1 8 8 24 24 25 25 1 2 2 3 4 3 3 0 5-2 6-3 1-1 45-43 45-43l111 143c1 0 2 1 4 1 1 0 2 0 3-1l31-24c0-1 1-3 1-4 0-1 0-2-1-3z"/>
<glyph glyph-name="arrow-top" unicode="&#87;" d="M402 165c0-5-2-10-5-13-4-4-8-6-13-6l-256 0c-5 0-9 2-13 6-3 3-5 8-5 13 0 5 2 9 5 12l128 128c4 4 8 6 13 6 5 0 9-2 13-6l128-128c3-3 5-7 5-12z"/>
<glyph glyph-name="help-1" unicode="&#88;" d="M345 435c-27 21-58 28-98 28-29 0-55-6-75-20-30-20-44-54-44-108l77 0c0 14-2 30 7 43 8 13 20 24 40 24 20 0 31-6 41-18 8-11 11-23 11-36 0-12-5-22-12-32-4-5-9-10-15-15 0 0-42-25-56-48-11-18-15-40-16-66 0-2 0-5 7-5 7 0 56 0 62 0 6 0 7 4 7 6 0 9 2 24 3 29 4 11 10 20 20 28l21 14c18 15 33 26 40 36 11 15 19 34 19 57 0 36-13 64-39 83z m-103-293c-26 1-47-17-48-46-1-28 19-46 45-47 27-1 48 17 49 45 1 28-19 47-46 48z"/>
<glyph glyph-name="checkmark-circle" unicode="&#89;" d="M171 296l-29-30 93-93 208 208-29 29-179-178z m251-40c0-92-74-166-166-166-92 0-166 74-166 166 0 92 74 166 166 166 16 0 31-2 46-6l32 32c-24 11-50 16-78 16-114 0-208-94-208-208 0-114 94-208 208-208 114 0 208 94 208 208z"/>
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -16,7 +16,7 @@
//= require jquery-ui/datepicker-es
//= require foundation
//= require turbolinks
//= require ckeditor/init
//= require ckeditor/loader
//= require_directory ./ckeditor
//= require social-share-button
//= require initial

View File

@@ -0,0 +1,3 @@
//= require ckeditor/init
CKEDITOR.config.customConfig = '<%= javascript_path 'ckeditor/config.js' %>';

View File

@@ -172,3 +172,6 @@
.icon-arrow-right:before {
content: "\55";
}
.icon-checkmark-circle:before {
content: "\59";
}

View File

@@ -5,6 +5,7 @@
// 03. Show participation
// 04. List participation
// 05. Featured
// 06. Proposals successfull
//
// 01. Votes and supports
@@ -269,6 +270,21 @@
}
}
.message {
@include supports;
background: none;
border-top: 0;
@include breakpoint(medium) {
border-left: 1px solid $border;
margin: $line-height rem-calc(-25) 0 rem-calc(12);
}
p {
font-size: $small-font-size;
}
}
// 02. New participation
// ---------------------
@@ -548,14 +564,14 @@
.debates-list, .proposals-list, .investment-projects-list {
@include breakpoint(small) {
@include breakpoint(medium) {
margin-bottom: rem-calc(48);
}
}
.investment-projects-list {
@include breakpoint(small) {
@include breakpoint(medium) {
min-height: $line-height*15;
}
}
@@ -574,7 +590,7 @@
min-height: rem-calc(192);
padding: rem-calc(12) rem-calc(12) 0 rem-calc(12);
@include breakpoint(small) {
@include breakpoint(medium) {
margin-bottom: rem-calc(-1);
padding-bottom: rem-calc(12);
}
@@ -732,7 +748,7 @@
border: 1px solid $votes-border;
margin: 0 rem-calc(-12);
@include breakpoint(small) {
@include breakpoint(medium) {
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
margin: 0 rem-calc(-25) 0 rem-calc(12);
@@ -750,7 +766,7 @@
right: -1px;
border-width: 13px 13px 0 0;
@include breakpoint(small) {
@include breakpoint(medium) {
content: "";
}
}
@@ -762,7 +778,7 @@
padding-top: rem-calc(12);
vertical-align: top;
@include breakpoint(small) {
@include breakpoint(medium) {
display: block;
float: none;
line-height: $line-height*2;
@@ -771,7 +787,7 @@
}
}
@include breakpoint(small) {
@include breakpoint(medium) {
.like, .unlike {
span.percentage {
@@ -790,7 +806,7 @@
border: 1px solid $proposals-border;
margin: 0 rem-calc(-12);
@include breakpoint(small) {
@include breakpoint(medium) {
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
margin: 0 rem-calc(-25) 0 rem-calc(12);
@@ -808,7 +824,7 @@
right: -1px;
border-width: 13px 13px 0 0;
@include breakpoint(small) {
@include breakpoint(medium) {
content: "";
}
}
@@ -819,7 +835,7 @@
padding-top: rem-calc(12);
vertical-align: top;
@include breakpoint(small) {
@include breakpoint(medium) {
display: block;
float: none;
margin-left: 0;
@@ -840,7 +856,7 @@
min-height: rem-calc(180);
padding-top: 0;
@include breakpoint(small) {
@include breakpoint(medium) {
padding-top: $line-height*1.5;
}
@@ -929,7 +945,8 @@
// 05. Featured
// ------------
.featured-debates, .featured-proposals {
.featured-debates, .featured-proposals,
.proposals-ballot, .proposals-ballot-list {
padding: $line-height/2 0;
@include breakpoint(medium) {
@@ -1029,3 +1046,112 @@
}
}
}
// 06. Proposals successfull
// -------------------------
.dark-heading {
background: #2D3E50;
color: white;
@include breakpoint(medium) {
padding-bottom: $line-height;
}
p {
margin-bottom: 0;
&.title {
color: #FFD200;
}
}
.info {
background: #314253;
padding-top: $line-height;
@include breakpoint(medium) {
border-top: rem-calc(6) solid #FFD200;
}
}
}
.featured-proposals-ballot-banner {
background: #2D3E50 image-url("ballot_tiny.gif") no-repeat;
background-position: 75% 0;
position: relative;
h2, a:hover h2 {
color: #FFD200 !important;
}
p {
color: white;
}
@include breakpoint(medium) {
margin-left: 0 !important;
margin-right: 0 !important;
}
@include breakpoint(large) {
background: #2D3E50 image-url("ballot.gif") no-repeat;
background-position: 90% 0;
}
}
.featured-proposals-ballot-banner,
.successfull .panel {
.icon-successfull {
border-right: 60px solid #FFD200;
border-top: 0;
border-bottom: 60px solid transparent;
height: 0;
position: absolute;
right: 0;
top: 0;
width: 0;
&:after {
color: #1B254C;
content: "\59";
font-family: "icons" !important;
left: 34px;
position: absolute;
top: 5px;
}
}
}
.proposals-ballot-list {
.proposal-sucessfull {
background: white;
border-top: 1px solid $border;
padding: $line-height 0;
position: relative;
}
}
.successfull {
.panel {
position: relative;
}
.truncate {
display: none;
}
.message {
@include supports;
background: none;
border-top: 0;
@include breakpoint(medium) {
border-left: 1px solid $border;
margin: $line-height rem-calc(-25) 0 rem-calc(12);
}
}
}

View File

@@ -12,7 +12,7 @@ module CommentableActions
@tag_cloud = tag_cloud
@banners = Banner.with_active
set_resource_votes(@resources)
set_resources_instance
end

View File

@@ -22,6 +22,7 @@ class DebatesController < ApplicationController
def index_customization
@featured_debates = @debates.featured
@proposal_successfull_exists = Proposal.successfull.exists?
end
def show

View File

@@ -0,0 +1,8 @@
class ProposalBallotsController < ApplicationController
skip_authorization_check
def index
@proposal_ballots = Proposal.successfull.sort_by_confidence_score
end
end

View File

@@ -12,7 +12,7 @@ class ProposalsController < ApplicationController
invisible_captcha only: [:create, :update], honeypot: :subtitle
has_orders %w{hot_score confidence_score created_at relevance}, only: :index
has_orders %w{hot_score confidence_score created_at relevance archival_date}, only: :index
has_orders %w{most_voted newest oldest}, only: :show
load_and_authorize_resource
@@ -26,8 +26,10 @@ class ProposalsController < ApplicationController
end
def index_customization
discard_archived
load_retired
load_featured
load_proposal_ballots
load_featured unless @proposal_successfull_exists
end
def vote
@@ -80,6 +82,10 @@ class ProposalsController < ApplicationController
@featured_proposals_votes = current_user ? current_user.proposal_votes(proposals) : {}
end
def discard_archived
@resources = @resources.not_archived unless @current_order == "archival_date"
end
def load_retired
if params[:retired].present?
@resources = @resources.retired
@@ -90,11 +96,15 @@ class ProposalsController < ApplicationController
end
def load_featured
@featured_proposals = Proposal.all.sort_by_confidence_score.limit(3) if (!@advanced_search_terms && @search_terms.blank? && @tag_filter.blank? && params[:retired].blank?)
@featured_proposals = Proposal.not_archived.sort_by_confidence_score.limit(3) if (!@advanced_search_terms && @search_terms.blank? && @tag_filter.blank? && params[:retired].blank?)
if @featured_proposals.present?
set_featured_proposal_votes(@featured_proposals)
@resources = @resources.where('proposals.id NOT IN (?)', @featured_proposals.map(&:id))
end
end
def load_proposal_ballots
@proposal_successfull_exists = Proposal.successfull.exists?
end
end

View File

@@ -44,9 +44,13 @@ class Proposal < ActiveRecord::Base
scope :sort_by_random, -> { reorder("RANDOM()") }
scope :sort_by_relevance, -> { all }
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
scope :sort_by_archival_date, -> { archived.sort_by_confidence_score }
scope :archived, -> { where("proposals.created_at <= ?", Setting["months_to_archive_proposals"].to_i.months.ago)}
scope :not_archived, -> { where("proposals.created_at > ?", Setting["months_to_archive_proposals"].to_i.months.ago)}
scope :last_week, -> { where("proposals.created_at >= ?", 7.days.ago)}
scope :retired, -> { where.not(retired_at: nil) }
scope :not_retired, -> { where(retired_at: nil) }
scope :successfull, -> { where("cached_votes_up + physical_votes >= ?", Proposal.votes_needed_for_success)}
def to_param
"#{id}-#{title}".parameterize
@@ -119,7 +123,7 @@ class Proposal < ActiveRecord::Base
end
def register_vote(user, vote_value)
if votable_by?(user)
if votable_by?(user) && !archived?
vote_by(voter: user, vote: vote_value)
end
end
@@ -155,6 +159,14 @@ class Proposal < ActiveRecord::Base
Setting['votes_for_proposal_success'].to_i
end
def successfull?
total_votes >= Proposal.votes_needed_for_success
end
def archived?
self.created_at <= Setting["months_to_archive_proposals"].to_i.months.ago
end
def notifications
proposal_notifications
end

View File

@@ -29,7 +29,11 @@
<%= render "shared/banner" %>
<% end %>
<% unless @tag_filter || @search_terms || !has_featured? %>
<% if @proposal_successfull_exists %>
<%= render "proposals/proposal_ballots_banner" %>
<% end %>
<% unless @tag_filter || @search_terms || !has_featured? || @proposal_ballots.present? || @proposal_successfull_exists %>
<%= render "featured_debates" %>
<% end %>

View File

@@ -0,0 +1,13 @@
<div id="<%= dom_id(proposal) %>" class="proposal-sucessfull">
<h3><%= link_to proposal.title, proposal %></h3>
<div class="info">
<% if proposal.author.hidden? || proposal.author.erased? %>
<%= t("proposals.show.author_deleted") %>
<% else %>
<%= proposal.author.name %>
<% end %>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<%= l proposal.created_at.to_date %>
</div>
</div>

View File

@@ -0,0 +1,35 @@
<div class="expanded no-margin-top margin-bottom dark-heading">
<div class="row" data-equalizer>
<div class="small-12 medium-6 column padding" data-equalizer-watch>
<h2>
<%= t("proposal_ballots.title") %>
</h2>
<p>
<%= t("proposal_ballots.description_html").html_safe %>
</p>
</div>
<div class="small-12 medium-4 column info" data-equalizer-watch>
<p class="title"><strong><%= t("proposal_ballots.date_title") %></strong></p>
<h3><%= t("proposal_ballots.date") %></h3>
</div>
</div>
</div>
<main>
<div class="row">
<div class="small-12 column">
<% if @proposal_ballots.present? %>
<div class="proposals-ballot-list">
<% @proposal_ballots.each do |proposal_for_vote| %>
<%= render "successfull_proposal", proposal: proposal_for_vote %>
<% end %>
</div>
<% else %>
<p>
<%= t("proposal_ballots.nothing_to_vote") %>
</p>
<% end %>
</div>
</div>
</main>

View File

@@ -7,7 +7,16 @@
<% else %>
<%= proposal.author.name %>
<% end %>
&nbsp;&bull;&nbsp;
<% if proposal.author.display_official_position_badge? %>
<span class="label round level-<%= proposal.author.official_level %>">
<%= proposal.author.official_position %>
</span>
&nbsp;&bull;&nbsp;
<% end %>
<strong><%= t("proposals.proposal.supports", count: proposal.total_votes) %></strong>
</div>
</div>

View File

@@ -1,10 +1,12 @@
<div id="<%= dom_id(proposal) %>" class="proposal clear" data-type="proposal">
<div id="<%= dom_id(proposal) %>"
class="proposal clear <%= ("successfull" if proposal.total_votes > Proposal.votes_needed_for_success) %>"
data-type="proposal">
<div class="panel">
<div class="icon-successfull"></div>
<div class="row">
<div class="small-12 medium-9 column">
<div class="proposal-content">
<% cache [locale_and_user_status(proposal), 'index', proposal, proposal.author] do %>
<span class="label-proposal float-left"><%= t("proposals.proposal.proposal") %></span>
<span class="icon-proposals"></span>
@@ -50,11 +52,26 @@
</div>
</div>
<div id="<%= dom_id(proposal) %>_votes" class="small-12 medium-3 column text-center">
<%= render 'votes',
{ proposal: proposal, vote_url: vote_proposal_path(proposal, value: 'yes') } %>
<div id="<%= dom_id(proposal) %>_votes" class="small-12 medium-3 column">
<% if proposal.successfull? %>
<div class="message">
<p>
<%= t("proposal_ballots.successfull",
voting: link_to(t("proposal_ballots.voting"), proposal_ballots_path)).html_safe %>
</p>
</div>
<% elsif proposal.archived? %>
<div class="message">
<strong><%= t("proposals.proposal.supports", count: proposal.total_votes) %></strong>
<p><%= t("proposals.proposal.archived") %></p>
</div>
<% else %>
<div class="text-center">
<%= render 'votes',
{ proposal: proposal, vote_url: vote_proposal_path(proposal, value: 'yes') } %>
</div>
<% end %>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,9 @@
<div id="next-voting" class="row featured-proposals-ballot-banner">
<%= link_to proposal_ballots_path do %>
<div class="small-12 column padding">
<div class="icon-successfull"></div>
<h2><%= t("proposal_ballots.featured_title") %></h2>
<p><%= t("proposal_ballots.info") %></p>
</div>
<% end %>
</div>

View File

@@ -30,8 +30,10 @@
<% if has_banners %>
<%= render "shared/banner" %>
<% end %>
<% if @featured_proposals.present? %>
<% if @proposal_successfull_exists %>
<%= render "proposal_ballots_banner" %>
<% elsif @featured_proposals.present? %>
<div id="featured-proposals" class="row featured-proposals">
<div class="small-12 column">
<h2>
@@ -52,8 +54,10 @@
<%= link_to t("proposals.index.start_proposal"), new_proposal_path, class: 'button expanded' %>
</div>
<%= render partial: 'proposals/proposal', collection: @proposals %>
<%= paginate @proposals %>
<div id="proposals-list">
<%= render partial: 'proposals/proposal', collection: @proposals %>
<%= paginate @proposals %>
</div>
</div>
<div class="small-12 medium-3 column">

View File

@@ -102,11 +102,23 @@
<aside class="small-12 medium-3 column">
<div class="sidebar-divider"></div>
<h3><%= t("votes.supports") %></h3>
<div class="text-center">
<div id="<%= dom_id(@proposal) %>_votes">
<%= render 'votes',
{ proposal: @proposal, vote_url: vote_proposal_path(@proposal, value: 'yes') } %>
</div>
<div id="<%= dom_id(@proposal) %>_votes">
<% if @proposal.successfull? %>
<p>
<%= t("proposal_ballots.successfull",
voting: link_to(t("proposal_ballots.voting"), proposal_ballots_path)).html_safe %>
</p>
<% elsif @proposal.archived? %>
<p class="text-center">
<strong><%= t("proposals.proposal.supports", count: @proposal.total_votes) %></strong>
</p>
<p><%= t("proposals.proposal.archived") %></p>
<% else %>
<div class="text-center">
<%= render 'votes',
{ proposal: @proposal, vote_url: vote_proposal_path(@proposal, value: 'yes') } %>
</div>
<% end %>
</div>
<div id="social-share" class="sidebar-divider"></div>
<h3><%= t("proposals.show.share") %></h3>

View File

@@ -8,6 +8,9 @@
<li>
<%= link_to t("layouts.header.proposals"), proposals_path, class: ("active" if controller_name == "proposals"), accesskey: "p" %>
</li>
<li>
<%= link_to t("layouts.header.proposal_ballot"), proposal_ballots_path, class: ("active" if controller_name == "proposal_ballots"), accesskey: "v" %>
</li>
<% if feature?(:spending_proposals) %>
<li>
<%= link_to t("layouts.header.spending_proposals"), spending_proposals_path, class: ("active" if controller_name == "spending_proposals"), accesskey: "s" %>

View File

@@ -1,8 +1,4 @@
#!/usr/bin/env ruby
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
require 'rails/commands'

View File

@@ -1,8 +1,4 @@
#!/usr/bin/env ruby
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
require_relative '../config/boot'
require 'rake'
Rake.application.run

View File

@@ -1,9 +1,5 @@
#!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__)
rescue LoadError => e
raise unless e.message.include?('spring')
end
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
@@ -11,11 +7,11 @@ end
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
require "rubygems"
require "bundler/setup"
load Gem.bin_path('rspec-core', 'rspec')
load Gem.bin_path("rspec-core", "rspec")

View File

@@ -1,15 +1,17 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'spring' is installed as part of a gem, and
# this file is here to facilitate running it.
#
# This file loads spring without using Bundler, in order to be fast.
# It gets overwritten when you run the `spring binstub` command.
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
unless defined?(Spring)
require 'rubygems'
require 'bundler'
require "rubygems"
require "bundler/setup"
if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m))
Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq }
gem 'spring', match[1]
require 'spring/binstub'
end
end
load Gem.bin_path("spring", "spring")

View File

@@ -9,7 +9,7 @@ Rails.application.config.assets.version = '1.0'
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
# Rails.application.config.assets.precompile += %w( search.js )
Rails.application.config.assets.precompile += %w( ckeditor/* )
Rails.application.config.assets.precompile += %w( ckeditor/config.js )
Rails.application.config.assets.precompile += %w( ie_lt9.js )
Rails.application.config.assets.precompile += %w( stat_graphs.js )
Rails.application.config.assets.precompile += %w( print.css )

View File

@@ -0,0 +1,4 @@
Ckeditor.setup do |config|
config.assets_languages = I18n.available_locales.map(&:to_s)
config.assets_plugins = []
end

View File

@@ -209,6 +209,7 @@ en:
open_data: Open data
open_gov: Open government
proposals: Proposals
proposal_ballot: Voting
see_all: See proposals
spending_proposals: Spending proposals
legislation:
@@ -301,6 +302,7 @@ en:
hot_score: most active
most_commented: most commented
relevance: relevance
archival_date: Archived
retired_proposals: Retired proposals
retired_proposals_link: "Proposals retired by the author"
retired_links:
@@ -350,6 +352,7 @@ en:
zero: No supports
supports_necessary: "%{number} supports needed"
total_percent: 100%
archived: "This proposal has been archived and can't collect supports."
show:
author_deleted: User deleted
code: 'Proposal code:'
@@ -371,6 +374,17 @@ en:
update:
form:
submit_button: Save changes
proposal_ballots:
title: "Votings"
description_html: "The following citizen proposals that have reached the <strong>required supports</strong> and will be voted."
date_title: "Dates of participation"
date: "Soon we'll announce the date of vote these proposals."
successfull: "This proposal has reached the required supports and will be voted in the %{voting}."
voting: "next voting"
featured_title: "#NextVoting"
nothing_to_vote: "There is nothing to vote at the moment."
info: "New proposals that have reached the voting phase."
button: "I want to decide"
proposal_notifications:
new:
title: "Send message"

View File

@@ -209,6 +209,7 @@ es:
open_data: Datos abiertos
open_gov: Gobierno %{open}
proposals: Propuestas
proposal_ballot: Votaciones
see_all: Ver propuestas
spending_proposals: Presupuestos ciudadanos
legislation:
@@ -301,6 +302,7 @@ es:
hot_score: Más activas hoy
most_commented: Más comentadas
relevance: Más relevantes
archival_date: Archivadas
retired_proposals: Propuestas retiradas
retired_proposals_link: "Propuestas retiradas por sus autores"
retired_links:
@@ -350,6 +352,7 @@ es:
zero: Sin apoyos
supports_necessary: "%{number} apoyos necesarios"
total_percent: 100%
archived: "Esta propuesta ha sido archivada y ya no puede recoger apoyos."
show:
author_deleted: Usuario eliminado
code: 'Código de la propuesta:'
@@ -371,6 +374,17 @@ es:
update:
form:
submit_button: Guardar cambios
proposal_ballots:
title: "Votaciones"
description_html: "Las siguientes propuestas ciudadanas han alcanzado el <strong>número de apoyos necesarios</strong> y pasarán a votación."
date_title: "Fechas de participación"
date: "En breve anunciaremos la fecha de votación de estas propuestas."
successfull: "Esta propuesta ha alcanzado los apoyos necesarios y pasará a la %{voting}."
voting: "próxima votación"
featured_title: "#PróximaVotación"
nothing_to_vote: "No hay nada que votar en este momento."
info: "Nuevas propuestas ciudadanas han llegado a la fase de votación."
button: "Quiero decidir"
proposal_notifications:
new:
title: "Enviar mensaje"

View File

@@ -33,13 +33,13 @@ es:
Tanto los hilos, como los comentarios podrán ser valorados por cualquiera, de tal manera que será la propia ciudadanía, y nadie en su nombre, la que decida cuáles son los temas más importantes en cada momento. Estos serán presentados en la portada del espacio, pudiendo por supuesto accederse a todos los demás temas en páginas posteriores, o usando otros criterios de ordenación (los temas con más comentarios, los más nuevos, los más controvertidos, etc.).
Cada uno de los trabajadores del Ayuntamiento tiene un usuario propio, que será resaltado como tal, permitiendo que participen en los debates al mismo nivel que todos los demás ciudadanos. Esto permitirá crear espacios de comunicación directos entre unos y otros, evitando los inconvenientes que implica la comunicación medidada, y respondiendo a un planteamiento claro por parte del nuevo gobierno por el cual el Ayuntamiento trabaja para la ciudadanía, y ante ella debe responder.
Cada uno de los trabajadores del Ayuntamiento tiene un usuario propio, que será resaltado como tal, permitiendo que participen en los debates al mismo nivel que todos los demás ciudadanos. Esto permitirá crear espacios de comunicación directos entre unos y otros, evitando los inconvenientes que implica la comunicación mediada, y respondiendo a un planteamiento claro por parte del nuevo gobierno por el cual el Ayuntamiento trabaja para la ciudadanía, y ante ella debe responder.
## <a id='i2'></a> I.I. Espacio de propuestas
En este espacio, cualquier persona puede proponer una iniciativa con la intención de recabar los suficientes apoyos como para que la idea pase a ser consultada a toda la ciudadanía con caracter vinculante.
En este espacio, cualquier persona puede proponer una iniciativa con la intención de recabar los suficientes apoyos como para que la idea pase a ser consultada a toda la ciudadanía con carácter vinculante.
Las propuestas pueden ser apoyadas por ciudadanos empadronados que hayan verificado su cuenta en la plataforma de participación, de tal manera que será la propia ciudadanía, y nadie en su nombre, la que decida cuáles son las propuestas que merecen la pena ser llevadas a cabo.
Una vez que una propuesta alcance una cantidad de apoyos equivalente al 2% del censo, automaticamente pasa a ser estudiada por un grupo de trabajo del Ayuntamiento y pasará a la siguiente fase de consulta popular, en la que la ciudadanía votará si se lleva a cabo o no. El plazo máximo para recabar los apoyos necesarios será de 12 meses.
Una vez que una propuesta alcance una cantidad de apoyos equivalente al 2% del censo, automáticamente pasa a ser estudiada por un grupo de trabajo del Ayuntamiento y pasará a la siguiente fase de consulta popular, en la que la ciudadanía votará si se lleva a cabo o no. El plazo máximo para recabar los apoyos necesarios será de 12 meses.
how_to_use:
text: |-
Utilízalo en tu municipio libremente o ayúdanos a mejorarlo, es software libre.

View File

@@ -11,6 +11,7 @@ en:
max_votes_for_debate_edit: "Number of votes from which a Debate can no longer be edited"
proposal_code_prefix: "Prefix for Proposal codes"
votes_for_proposal_success: "Number of votes necessary for approval of a Proposal"
months_to_archive_proposals: "Months to archive Proposals"
email_domain_for_officials: "Email domain for public officials"
per_page_code: "Code to be included on every page"
feature:

View File

@@ -11,6 +11,7 @@ es:
max_votes_for_debate_edit: "Número de votos en que un Debate deja de poderse editar"
proposal_code_prefix: "Prefijo para los códigos de Propuestas"
votes_for_proposal_success: "Número de votos necesarios para aprobar una Propuesta"
months_to_archive_proposals: "Meses para archivar las Propuestas"
email_domain_for_officials: "Dominio de email para cargos públicos"
per_page_code: "Código a incluir en cada página"
feature:

View File

@@ -61,6 +61,8 @@ Rails.application.routes.draw do
end
end
resources :proposal_ballots, only: [:index]
resources :comments, only: [:create, :show], shallow: true do
member do
post :vote

View File

@@ -13,6 +13,7 @@ Setting.create(key: 'max_votes_for_debate_edit', value: '1000')
Setting.create(key: 'max_votes_for_proposal_edit', value: '1000')
Setting.create(key: 'proposal_code_prefix', value: 'MAD')
Setting.create(key: 'votes_for_proposal_success', value: '100')
Setting.create(key: 'months_to_archive_proposals', value: '12')
Setting.create(key: 'comments_body_max_length', value: '1000')
Setting.create(key: 'twitter_handle', value: '@consul_dev')
@@ -149,7 +150,7 @@ tags = Faker::Lorem.words(25)
description = "<p>#{Faker::Lorem.paragraphs.join('</p><p>')}</p>"
proposal = Proposal.create!(author: author,
title: Faker::Lorem.sentence(3).truncate(60),
question: Faker::Lorem.sentence(3),
question: Faker::Lorem.sentence(3) + "?",
summary: Faker::Lorem.sentence(3),
responsible_name: Faker::Name.name,
external_url: Faker::Internet.url,
@@ -161,6 +162,27 @@ tags = Faker::Lorem.words(25)
puts " #{proposal.title}"
end
puts "Creating Archived Proposals"
tags = Faker::Lorem.words(25)
(1..5).each do |i|
author = User.reorder("RANDOM()").first
description = "<p>#{Faker::Lorem.paragraphs.join('</p><p>')}</p>"
proposal = Proposal.create!(author: author,
title: Faker::Lorem.sentence(3).truncate(60),
question: Faker::Lorem.sentence(3) + "?",
summary: Faker::Lorem.sentence(3),
responsible_name: Faker::Name.name,
external_url: Faker::Internet.url,
description: description,
created_at: rand((Time.now - 1.week) .. Time.now),
tag_list: tags.sample(3).join(','),
geozone: Geozone.reorder("RANDOM()").first,
terms_of_service: "1",
created_at: Setting["months_to_archive_proposals"].to_i.months.ago)
puts " #{proposal.title}"
end
tags = ActsAsTaggableOn::Tag.where(kind: 'category')
(1..30).each do |i|
@@ -168,7 +190,7 @@ tags = ActsAsTaggableOn::Tag.where(kind: 'category')
description = "<p>#{Faker::Lorem.paragraphs.join('</p><p>')}</p>"
proposal = Proposal.create!(author: author,
title: Faker::Lorem.sentence(3).truncate(60),
question: Faker::Lorem.sentence(3),
question: Faker::Lorem.sentence(3) + "?",
summary: Faker::Lorem.sentence(3),
responsible_name: Faker::Name.name,
external_url: Faker::Internet.url,

View File

@@ -31,6 +31,9 @@ Setting["proposal_code_prefix"] = 'MAD'
# Number of votes needed for proposal success
Setting["votes_for_proposal_success"] = 53726
# Months to archive proposals
Setting["months_to_archive_proposals"] = 12
# Users with this email domain will automatically be marked as level 1 officials
# Emails under the domain's subdomains will also be included
Setting["email_domain_for_officials"] = ''

View File

@@ -1,21 +1,21 @@
# Funcionalidades
Actualmente Consul soporta:
Actualmente Consul soporta:
* Registro y verificación de usuarios tanto en la misma aplicación como con distintos proveedores (Twitter, Facebook, Google)
* Distintos perfiles de usuario, tanto ciudadanos individuales como organizaciones.
* Distintos perfiles de administración, gestión y moderación.
* Espacio permanente de debates y propuestas.
* Comentarios anidados en debates y propuestas.
* Presupuestos participativos a través de distintas fases.
* Registro y verificación de usuarios tanto en la misma aplicación como con distintos proveedores (Twitter, Facebook, Google).
* Distintos perfiles de usuario, tanto ciudadanos individuales como organizaciones.
* Distintos perfiles de administración, gestión y moderación.
* Espacio permanente de debates y propuestas.
* Comentarios anidados en debates y propuestas.
* Presupuestos participativos a través de distintas fases.
# Usuario
Para registrar un usuario nuevo es posible hacerlo en la propia aplicación, dando un nombre de usuario (Nombre público que aparecerá en tus publicaciones), un correo electrónico y una contraseña con la que se accederá a la web. Se deben aceptar las condiciones de uso. El usuario debe confirmar su correo electrónico para poder iniciar sesión
Para registrar un usuario nuevo es posible hacerlo en la propia aplicación, dando un nombre de usuario (nombre público que aparecerá en tus publicaciones), un correo electrónico y una contraseña con la que se accederá a la web. Se deben aceptar las condiciones de uso. El usuario debe confirmar su correo electrónico para poder iniciar sesión.
![Registro de usuario](imgs/user_registration.png "Registro de usuario")
Por otro lado también se puede habilitar el registro a través de servicios externos como Twitter, Facebook y Google. Para esto hace falta tener la configuración habilitada en Settings y las claves y secretos de estos servicios en el fichero *config/secrets.yml*.
Por otro lado también se puede habilitar el registro a través de servicios externos como Twitter, Facebook y Google. Para esto hace falta tener la configuración habilitada en Settings y las claves y secretos de estos servicios en el fichero *config/secrets.yml*.
```
twitter_key: ""
@@ -26,7 +26,7 @@ Por otro lado también se puede habilitar el registro a través de servicios ext
google_oauth2_secret: ""
```
Una vez el usuario ha iniciado sesión le aparecerá la posibilidad de verificar su cuenta, a través de una conexión con el padrón municipal.
Una vez el usuario ha iniciado sesión le aparecerá la posibilidad de verificar su cuenta, a través de una conexión con el padrón municipal.
![Verificación de usuario](imgs/user_preverification.png?raw=true "Verificación de usuario")
@@ -36,17 +36,17 @@ Para esta funcionalidad hace falta que el padrón municipal soporte la posibilid
# Perfil de usuario
Dentro de su perfil cada usuario puede configurar si quiere mostrar públicamente su lista de actividades, así como las notificaciones que le enviará la aplicación a través de correo electrónico. Estas notificiaciones pueden ser:
Dentro de su perfil cada usuario puede configurar si quiere mostrar públicamente su lista de actividades, así como las notificaciones que le enviará la aplicación a través de correo electrónico. Estas notificiaciones pueden ser:
* Recibir un email cuando alguien comenta en sus propuestas o debates
* Recibir un email cuando alguien contesta a sus comentarios
* Recibir emails con información interesante sobre la web
* Recibir resumen de notificaciones sobre propuestas
* Recibir emails con mensajes privados
* Recibir un email cuando alguien comenta en sus propuestas o debates.
* Recibir un email cuando alguien contesta a sus comentarios.
* Recibir emails con información interesante sobre la web.
* Recibir resumen de notificaciones sobre propuestas.
* Recibir emails con mensajes privados.
# Paneles de administración, gestión y moderación
# Paneles de administración, gestión y moderación
Consul cuenta con tres perfiles de usuario diferenciados para hacer tareas de revisión y moderación de los contenidos. Se detallan a continuación:
Consul cuenta con tres perfiles de usuario diferenciados para hacer tareas de revisión y moderación de los contenidos. Se detallan a continuación:
## Administración
@@ -56,7 +56,7 @@ Desde aquí puedes administrar el sistema, a través de las siguientes acciones:
### Temas de debate
Los temas (también llamadas tags, o etiquetas) de debate son palabras que definen los usuarios al crear debates, para catalogarlos (ej: sanidad, movilidad, arganzuela, ...). Aquí se pueden eliminar temas inapropiados, o marcarlos para ser propuestos al crear debates (cada usuario puede definir los que quiera, pero se le sugieren algunos que nos parecen útiles como catalogación por defecto; aquí se puede cambiar cuáles se sugieren)
Los temas (también llamadas tags, o etiquetas) de debate son palabras que definen los usuarios al crear debates, para catalogarlos (ej: sanidad, movilidad, arganzuela, ...). Aquí se pueden eliminar temas inapropiados, o marcarlos para ser propuestos al crear debates (cada usuario puede definir los que quiera, pero se le sugieren algunos que nos parecen útiles como catalogación por defecto; aquí se puede cambiar cuáles se sugieren).
### Propuestas/Debates/Comentarios ocultos
@@ -77,8 +77,6 @@ En la web hay dos tipos de usuarios: individuales y organizaciones. Cualquier pe
En caso de que el proceso de verificación haya sido negativo, se pulsa el botón "Rechazar". Para editar alguno de los datos de la organización, se pulsa el botón "Editar".
En caso de que el proceso de verificación haya sido negativo, se pulsa el botón "Rechazar". Para editar alguno de los datos de la organización, se pulsa el botón "Editar".
Las organizaciones que no aparecen en la lista pueden ser encontradas para actuar sobre ellas por medio del buscador en la parte superior. Para facilitar la gestión, arriba encontramos un filtro con las secciones: "pendientes" (las organizaciones que todavía no han sido verificadas o rechazadas), "verificadas", "rechazadas" y "todas".
Es recomendable revisar regularmente la sección "pendientes".
@@ -89,7 +87,7 @@ En la web, los usuarios individuales pueden ser usuarios normales, o cargos púb
### Moderadores
Mediante el buscador de la parte superior se pueden buscar usuarios, para activarlos o desactivarlos como moderadores de la web. Los moderadores al acceder a la web con su usuario ven en la parte superior una nueva sección llamada "Moderar"
Mediante el buscador de la parte superior se pueden buscar usuarios, para activarlos o desactivarlos como moderadores de la web. Los moderadores al acceder a la web con su usuario ven en la parte superior una nueva sección llamada "Moderar".
### Actividad de moderadores
@@ -103,7 +101,7 @@ Opciones generales de configuración del sistema.
Estadísticas generales del sistema.
## Moderación
## Moderación
![Panel de moderación](imgs/panel_moderation.png?raw=true "Panel de moderación")
@@ -116,13 +114,14 @@ Cuando un usuario marca en una Propuesta/Debate/Comentario la opción de "denunc
A la derecha de cada elemento aparece una caja que podemos marcar para seleccionar todos los que queramos de la lista. Una vez seleccionados uno o varios, encontramos al final de la página tres botones para realizar acciones sobre ellos:
* Ocultar: hará que esos elementos dejen de mostrarse en la web.
* Bloquear autores: hará que el autor de ese elemento deje de poder acceder a la web, y que además todos las Propuestas/Debates/Comentarios de ese usuario dejen de mostrarse en la web.
* Bloquear autores: hará que el autor de ese elemento deje de poder acceder a la web, y que además todas las Propuestas/Debates/Comentarios de ese usuario dejen de mostrarse en la web.
* Marcar como revisados cuando consideramos que esos elementos no deben ser moderados, que su contenido es correcto, y que por lo tanto deben dejar de ser mostrados en esta lista de elementos inapropiados.
Para facilitar la gestión, arriba encontramos un filtro con las secciones:
Pendientes: las Propuestas/Debates/Comentarios sobre los que todavía no se ha pulsado "ocultar", "bloquear" o "marcar como revisados", y que por lo tanto deberían ser revisados todavía
Todos: mostrando todos las Propuestas/Debates/Comentarios de la web, y no sólo los marcados como inapropiados.
Marcados como revisados: los que algún moderador ha marcado como revisados y por lo tanto parecen correctos.
* Pendientes: las Propuestas/Debates/Comentarios sobre los que todavía no se ha pulsado "ocultar", "bloquear" o "marcar como revisados", y que por lo tanto deberían ser revisados todavía.
* Todos: mostrando todos las Propuestas/Debates/Comentarios de la web, y no sólo los marcados como inapropiados.
* Marcados como revisados: los que algún moderador ha marcado como revisados y por lo tanto parecen correctos.
Es recomendable revisar regularmente la sección "pendientes".
@@ -136,12 +135,12 @@ Un buscador nos permite encontrar cualquier usuario introduciendo su nombre de u
Desde aquí puedes gestionar usuarios a través de las siguientes acciones:
* Usuarios
* Editar cuenta de usuario
* Crear propuesta
* Apoyar propuestas
* Crear propuesta de inversión
* Apoyar propts. de inversión
* Imprimir propuestas
* Imprimir propts. de inversión
* Invitaciones para usuarios
* Usuarios.
* Editar cuenta de usuario.
* Crear propuesta.
* Apoyar propuestas.
* Crear propuesta de inversión.
* Apoyar propts. de inversión.
* Imprimir propuestas.
* Imprimir propts. de inversión.
* Invitaciones para usuarios.

9
doc/en/dev_test_setup.md Normal file
View File

@@ -0,0 +1,9 @@
# Configuration for development and test environments (Mac OS X)
## Linux
## Mac OS X
See [here](dev_test_setup_osx.md)
## Windows

View File

@@ -0,0 +1,128 @@
# Configuration for development and test environments (Mac OS X)
## Homebrew
Homebrew is a very popular package manager for OS X. It's advised to use it since it makes the installation of some of the dependencies much easier.
You can find the installation instructions at: [brew.sh](http://brew.sh)
## XCode and XCode Command Line Tools
To install *git* you'll first need to install *Xcode* (download it from the Mac App Store) and its *Xcode Command Line Tools* (you can install them from the Xcode's app menu)
## Git
You can download git from: [git-scm.com/download/mac](https://git-scm.com/download/mac)
## Ruby y rbenv
OS X already comes with a preinstalled Ruby version, but it's quite old and we need a newer one (2.2.3). One of the multiple ways of installing Ruby in OS X is through *rbenv*. The installation instructions are in its GitHub repository and are pretty straight-forward:
[github.com/rbenv/rbenv](https://github.com/rbenv/rbenv)
## Bundler
```
gem install bundler
```
## PostgreSQL (>=9.4)
```
brew install postgres
```
Once installed, we need to *initialize* it:
```
initdb /usr/local/var/postgres
```
Now we're going to configure some things related to the *default user*. First we start postgres server with:
```
postgres -D /usr/local/var/postgres
```
At this point we're supposed to have postgres correctly installed and a default user will automatically be created (whose name will match our username). This user hasn't got a password yet.
If we run `psql` we'll login into the postgres console with the default user. Probably it will fail since its required that a default database exists for that user. We can create it by typing:
```
createdb 'your_username'
```
If we run `psql` again we should now get access to postgres console. With `\du` you can see the current users list.
In case you want to set a password for your user you can make it throught postgres console by:
```
ALTER USER your_username WITH PASSWORD 'your_password';
```
Now we'll create the *consul* user, the one the application is using. Run in postgres console:
```
CREATE ROLE consul WITH PASSWORD '000';
ALTER ROLE consul WITH SUPERUSER;
ALTER ROLE consul WITH login;
```
If at any point during PostgreSQL installation you feel you have messed things up, you can uninstall it and start again by running:
```
brew uninstall postgres
```
You'll have to delete also this directory (otherwise the new installation will generate conflicts, source: [gist.github.com/lxneng/741932](https://gist.github.com/lxneng/741932)):
```
rm -rf /usr/local/var/postgres
```
## Postgis
```
brew install postgis
```
## Ghostscript
```
brew install ghostscript
```
## PhantomJS
```
brew install phantomjs
```
## Cloning the repository
Now that we have all the dependencies installed we can download the repository:
```
git clone https://github.com/consul/consul.git
cd consul
bundle install
cp config/database.yml.example config/database.yml
cp config/secrets.yml.example config/secrets.yml
```
Now copy in `database.yml` the database user and password you used for *consul*.
After this:
```
rake db:create
rake db:setup
rake db:dev_seed
RAILS_ENV=test bin/rake db:setup
```
To run the tests:
```
bundle exec rspec
```

9
doc/es/dev_test_setup.md Normal file
View File

@@ -0,0 +1,9 @@
# Configuración para los entornos de desarrollo y pruebas
## Linux
## Mac OS X
Consultar [aquí](dev_test_setup_osx.md)
## Windows

View File

@@ -0,0 +1,128 @@
# Configuración para los entornos de desarrollo y pruebas (Mac OS X)
## Homebrew
Homebrew es un gestor de paquetes para OS X muy popular. Es recomendable usarlo pues facilita enormemente la instalación de algunos de los paquetes necesarios.
Puedes encontrar las instrucciones de instalación en: [brew.sh](http://brew.sh)
## XCode y XCode Command Line Tools
Para utilizar git necesitarás instalar *Xcode* (está en la Mac App Store) y las *Xcode Command Line Tools* (se instalan desde el menú de Xcode).
## Git y Github
Puedes descargar git desde: [git-scm.com/download/mac](https://git-scm.com/download/mac)
## Ruby y rbenv
OS X ya viene con una versión preinstalada de ruby, pero es bastante vieja y en nuestro caso no nos sirve. Una de las formas de instalar Ruby es a través de rbenv. Las instrucciones de instalación están en su GitHub y son bastante claras:
[github.com/rbenv/rbenv](https://github.com/rbenv/rbenv)
Después instala la versión de Ruby 2.2.3
## Bundler
```
gem install bundler
```
## PostgreSQL (>=9.4)
```
brew install postgres
```
Una vez instalado es necesario *inicializar* la instalación:
```
initdb /usr/local/var/postgres
```
Ahora vamos a configurar algunos aspectos del usuario por defecto. Primero iniciamos el servidor de postgres con:
```
postgres -D /usr/local/var/postgres
```
Llegados a este punto se supone que tenemos postgres correctamente instalado y se nos habrá creado un usuario por defecto (cuyo nombre es nuestro nombre de usuario), y que (todavía) no tiene contraseña.
Si ejecutamos `psql` accederemos a la consola de postgres con el usuario por defecto. Probablemente fallará porque es necesario que de antemano exista una base de datos por defecto para dicho usuario. Podemos crearla ejecutando sobre la terminal:
```
createdb 'tu_nombre_de_usuario'
```
Si ahora ejecutamos `psql` de nuevo deberíamos poder acceder correctamente a la consola de postgres. Si sobre la consola de postgres ejecutas `\du` puede ver la lista de usuarios actual.
En el caso de que quieras asignarte una contraseña puedes hacerlo desde la consola de postgres con:
```
ALTER USER tu_nombre_usuario WITH PASSWORD 'tu_contraseña';
```
Ahora vamos a crear el usuario *consul*, que es el que utiliza la aplicación. Ejecuta sobre la consola de postgres:
```
CREATE ROLE consul WITH PASSWORD '000';
ALTER ROLE consul WITH SUPERUSER;
ALTER ROLE consul WITH login;
```
Si en algún momento durante la instalación de PostgreSQL y postgis sospechas que te has equivocado y deseas desinstalarlo y volver a empezar desde cero:
```
brew uninstall postgres
```
También tendrás que borrar el siguiente directorio para que no de conflictos cuando intentes volver a instalarlo (fuente: [gist.github.com/lxneng/741932](https://gist.github.com/lxneng/741932)):
```
rm -rf /usr/local/var/postgres
```
## Postgis
```
brew install postgis
```
## Ghostscript
```
brew install ghostscript
```
## PhantomJS
```
brew install phantomjs
```
## Clonar el repositorio
Ahora que ya tenemos todas las dependencias instalado podemos bajarnos el proyecto:
```
git clone https://github.com/consul/consul.git
cd consul
bundle install
cp config/database.yml.example config/database.yml
cp config/secrets.yml.example config/secrets.yml
```
Ahora copia en `database.yml` el usuario y la contraseña que pusiste para *consul*. Cuando ya lo hayas hecho:
```
rake db:create
rake db:setup
rake db:dev_seed
RAILS_ENV=test bin/rake db:setup
```
Para ejecutar los tests:
```
bundle exec rspec
```

View File

@@ -163,6 +163,10 @@ FactoryGirl.define do
end
end
trait :archived do
created_at 25.months.ago
end
trait :with_hot_score do
before(:save) { |d| d.calculate_hot_score }
end

View File

@@ -50,7 +50,7 @@ feature 'Official positions' do
@proposal1 = create(:proposal, author: @user1)
@proposal2 = create(:proposal, author: @user2)
featured_proposals = 3.times { create(:proposal) }
create_featured_proposals
end
scenario "Index" do

View File

@@ -0,0 +1,57 @@
# coding: utf-8
require 'rails_helper'
feature 'Proposal ballots' do
scenario 'Banner shows in proposal index' do
create_featured_proposals
visit proposals_path
expect(page).to_not have_css("#next-voting")
expect(page).to have_css("#featured-proposals")
create_successfull_proposals
visit proposals_path
expect(page).to have_css("#next-voting")
expect(page).to_not have_css("#featured-proposals")
end
scenario 'Successfull proposals do not show support buttons in index' do
successfull_proposals = create_successfull_proposals
visit proposals_path
successfull_proposals.each do |proposal|
within("#proposal_#{proposal.id}_votes") do
expect(page).to_not have_css(".supports")
expect(page).to have_content "This proposal has reached the required supports"
end
end
end
scenario 'Successfull proposals do not show support buttons in show' do
successfull_proposals = create_successfull_proposals
successfull_proposals.each do |proposal|
visit proposal_path(proposal)
within("#proposal_#{proposal.id}_votes") do
expect(page).to_not have_css(".supports")
expect(page).to have_content "This proposal has reached the required supports"
end
end
end
scenario 'Successfull proposals are listed in the proposal ballots index' do
successfull_proposals = create_successfull_proposals
visit proposal_ballots_path
successfull_proposals.each do |proposal|
expect(page).to have_content(proposal.title)
end
end
end

View File

@@ -3,48 +3,50 @@ require 'rails_helper'
feature 'Proposals' do
scenario 'Index' do
featured_proposals = create_featured_proposals
proposals = [create(:proposal), create(:proposal), create(:proposal)]
context 'Index' do
scenario 'Lists featured and regular proposals' do
featured_proposals = create_featured_proposals
proposals = [create(:proposal), create(:proposal), create(:proposal)]
visit proposals_path
visit proposals_path
expect(page).to have_selector('#proposals .proposal-featured', count: 3)
featured_proposals.each do |featured_proposal|
within('#featured-proposals') do
expect(page).to have_content featured_proposal.title
expect(page).to have_css("a[href='#{proposal_path(featured_proposal)}']")
expect(page).to have_selector('#proposals .proposal-featured', count: 3)
featured_proposals.each do |featured_proposal|
within('#featured-proposals') do
expect(page).to have_content featured_proposal.title
expect(page).to have_css("a[href='#{proposal_path(featured_proposal)}']")
end
end
expect(page).to have_selector('#proposals .proposal', count: 3)
proposals.each do |proposal|
within('#proposals') do
expect(page).to have_content proposal.title
expect(page).to have_css("a[href='#{proposal_path(proposal)}']", text: proposal.title)
expect(page).to have_css("a[href='#{proposal_path(proposal)}']", text: proposal.summary)
end
end
end
expect(page).to have_selector('#proposals .proposal', count: 3)
proposals.each do |proposal|
within('#proposals') do
expect(page).to have_content proposal.title
expect(page).to have_css("a[href='#{proposal_path(proposal)}']", text: proposal.title)
expect(page).to have_css("a[href='#{proposal_path(proposal)}']", text: proposal.summary)
scenario 'Pagination' do
per_page = Kaminari.config.default_per_page
(per_page + 5).times { create(:proposal) }
visit proposals_path
expect(page).to have_selector('#proposals .proposal', count: per_page)
within("ul.pagination") do
expect(page).to have_content("1")
expect(page).to have_content("2")
expect(page).to_not have_content("3")
click_link "Next", exact: false
end
expect(page).to have_selector('#proposals .proposal', count: 2)
end
end
scenario 'Paginated Index' do
per_page = Kaminari.config.default_per_page
(per_page + 5).times { create(:proposal) }
visit proposals_path
expect(page).to have_selector('#proposals .proposal', count: per_page)
within("ul.pagination") do
expect(page).to have_content("1")
expect(page).to have_content("2")
expect(page).to_not have_content("3")
click_link "Next", exact: false
end
expect(page).to have_selector('#proposals .proposal', count: 2)
end
scenario 'Show' do
proposal = create(:proposal)
@@ -676,6 +678,110 @@ feature 'Proposals' do
end
end
feature 'Archived proposals' do
scenario 'show on archived tab' do
create_featured_proposals
archived_proposals = create_archived_proposals
visit proposals_path
click_link 'Archived'
within("#proposals-list") do
archived_proposals.each do |proposal|
expect(page).to have_content(proposal.title)
end
end
end
scenario 'do not show in other index tabs' do
create_featured_proposals
archived_proposal = create(:proposal, :archived)
visit proposals_path
within("#proposals-list") do
expect(page).to_not have_content archived_proposal.title
end
orders = %w{hot_score confidence_score created_at relevance}
orders.each do |order|
visit proposals_path(order: order)
within("#proposals-list") do
expect(page).to_not have_content archived_proposal.title
end
end
end
scenario 'do not show support buttons in index' do
create_featured_proposals
archived_proposals = create_archived_proposals
visit proposals_path(order: 'archival_date')
within("#proposals-list") do
archived_proposals.each do |proposal|
within("#proposal_#{proposal.id}_votes") do
expect(page).to_not have_css(".supports")
expect(page).to have_content "This proposal has been archived and can't collect supports"
end
end
end
end
scenario 'do not show support buttons in show' do
archived_proposal = create(:proposal, :archived)
visit proposal_path(archived_proposal)
expect(page).to_not have_css(".supports")
expect(page).to have_content "This proposal has been archived and can't collect supports"
end
scenario 'do not show in featured proposals section' do
featured_proposal = create(:proposal, :with_confidence_score, cached_votes_up: 100)
archived_proposal = create(:proposal, :archived, :with_confidence_score, cached_votes_up: 10000)
visit proposals_path
within("#featured-proposals") do
expect(page).to have_content(featured_proposal.title)
expect(page).to_not have_content(archived_proposal.title)
end
within("#proposals-list") do
expect(page).to_not have_content(featured_proposal.title)
expect(page).to_not have_content(archived_proposal.title)
end
click_link "Archived"
within("#featured-proposals") do
expect(page).to have_content(featured_proposal.title)
expect(page).to_not have_content(archived_proposal.title)
end
within("#proposals-list") do
expect(page).to_not have_content(featured_proposal.title)
expect(page).to have_content(archived_proposal.title)
end
end
scenario "Order by votes" do
create(:proposal, :archived, title: "Least voted").update_column(:confidence_score, 10)
create(:proposal, :archived, title: "Most voted").update_column(:confidence_score, 50)
create(:proposal, :archived, title: "Some votes").update_column(:confidence_score, 25)
visit proposals_path
click_link 'Archived'
within("#proposals-list") do
expect(all(".proposal")[0].text).to match "Most voted"
expect(all(".proposal")[1].text).to match "Some votes"
expect(all(".proposal")[2].text).to match "Least voted"
end
end
end
context "Search" do
context "Basic search" do

View File

@@ -204,6 +204,13 @@ describe Proposal do
expect {proposal.register_vote(user, 'yes')}.to change{proposal.reload.votes_for.size}.by(0)
end
end
it "should not register vote for archived proposals" do
user = create(:user, verified_at: Time.now)
archived_proposal = create(:proposal, :archived)
expect {archived_proposal.register_vote(user, 'yes')}.to change{proposal.reload.votes_for.size}.by(0)
end
end
describe '#cached_votes_up' do
@@ -811,4 +818,30 @@ describe Proposal do
end
end
describe "archived" do
before(:each) do
@new_proposal = create(:proposal)
@archived_proposal = create(:proposal, :archived)
end
it "archived? is true only for proposals created more than n (configured months) ago" do
expect(@new_proposal.archived?).to eq false
expect(@archived_proposal.archived?).to eq true
end
it "scope archived" do
archived = Proposal.archived
expect(archived.size).to eq(1)
expect(archived.first).to eq(@archived_proposal)
end
it "scope archived" do
not_archived = Proposal.not_archived
expect(not_archived.size).to eq(1)
expect(not_archived.first).to eq(@new_proposal)
end
end
end

View File

@@ -192,6 +192,16 @@ module CommonActions
create(:debate, :with_confidence_score, cached_votes_up: 80)]
end
def create_successfull_proposals
[create(:proposal, title: "Winter is coming", question: "Do you speak it?", cached_votes_up: Proposal.votes_needed_for_success + 100),
create(:proposal, title: "Fire and blood", question: "You talking to me?", cached_votes_up: Proposal.votes_needed_for_success + 1)]
end
def create_archived_proposals
[create(:proposal, title: "This is an expired proposal", created_at: Setting["months_to_archive_proposals"].to_i.months.ago),
create(:proposal, title: "This is an oldest expired proposal", created_at: (Setting["months_to_archive_proposals"].to_i + 2).months.ago)]
end
def tag_names(tag_cloud)
tag_cloud.tags.map(&:name)
end