Merge pull request #487 from AyuntamientoMadrid/proposals

Proposals
This commit is contained in:
Juanjo Bazán
2015-09-14 22:00:43 +02:00
140 changed files with 5184 additions and 592 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -0,0 +1,8 @@
App.ModeratorProposals =
add_class_faded: (id) ->
$("##{id}").addClass("faded")
$("#comments").addClass("faded")
hide_moderator_actions: (id) ->
$("##{id} .js-moderator-proposals-actions:first").hide()

View File

@@ -1,26 +1,32 @@
App.PreventDoubleSubmission =
disable_button: (button) ->
unless button.hasClass('disabled')
loading = button.data('loading') ? '...'
button.addClass('disabled').attr('disabled', 'disabled')
button.data('text', button.val())
button.val(loading)
disable_buttons: (buttons) ->
setTimeout ->
buttons.each ->
button = $(this)
unless button.hasClass('disabled')
loading = button.data('loading') ? '...'
button.addClass('disabled').attr('disabled', 'disabled')
button.data('text', button.val())
button.val(loading)
, 1
reset_button: (button) ->
if button.hasClass('disabled')
button_text = button.data('text')
button.removeClass('disabled').attr('disabled', null)
if button_text
button.val(button_text)
button.data('text', null)
reset_buttons: (buttons) ->
buttons.each ->
button = $(this)
if button.hasClass('disabled')
button_text = button.data('text')
button.removeClass('disabled').attr('disabled', null)
if button_text
button.val(button_text)
button.data('text', null)
initialize: ->
$('form').on('submit', event, ->
button = $(this).find(':button, :submit')
App.PreventDoubleSubmission.disable_button(button)
buttons = $(this).find(':button, :submit')
App.PreventDoubleSubmission.disable_buttons(buttons)
).on('ajax:success', ->
button = $(this).find(':button, :submit')
App.PreventDoubleSubmission.reset_button(button)
buttons = $(this).find(':button, :submit')
App.PreventDoubleSubmission.reset_buttons(buttons)
)
false

View File

@@ -1,7 +1,7 @@
App.Tags =
initialize: ->
$tag_input = $('input#debate_tag_list')
$tag_input = $('input.js-tag-list')
$('body .js-add-tag-link').each ->
$this = $(this)

View File

@@ -12,8 +12,5 @@ App.Votes =
initialize: ->
App.Votes.hoverize votes for votes in $("div.votes")
App.Votes.hoverize votes for votes in $("div.supports")
false

View File

@@ -10,4 +10,5 @@
@import "admin";
@import "participacion";
@import "debates";
@import "proposals";
@import "c3";

View File

@@ -352,7 +352,7 @@
@media (min-width: $small-breakpoint) {
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
margin: 0 rem-calc(-24) 0 rem-calc(12);
margin: 0 rem-calc(-25) 0 rem-calc(12);
}
&:after {
@@ -487,13 +487,16 @@
font-weight: bold;
}
h3 {
border-top: 2px solid $brand;
display: inline-block;
font-size: rem-calc(16);
margin: -1px 0 rem-calc(12);
padding-top: rem-calc(6);
text-transform: uppercase;
aside {
h3 {
border-top: 2px solid $brand;
display: inline-block;
font-size: rem-calc(16);
margin: -1px 0 rem-calc(12);
padding-top: rem-calc(6);
text-transform: uppercase;
}
}
.votes {
@@ -636,7 +639,7 @@
.comment-votes {
color: $text-medium;
font-weight: lighter;
margin: rem-calc(15) rem-calc(6) 0;
margin: rem-calc(8) rem-calc(12) rem-calc(6) 0;
a {
color: $text-light;
@@ -650,7 +653,7 @@
[class^="icon-"] {
font-size: rem-calc(20);
vertical-align: top;
vertical-align: middle;
}
}
@@ -676,6 +679,7 @@
.comment-user {
margin-top: rem-calc(6);
padding: rem-calc(6) 0;
overflow: hidden;
@each $n in ("1", "2", "3","4", "5") {
&.level-#{$n} {
@@ -757,6 +761,7 @@
.button {
background: none;
margin-bottom: 0;
padding: 0;
}
}

View File

@@ -85,7 +85,7 @@
.icon-stats:before {
content: "r";
}
.icon-initiatives:before {
.icon-proposals:before {
content: "h";
}
.icon-organizations:before {

View File

@@ -329,7 +329,7 @@ header {
@media (min-width: $small-breakpoint) {
display: inline-block;
margin-bottom: 0;
margin-left: rem-calc(24);
margin-left: rem-calc(12);
}
&:hover {
@@ -656,7 +656,7 @@ header {
@media (min-width: $small-breakpoint) {
line-height: $line-height*3;
margin-left: rem-calc(12);
margin-right: rem-calc(72);
margin-right: rem-calc(36);
}
&:after {
@@ -772,7 +772,7 @@ footer {
@extend .tags;
h3 {
border-top: 1px solid $votes-border;
border-top: 2px solid $brand;
display: inline-block;
font-family: $font-family-sans-serif;
font-size: rem-calc(16);
@@ -925,6 +925,18 @@ form {
margin-bottom: rem-calc(12);
}
.note-marked {
@extend .note;
background: yellow;
display: inline-block;
em {
background: white;
display: inline-block;
padding-left: rem-calc(6);
}
}
.ckeditor {
min-height: rem-calc(312);
}
@@ -1403,6 +1415,7 @@ table {
li {
font-size: rem-calc(15);
line-height: rem-calc(30);
margin-bottom: rem-calc(12);
}
}

View File

@@ -0,0 +1,733 @@
// Table of Contents
//
// 01. Debates
// 02. Index
// 02.1. Featured
// 02.2. List
// 03. Show
// 04. New
// 05. Comments
// 06. Flags
//
// 01. Proposals
// - - - - - - - - - - - - - - - - - - - - - - - - -
.button-proposal {
background: $proposals;
&:hover {
background: $proposals-border;
}
}
@mixin supports {
background: $proposals;
border-top: 1px solid $proposals-border;
margin: 0 rem-calc(-12);
padding: rem-calc(14) rem-calc(12);
position: relative;
.progress {
background-color: rgba(255,255,255,.8);
height: rem-calc(12);
margin-bottom: rem-calc(6);
margin-top: rem-calc(4);
.meter {
background: $votes-like;
}
}
abbr {
color: white;
&[title] {
border-bottom: 1px dotted white;
}
}
.button-support {
background: white;
color: $proposals;
display: inline-block;
font-size: rem-calc(14);
margin-top: rem-calc(12);
&:hover {
background: $proposals-border;
color: white;
cursor: pointer;
}
&:active {
opacity: .75;
}
}
.total-supports {
color: white;
text-align: center;
font-size: rem-calc(14);
span {
display: block;
font-size: rem-calc(11);
opacity: .75;
}
}
.divider {
margin: 0 rem-calc(6);
}
.not-logged {
background: rgba(255,164,45,.9);
color: white;
height: 100%;
left: 0;
line-height: $line-height*2;
padding-top: rem-calc(12);
position: absolute;
text-align: center;
top: 0;
width: 100%;
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#222222', endColorstr='#222222'); /* IE */
a {
color: white;
text-decoration: underline;
}
}
.anonymous-votes, .organizations-votes {
background: $warning-bg;
color: $warning-color;
height: 100%;
left: 0;
line-height: $line-height;
padding-top: rem-calc(12);
position: absolute;
text-align: center;
top: 0;
width: 100%;
p {
color: $warning-color;
margin: 0 rem-calc(12);
text-align: left;
}
a {
color: $warning-color;
font-weight: bold;
text-decoration: underline;
}
}
.supported {
color: white;
margin-top: rem-calc(12);
}
}
// 02. Index
// - - - - - - - - - - - - - - - - - - - - - - - - -
// 02.1. Featured
// - - - - - - - - - - - - -
.proposal-featured {
.panel {
background: white;
border: 1px solid;
border-color: #e5e6e9 #dfe0e4 #d0d1d5;
border-radius: rem-calc(3);
margin-bottom: rem-calc(24);
padding: rem-calc(24) rem-calc(12) 0 rem-calc(12);
.proposal-content {
min-height: rem-calc(353);
}
.label {
background: none;
clear: both;
color: $proposals;
display: block;
font-weight: bold;
text-transform: uppercase;
padding-left: 0;
padding-top: 0;
}
.icon-proposals {
color: $proposals;
font-size: rem-calc(36);
line-height: $line-height;
position: absolute;
right: rem-calc(18);
top: rem-calc(12);
}
h3 {
font-weight: bold;
margin: rem-calc(8) 0 0 0;
min-height: rem-calc(65);
a {
clear: both;
color: $text;
display: block;
font-size: rem-calc(16);
line-height: $line-height;
text-transform: lowercase;
&:first-letter {
text-transform: uppercase;
}
}
}
.proposal-info {
color: $text-medium;
font-weight: lighter;
margin-bottom: 0;
.icon-comments {
font-size: rem-calc(16);
vertical-align: top;
}
a {
color: $text-medium;
}
}
.proposal-description {
color: $text;
font-size: rem-calc(13);
height: rem-calc(156);
line-height: $line-height;
margin-bottom: rem-calc(12);
margin-top: rem-calc(24);
overflow: hidden;
position: relative;
a {
color: $text;
}
ul, ol {
li {
font-size: rem-calc(13);
margin-bottom: rem-calc(12);
}
}
}
.truncate {
background: image-url('truncate.png');
background-repeat: repeat-x;
bottom: 0;
height: 24px;
position: absolute;
width: 100%;
}
p {
color: $text;
font-size: rem-calc(14);
line-height: $line-height;
margin-bottom: rem-calc(12);
&.debate-info {
font-size: rem-calc(13);
}
}
}
.supports {
@include supports;
}
}
// 02.2. List
// - - - - - - - - - - - - -
.proposals-list {
@media (min-width: $small-breakpoint) {
margin-bottom: rem-calc(48);
}
}
.proposal {
@extend .proposal-featured;
margin-bottom: 0;
margin-top: 0;
.panel {
border-radius: 0;
box-shadow: 0px 1px 3px 0 $border;
margin-bottom: rem-calc(12);
min-height: rem-calc(192);
padding-top: rem-calc(12);
@media (min-width: $small-breakpoint) {
margin-bottom: rem-calc(-1);
padding-bottom: rem-calc(12);
}
.label {
line-height: $line-height;
padding-bottom: 0;
}
h3 {
margin-top: 0;
min-height: rem-calc(48);
}
.proposal-content {
margin: 0;
min-height: rem-calc(180);
.tags {
display: block;
}
}
.icon-proposals {
font-size: rem-calc(18);
left: rem-calc(88);
top: 0;
}
.proposal-description {
height: rem-calc(72);
margin-top: 0;
}
}
.supports {
border: 1px solid $proposals-border;
margin: 0 rem-calc(-12);
@media (min-width: $small-breakpoint) {
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
margin: 0 rem-calc(-25) 0 rem-calc(12);
}
&:after {
content: none;
position: absolute;
display: block;
border-style: solid;
border-color: #664212 transparent transparent transparent;
bottom: rem-calc(-14);
border-left-width: 0;
border-right-color: transparent;
right: rem-calc(-1);
border-width: 1em 1em 0 0;
@media (min-width: $small-breakpoint) {
content: "";
}
}
.total-supports {
display: inline-block;
line-height: $line-height;
padding-top: rem-calc(12);
vertical-align: top;
@media (min-width: $small-breakpoint) {
display: block;
float: none;
margin-left: 0;
padding-top: 0;
}
}
.not-logged {
line-height: $line-height;
padding-top: rem-calc(24);
}
.anonymous-votes, .organizations-votes {
padding-top: rem-calc(24);
}
.divider {
display: none;
}
@media (min-width: $medium-breakpoint) {
.divider {
display: inline-block;
}
}
}
}
// 03. Show
// - - - - - - - - - - - - - - - - - - - - - - - - -
.proposal-show {
padding-top: rem-calc(12);
.back {
@include back;
}
.icon-angle-left {
@extend .back;
}
h1 {
clear: both;
font-size: rem-calc(30);
font-weight: bold;
margin: 0;
text-transform: lowercase;
&:first-letter {
text-transform: uppercase;
}
}
.edit-proposal {
margin-bottom: 0;
}
.proposal-info {
clear: both;
color: $text-medium;
font-weight: lighter;
line-height: $line-height*2;
text-align: justify;
a {
color: $text-medium;
}
p {
font-size: rem-calc(15);
line-height: $line-height;
margin-bottom: 0;
}
}
ul, ol {
li {
font-size: rem-calc(13);
margin-bottom: rem-calc(12);
}
}
.author-photo {
line-height: $line-height*2;
margin-right: rem-calc(6);
vertical-align: middle;
width: 32px;
}
.author {
color: $text;
font-weight: bold;
}
aside {
h3 {
border-top: 2px solid $brand;
display: inline-block;
font-size: rem-calc(16);
margin: -1px 0 rem-calc(12);
padding-top: rem-calc(6);
text-transform: uppercase;
}
}
.supports {
@include supports;
border: 0;
border-radius: 0;
margin: 0;
.total-supports {
display: block;
float: none;
line-height: $line-height;
}
.not-logged {
line-height: $line-height;
padding: rem-calc(24);
}
@media (min-width: $small-breakpoint + em-calc(1)) and (max-width:$medium-breakpoint) {
.in-favor, .against {
text-align: left;
width: rem-calc(100);
}
}
.divider {
display: none;
}
@media (min-width: $medium-breakpoint) {
.divider {
display: inline-block;
}
}
}
.leave-comment {
display: inline-block;
margin-top: rem-calc(24);
}
.tags {
display: block;
margin: rem-calc(24) 0;
a {
margin-right: rem-calc(6);
}
}
}
.bullet {
color: $border;
}
// 04. New
// - - - - - - - - - - - - - - - - - - - - - - - - -
.proposal-new {
background: white;
padding-top: rem-calc(24);
.back {
@include back;
}
h1 {
clear: both;
font-size: rem-calc(36);
font-weight: bold;
line-height: $line-height*2;
margin-bottom: rem-calc(24);
}
.icon-proposals {
color: $proposals;
font-size: rem-calc(50);
line-height: $line-height;
opacity: .5;
}
h2 {
clear: both;
font-size: rem-calc(20);
font-weight: bold;
line-height: $line-height;
margin: 0;
}
.recommendations {
list-style-type: none;
margin-left: 0;
margin-top: rem-calc(24);
li {
font-size: rem-calc(12);
margin: rem-calc(12) 0;
&:before {
color: $proposals;
content: "l ";
font-family: "icons" !important;
}
}
}
}
.proposal-edit {
@extend .proposal-new;
}
// 05. Comments
// - - - - - - - - - - - - - - - - - - - - - - - - -
.comments {
background: $white;
background-repeat: repeat-x;
padding-top: rem-calc(24);
padding-bottom: rem-calc(96);
h2 {
margin: 0;
font-weight: bold;
span {
font-size: rem-calc(18);
font-weight: normal;
opacity: .8;
}
}
.comment {
margin: rem-calc(6) 0;
p {
margin-bottom: 0;
}
.comment-votes {
color: $text-medium;
font-weight: lighter;
margin: rem-calc(8) rem-calc(12) rem-calc(6) 0;
a {
color: $text-light;
display: inline-block;
vertical-align: top;
&:hover {
color: $text-medium;
}
}
[class^="icon-"] {
font-size: rem-calc(20);
vertical-align: middle;
}
}
.comment-body {
margin-left: rem-calc(42);
p {
font-size: rem-calc(14);
}
.reply {
background: white;
border: 1px solid $border;
font-size: rem-calc(12);
margin: rem-calc(6) 0;
padding: rem-calc(6);
.divider {
color: $text-light;
}
}
.comment-user {
margin-top: rem-calc(6);
padding: rem-calc(6) 0;
overflow: hidden;
@each $n in ("1", "2", "3","4", "5") {
&.level-#{$n} {
@if $n == "5" {
background: $comment-level-5;
padding: rem-calc(6) rem-calc(12);
}
@elseif $n == "1" {
background: none;
padding: rem-calc(6) rem-calc(12);
}
@else {
background: $comment-official;
padding: rem-calc(6) rem-calc(12);
}
}
}
&.is-author {
background: $comment-author;
padding: rem-calc(6) rem-calc(12);
}
&.is-admin {
background: $comment-admin;
padding: rem-calc(6) rem-calc(12);
}
&.is-moderator {
@extend .is-admin;
}
&.level-5 {
background: $comment-level-5;
padding: rem-calc(6) rem-calc(12);
}
}
}
.is-deleted {
background: $deleted;
margin-left: rem-calc(42);
padding: rem-calc(6) rem-calc(12);
}
.comment-children {
border-left: 1px dashed $border;
margin-left: rem-calc(42);
padding-left: rem-calc(6);
@media only screen and (max-width: 40em) {
margin-left: rem-calc(16);
}
}
.comment-info {
color: $text-light;
font-size: rem-calc(13);
font-weight: lighter;
margin-top: rem-calc(6);
vertical-align: middle;
span.user-name {
color: $text;
font-weight: bold;
}
}
}
}
.faded {
opacity: 0.4;
}
// 06. Flags
// - - - - - - - - - - - - - - - - - - - - - - - - -
.flag-content {
.button {
background: none;
margin-bottom: 0;
padding: 0;
}
}
.flag-disable {
color: $text-medium;
line-height: rem-calc(24);
vertical-align: middle;
}
.flag-active {
@extend .flag-disable;
color: $delete;
}

View File

@@ -41,6 +41,9 @@ $votes-unlike-act: #BD6A6A;
$delete: #F04124;
$check: #46DB91;
$proposals: #FFA42D;
$proposals-border: #CC8425;
// 03. Forms
// - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@@ -23,9 +23,9 @@ class AccountController < ApplicationController
def account_params
if @account.organization?
params.require(:account).permit(:phone_number, :email_on_debate_comment, :email_on_comment_reply, organization_attributes: [:name, :responsible_name])
params.require(:account).permit(:phone_number, :email_on_comment, :email_on_comment_reply, organization_attributes: [:name, :responsible_name])
else
params.require(:account).permit(:username, :email_on_debate_comment, :email_on_comment_reply)
params.require(:account).permit(:username, :email_on_comment, :email_on_comment_reply)
end
end

View File

@@ -0,0 +1,26 @@
class Admin::ProposalsController < Admin::BaseController
has_filters %w{without_confirmed_hide all with_confirmed_hide}, only: :index
before_action :load_proposal, only: [:confirm_hide, :restore]
def index
@proposals = Proposal.only_hidden.send(@current_filter).order(hidden_at: :desc).page(params[:page])
end
def confirm_hide
@proposal.confirm_hide
redirect_to request.query_parameters.merge(action: :index)
end
def restore
@proposal.restore
redirect_to request.query_parameters.merge(action: :index)
end
private
def load_proposal
@proposal = Proposal.with_hidden.find(params[:id])
end
end

View File

@@ -3,6 +3,7 @@ require "application_responder"
class ApplicationController < ActionController::Base
include SimpleCaptcha::ControllerHelpers
include HasFilters
include HasOrders
before_action :authenticate_http_basic, if: :http_basic_auth_site?
before_action :authenticate_user!, unless: :devise_controller?, if: :beta_site?
@@ -78,6 +79,10 @@ class ApplicationController < ActionController::Base
@debate_votes = current_user ? current_user.debate_votes(debates) : {}
end
def set_proposal_votes(proposals)
@proposal_votes = current_user ? current_user.proposal_votes(proposals) : {}
end
def set_comment_flags(comments)
@comment_flags = current_user ? current_user.comment_flags(comments) : {}
end

View File

@@ -55,11 +55,11 @@ class CommentsController < ApplicationController
end
def administrator_comment?
["1", true].include?(comment_params[:as_administrator]) && can?(:comment_as_administrator, Debate)
["1", true].include?(comment_params[:as_administrator]) && can?(:comment_as_administrator, @commentable)
end
def moderator_comment?
["1", true].include?(comment_params[:as_moderator]) && can?(:comment_as_moderator, Debate)
["1", true].include?(comment_params[:as_moderator]) && can?(:comment_as_moderator, @commentable)
end
end

View File

@@ -0,0 +1,12 @@
module HasOrders
extend ActiveSupport::Concern
class_methods do
def has_orders(valid_orders, *args)
before_action(*args) do
@valid_orders = valid_orders
@current_order = @valid_orders.include?(params[:order]) ? params[:order] : @valid_orders.first
end
end
end
end

View File

@@ -1,16 +1,17 @@
class DebatesController < 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]
has_orders %w{confidence_score hot_score created_at most_commented random}, only: :index
load_and_authorize_resource
respond_to :html, :js
def index
@debates = @search_terms.present? ? Debate.search(@search_terms) : Debate.all
@debates = @debates.tagged_with(@tag_filter) if @tag_filter
@debates = @debates.page(params[:page]).for_render.send("sort_by_#{@order}")
@debates = @debates.page(params[:page]).for_render.send("sort_by_#{@current_order}")
@tag_cloud = Debate.tag_counts.order(taggings_count: :desc, name: :asc).limit(20)
set_debate_votes(@debates)
end
@@ -82,11 +83,6 @@ class DebatesController < ApplicationController
@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?

View File

@@ -0,0 +1,44 @@
class Moderation::ProposalsController < Moderation::BaseController
has_filters %w{pending_flag_review all with_ignored_flag}, only: :index
has_orders %w{created_at flags}, only: :index
before_filter :load_proposals, only: [:index, :moderate]
load_and_authorize_resource
def index
@proposals = @proposals.send(@current_filter)
.send("sort_by_#{@current_order}")
.page(params[:page])
.per(50)
end
def hide
@proposal.hide
end
def moderate
@proposals = @proposals.where(id: params[:proposal_ids])
if params[:hide_proposals].present?
@proposals.accessible_by(current_ability, :hide).each(&:hide)
elsif params[:ignore_flags].present?
@proposals.accessible_by(current_ability, :ignore_flag).each(&:ignore_flag)
elsif params[:block_authors].present?
author_ids = @proposals.pluck(:author_id).uniq
User.where(id: author_ids).accessible_by(current_ability, :block).each(&:block)
end
redirect_to request.query_parameters.merge(action: :index)
end
private
def load_proposals
@proposals = Proposal.accessible_by(current_ability, :moderate)
end
end

View File

@@ -34,6 +34,15 @@ class PagesController < ApplicationController
def transparency
end
def proposals_info
end
def participation_facts
end
def participation_world
end
def blog
redirect_to "http://diario.madrid.es/blog/category/gobiernoabierto/"
end

View File

@@ -0,0 +1,94 @@
class ProposalsController < ApplicationController
before_action :parse_tag_filter, only: :index
before_action :parse_search_terms, only: :index
before_action :authenticate_user!, except: [:index, :show]
has_orders %w{confidence_score hot_score created_at most_commented random}, only: :index
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_#{@current_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
@root_comments = @proposal.comments.roots.recent.page(params[:page]).per(10).for_render
@comments = @root_comments.inject([]){|all, root| all + Comment.descendants_of(root).for_render}
@all_visible_comments = @root_comments + @comments
set_comment_flags(@all_visible_comments)
end
def new
@proposal = Proposal.new
load_featured_tags
end
def create
@proposal = Proposal.new(proposal_params)
@proposal.author = current_user
if @proposal.save_with_captcha
ahoy.track :proposal_created, proposal_id: @proposal.id
redirect_to @proposal, notice: t('flash.actions.create.notice', resource_name: 'Proposal')
else
load_featured_tags
render :new
end
end
def edit
load_featured_tags
end
def update
@proposal.assign_attributes(proposal_params)
if @proposal.save_with_captcha
redirect_to @proposal, notice: t('flash.actions.update.notice', resource_name: 'Proposal')
else
load_featured_tags
render :edit
end
end
def flag
Flag.flag(current_user, @proposal)
respond_with @proposal, template: 'proposals/_refresh_flag_actions'
end
def unflag
Flag.unflag(current_user, @proposal)
respond_with @proposal, template: 'proposals/_refresh_flag_actions'
end
def vote
@proposal.register_vote(current_user, 'yes')
set_proposal_votes(@proposal)
end
private
def proposal_params
params.require(:proposal).permit(:title, :question, :summary, :description, :external_url, :video_url, :responsible_name, :tag_list, :terms_of_service, :captcha, :captcha_key)
end
def load_featured_tags
@featured_tags = ActsAsTaggableOn::Tag.where(featured: true)
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

View File

@@ -13,7 +13,7 @@ class Verification::LetterController < ApplicationController
def create
@letter = Verification::Letter.new(user: current_user)
if @letter.save
redirect_to edit_letter_path, notice: t('verification.letter.create.flash.success')
redirect_to edit_letter_path
else
flash.now.alert = t('verification.letter.create.alert.failure')
render :new

View File

@@ -4,11 +4,26 @@ class WelcomeController < ApplicationController
layout "devise", only: :welcome
def index
@featured_debates = Debate.sort_by_confidence_score.limit(3).for_render
set_debate_votes(@featured_debates)
if current_user
redirect_to :proposals
end
end
def welcome
end
def highlights
debates = Debate.sort_by_hot_score.page(params[:page]).per(10).for_render
set_debate_votes(debates)
proposals = Proposal.sort_by_hot_score.page(params[:page]).per(10).for_render
set_proposal_votes(proposals)
@list = (debates.to_a + proposals.to_a).sort{|a, b| b.hot_score <=> a.hot_score}
@paginator = debates.total_pages > proposals.total_pages ? debates : proposals
render 'highlights'
end
end

View File

@@ -6,6 +6,7 @@ module ApplicationHelper
end
def home_page?
return false if user_signed_in?
# Using path because fullpath yields false negatives since it contains
# parameters too
request.path == '/'

View File

@@ -10,7 +10,7 @@ module CacheKeysHelper
if user_signed_in?
user_status += ":signed"
user_status += ":verified" if current_user.verified_at.present?
user_status += ":verified" if current_user.level_two_or_three_verified?
user_status += ":org" if current_user.organization?
user_status += ":admin" if current_user.administrator?
user_status += ":moderator" if current_user.moderator?

View File

@@ -0,0 +1,21 @@
module ProposalsHelper
def progress_bar_percentage(proposal)
case proposal.cached_votes_up
when 0 then 0
when 1..Proposal.votes_needed_for_success then (proposal.cached_votes_up.to_f * 100 / Proposal.votes_needed_for_success).floor
else 100
end
end
def supports_percentage(proposal)
percentage = (proposal.cached_votes_up.to_f * 100 / Proposal.votes_needed_for_success)
case percentage
when 0 then "0%"
when 0..(0.1) then "0.1%"
when (0.1)..100 then number_to_percentage(percentage, strip_insignificant_zeros: true, precision: 1)
else "100%"
end
end
end

View File

@@ -0,0 +1,14 @@
module TagsHelper
def taggable_path(taggable, tag_name)
case taggable
when 'debate'
debates_path(tag: tag_name)
when 'proposal'
proposals_path(tag: tag_name)
else
'#'
end
end
end

View File

@@ -6,4 +6,9 @@ module TextWithLinksHelper
Rinku.auto_link(sanitized, :all, 'target="_blank" rel="nofollow"').html_safe
end
def safe_html_with_links(html)
return html unless html.html_safe?
Rinku.auto_link(html, :all, 'target="_blank" rel="nofollow"').html_safe
end
end

View File

@@ -1,7 +1,7 @@
module VotesHelper
def css_classes_for_debate_vote(debate_votes, debate)
case debate_votes[debate.id]
def css_classes_for_vote(votes, votable)
case votes[votable.id]
when true
{in_favor: "voted", against: "no-voted"}
when false
@@ -11,4 +11,8 @@ module VotesHelper
end
end
def voted_for?(votes, votable)
votes[votable.id]
end
end

View File

@@ -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
@@ -19,8 +19,14 @@ class Ability
debate.editable_by?(user)
end
can :read, Proposal
can :update, Proposal do |proposal|
proposal.editable_by?(user)
end
can :create, Comment
can :create, Debate
can :create, Proposal
can [:flag, :unflag], Comment
cannot [:flag, :unflag], Comment, user_id: user.id
@@ -28,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_or_three_verified?
can :vote, Proposal
end
if user.moderator? || user.administrator?
can :read, Organization
can(:verify, Organization){ |o| !o.verified? }
@@ -52,12 +65,24 @@ 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 :moderate, Proposal
cannot :moderate, Proposal, author_id: user.id
can :hide, User
cannot :hide, User, id: user.id
can :block, User
cannot :block, 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?
@@ -67,6 +92,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
@@ -76,10 +104,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

View File

@@ -1,4 +1,5 @@
class Comment < ActiveRecord::Base
include Flaggable
acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases
@@ -9,21 +10,16 @@ class Comment < ActiveRecord::Base
validates :body, presence: true
validates :user, presence: true
validates_inclusion_of :commentable_type, in: ["Debate"]
validates_inclusion_of :commentable_type, in: ["Debate", "Proposal"]
validate :validate_body_length
belongs_to :commentable, -> { with_hidden }, polymorphic: true, counter_cache: true
belongs_to :user, -> { with_hidden }
has_many :flags, as: :flaggable
scope :recent, -> { order(id: :desc) }
scope :sort_for_moderation, -> { order(flags_count: :desc, updated_at: :desc) }
scope :pending_flag_review, -> { where(ignored_flag_at: nil, hidden_at: nil) }
scope :with_ignored_flag, -> { where(hidden_at: nil).where.not(ignored_flag_at: nil) }
scope :flagged, -> { where("flags_count > 0") }
scope :for_render, -> { with_hidden.includes(user: :organization) }
@@ -68,14 +64,6 @@ class Comment < ActiveRecord::Base
cached_votes_down
end
def ignored_flag?
ignored_flag_at.present?
end
def ignore_flag
update(ignored_flag_at: Time.now)
end
def as_administrator?
administrator_id.present?
end

View File

@@ -12,16 +12,16 @@ class CommentNotifier
private
def send_comment_email
Mailer.comment(@comment).deliver_later if email_on_debate_comment?
Mailer.comment(@comment).deliver_later if email_on_comment?
end
def send_reply_email
Mailer.reply(@comment).deliver_later if email_on_comment_reply?
end
def email_on_debate_comment?
def email_on_comment?
commentable_author = @comment.commentable.author
commentable_author != @author && commentable_author.email_on_debate_comment?
commentable_author != @author && commentable_author.email_on_comment?
end
def email_on_comment_reply?

View File

@@ -0,0 +1,19 @@
module Flaggable
extend ActiveSupport::Concern
included do
has_many :flags, as: :flaggable
scope :flagged, -> { where("flags_count > 0") }
scope :pending_flag_review, -> { where(ignored_flag_at: nil, hidden_at: nil) }
scope :with_ignored_flag, -> { where.not(ignored_flag_at: nil).where(hidden_at: nil) }
end
def ignored_flag?
ignored_flag_at.present?
end
def ignore_flag
update(ignored_flag_at: Time.now)
end
end

View File

@@ -0,0 +1,47 @@
module Verification
extend ActiveSupport::Concern
included do
scope :level_three_verified, -> { where.not(verified_at: nil) }
scope :level_two_verified, -> { where("users.confirmed_phone IS NOT NULL AND users.residence_verified_at IS NOT NULL") }
scope :level_two_or_three_verified, -> { where("users.verified_at IS NOT NULL OR (users.confirmed_phone IS NOT NULL AND users.residence_verified_at IS NOT NULL)") }
scope :unverified, -> { where("users.verified_at IS NULL AND (users.confirmed_phone IS NULL OR users.residence_verified_at IS NOT NULL)") }
end
def verification_email_sent?
email_verification_token.present?
end
def verification_sms_sent?
unconfirmed_phone.present? && sms_confirmation_code.present?
end
def verification_letter_sent?
letter_requested_at.present? && letter_verification_code.present?
end
def residence_verified?
residence_verified_at.present?
end
def sms_verified?
confirmed_phone.present?
end
def level_two_verified?
residence_verified? && sms_verified?
end
def level_three_verified?
verified_at.present?
end
def level_two_or_three_verified?
level_two_verified? || level_three_verified?
end
def unverified?
!level_two_or_three_verified?
end
end

View File

@@ -1,5 +1,6 @@
require 'numeric'
class Debate < ActiveRecord::Base
include Flaggable
apply_simple_captcha
acts_as_votable
@@ -9,7 +10,6 @@ class Debate < ActiveRecord::Base
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
has_many :comments, as: :commentable
has_many :flags, as: :flaggable
validates :title, presence: true
validates :description, presence: true
@@ -26,9 +26,6 @@ class Debate < ActiveRecord::Base
before_save :calculate_hot_score, :calculate_confidence_score
scope :sort_for_moderation, -> { order(flags_count: :desc, updated_at: :desc) }
scope :pending_flag_review, -> { where(ignored_flag_at: nil, hidden_at: nil) }
scope :with_ignored_flag, -> { where.not(ignored_flag_at: nil).where(hidden_at: nil) }
scope :flagged, -> { where("flags_count > 0") }
scope :for_render, -> { includes(:tags) }
scope :sort_by_hot_score , -> { order(hot_score: :desc) }
scope :sort_by_confidence_score , -> { order(confidence_score: :desc) }
@@ -100,37 +97,20 @@ class Debate < ActiveRecord::Base
count < 0 ? 0 : count
end
def ignored_flag?
ignored_flag_at.present?
end
def ignore_flag
update(ignored_flag_at: Time.now)
end
def after_commented
save # updates the hot_score because there is a before_save
end
def calculate_hot_score
start = Time.new(2015, 6, 15)
comments_weight = 1.0/20 # 1 positive vote / x comments
time_unit = 12.hours.to_f
total = cached_votes_total + comments_weight * comments_count
ups = cached_votes_up + comments_weight * comments_count
downs = total - ups
score = ups - downs
order = Math.log([score.abs, 1].max, 10)
sign = (score <=> 0).to_f
seconds = ((created_at || Time.now) - start).to_f
self.hot_score = (((order * sign) + (seconds/time_unit)) * 1000000).round
self.hot_score = ScoreCalculator.hot_score(created_at,
cached_votes_total,
cached_votes_up,
comments_count)
end
def calculate_confidence_score
return unless cached_votes_total > 0
self.confidence_score = cached_votes_score * (cached_votes_up / cached_votes_total.to_f) * 100
self.confidence_score = ScoreCalculator.confidence_score(cached_votes_total,
cached_votes_up)
end
def self.search(terms)

181
app/models/proposal.rb Normal file
View File

@@ -0,0 +1,181 @@
class Proposal < ActiveRecord::Base
include Flaggable
apply_simple_captcha
acts_as_votable
acts_as_taggable
acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
has_many :comments, as: :commentable
validates :title, presence: true
validates :question, presence: true
validates :summary, presence: true
validates :author, presence: true
validates :responsible_name, presence: true
validate :validate_title_length
validate :validate_question_length
validate :validate_description_length
validate :validate_responsible_length
validates :terms_of_service, acceptance: { allow_nil: false }, on: :create
before_validation :sanitize_description
before_validation :sanitize_tag_list
before_validation :set_responsible_name
before_save :calculate_hot_score, :calculate_confidence_score
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()") }
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
def total_votes
cached_votes_up
end
def conflictive?
return false unless flags_count > 0 && cached_votes_up > 0
cached_votes_up/flags_count.to_f < 5
end
def description
super.try :html_safe
end
def tag_list_with_limit(limit = nil)
return tags if limit.blank?
tags.sort{|a,b| b.taggings_count <=> a.taggings_count}[0, limit]
end
def tags_count_out_of_limit(limit = nil)
return 0 unless limit
count = tags.size - limit
count < 0 ? 0 : count
end
def description
super.try :html_safe
end
def editable?
total_votes <= Setting.value_for("max_votes_for_proposal_edit").to_i
end
def editable_by?(user)
author_id == user.id && editable?
end
def votable_by?(user)
user.level_two_or_three_verified?
end
def register_vote(user, vote_value)
if votable_by?(user)
vote_by(voter: user, vote: vote_value)
end
end
def code
"#{Setting.value_for("proposal_code_prefix")}-#{created_at.strftime('%Y-%M')}-#{id}"
end
def after_commented
save # updates the hot_score because there is a before_save
end
def calculate_hot_score
self.hot_score = ScoreCalculator.hot_score(created_at,
cached_votes_up,
cached_votes_up,
comments_count)
end
def calculate_confidence_score
self.confidence_score = ScoreCalculator.confidence_score(cached_votes_up,
cached_votes_up)
end
def self.title_max_length
@@title_max_length ||= self.columns.find { |c| c.name == 'title' }.limit || 80
end
def self.question_max_length
140
end
def self.description_max_length
6000
end
def self.responsible_name_max_length
60
end
def self.search(terms)
terms.present? ? where("title ILIKE ? OR description ILIKE ? OR question ILIKE ?", "%#{terms}%", "%#{terms}%", "%#{terms}%") : none
end
def self.votes_needed_for_success
Setting.value_for('votes_for_proposal_success').to_i
end
protected
def sanitize_description
self.description = WYSIWYGSanitizer.new.sanitize(description)
end
def sanitize_tag_list
self.tag_list = TagSanitizer.new.sanitize_tag_list(self.tag_list)
end
def set_responsible_name
if author && author.level_two_or_three_verified?
self.responsible_name = author.document_number
end
end
private
def validate_description_length
validator = ActiveModel::Validations::LengthValidator.new(
attributes: :description,
maximum: Proposal.description_max_length)
validator.validate(self)
end
def validate_title_length
validator = ActiveModel::Validations::LengthValidator.new(
attributes: :title,
minimum: 4,
maximum: Proposal.title_max_length)
validator.validate(self)
end
def validate_question_length
validator = ActiveModel::Validations::LengthValidator.new(
attributes: :title,
minimum: 10,
maximum: Proposal.question_max_length)
validator.validate(self)
end
def validate_responsible_length
validator = ActiveModel::Validations::LengthValidator.new(
attributes: :title,
minimum: 6,
maximum: Proposal.responsible_name_max_length)
validator.validate(self)
end
end

View File

@@ -1,7 +1,7 @@
class Setting < ActiveRecord::Base
validates :key, presence: true, uniqueness: true
default_scope { order(key: :desc) }
default_scope { order(id: :asc) }
def self.value_for(key)
where(key: key).pluck(:value).first

View File

@@ -86,6 +86,11 @@ class User < ActiveRecord::Base
voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value }
end
def proposal_votes(proposals)
voted = votes.for_proposals(proposals)
voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value }
end
def comment_flags(comments)
comment_flags = flags.for_comments(comments)
comment_flags.each_with_object({}){ |f, h| h[f.flaggable_id] = true }
@@ -123,10 +128,13 @@ class User < ActiveRecord::Base
def block
debates_ids = Debate.where(author_id: id).pluck(:id)
comments_ids = Comment.where(user_id: id).pluck(:id)
proposal_ids = Proposal.where(author_id: id).pluck(:id)
self.hide
Debate.hide_all debates_ids
Comment.hide_all comments_ids
Proposal.hide_all proposal_ids
end

View File

@@ -29,25 +29,25 @@
<h2><%= t("account.show.personal")%></h2>
<div class="small-12 medium-4">
<% if @account.organization? %>
<%= f.fields_for :organization do |fo| %>
<%= fo.text_field :name, autofocus: true, maxlength: Organization.name_max_length, placeholder: t("account.show.organization_name_label") %>
<%= fo.text_field :responsible_name, autofocus: true, maxlength: Organization.responsible_name_max_length, placeholder: t("account.show.organization_responsible_name_placeholder") %>
<% end %>
<%= f.text_field :phone_number, placeholder: t("account.show.phone_number_label") %>
<div class="small-12 medium-4">
<% if @account.organization? %>
<%= f.fields_for :organization do |fo| %>
<%= fo.text_field :name, autofocus: true, maxlength: Organization.name_max_length, placeholder: t("account.show.organization_name_label") %>
<%= fo.text_field :responsible_name, autofocus: true, maxlength: Organization.responsible_name_max_length, placeholder: t("account.show.organization_responsible_name_placeholder") %>
<% end %>
<%= f.text_field :phone_number, placeholder: t("account.show.phone_number_label") %>
<% else %>
<%= f.text_field :username, maxlength: User.username_max_length, placeholder: t("account.show.username_label") %>
<% end %>
<%= f.text_field :username, maxlength: User.username_max_length, placeholder: t("account.show.username_label") %>
<% end %>
</div>
<h2><%= t("account.show.notifications")%></h2>
<div>
<%= f.label :email_on_debate_comment do %>
<%= f.check_box :email_on_debate_comment, label: false %>
<span class="checkbox"><%= t("account.show.email_on_debate_comment_label") %></span>
<%= f.label :email_on_comment do %>
<%= f.check_box :email_on_comment, label: false %>
<span class="checkbox"><%= t("account.show.email_on_comment_label") %></span>
<% end %>
</div>

View File

@@ -11,6 +11,13 @@
<% end %>
</li>
<li <%= "class=active" if controller_name == "proposals" %>>
<%= link_to admin_proposals_path do %>
<i class="icon-proposals"></i>
<%= t("admin.menu.hidden_proposals") %>
<% end %>
</li>
<li <%= "class=active" if controller_name == "debates" %>>
<%= link_to admin_debates_path do %>
<i class="icon-debates"></i>

View File

@@ -0,0 +1,28 @@
<h2><%= t("admin.proposals.index.title") %></h2>
<%= render 'shared/filter_subnav', i18n_namespace: "admin.proposals.index" %>
<h3><%= page_entries_info @proposals %></h3>
<ul class="admin-list">
<% @proposals.each do |proposal| %>
<li id="<%= dom_id(proposal) %>">
<%= link_to proposal.title, proposal_path(proposal) %>
<%= link_to t("admin.actions.restore"),
restore_admin_proposal_path(proposal, request.query_parameters),
method: :put,
data: { confirm: t("admin.actions.confirm") },
class: "button radius tiny success right" %>
<% unless proposal.confirmed_hide? %>
<%= link_to t("admin.actions.confirm_hide"),
confirm_hide_admin_proposal_path(proposal, request.query_parameters),
method: :put,
class: "button radius tiny warning right" %>
<% end %>
</li>
<% end %>
</ul>
<%= paginate @proposals %>

View File

@@ -11,7 +11,7 @@
<% if can? :hide, comment.user %>
<span class="divider">&nbsp;&bull;&nbsp;</span>
<%= link_to t("admin.actions.hide_author").capitalize, hide_moderation_user_path(comment.user_id, debate_id: @debate.id),
<%= link_to t("admin.actions.hide_author").capitalize, hide_moderation_user_path(comment.user_id),
method: :put, data: { confirm: t('admin.actions.confirm') } %>
<% end %>
</span>

View File

@@ -5,7 +5,7 @@
<% if comment.hidden? || comment.user.hidden? %>
<% if select_children(@comments, comment).size > 0 %>
<div class="is-deleted">
<p><%= t("debates.comment.deleted") %></p>
<p><%= t("comments.comment.deleted") %></p>
</div>
<% end %>
<% else %>
@@ -28,13 +28,13 @@
<div class="comment-info">
<% if comment.as_administrator? %>
<span class="user-name"><%= t("debates.comment.admin") %> #<%= comment.administrator_id%></span>
<span class="user-name"><%= t("comments.comment.admin") %> #<%= comment.administrator_id%></span>
<% elsif comment.as_moderator? %>
<span class="user-name"><%= t("debates.comment.moderator") %> #<%= comment.moderator_id%></span>
<span class="user-name"><%= t("comments.comment.moderator") %> #<%= comment.moderator_id%></span>
<% else %>
<% if comment.user.hidden? %>
<span class="user-name"><%= t("debates.comment.user_deleted") %></span>
<span class="user-name"><%= t("comments.comment.user_deleted") %></span>
<% else %>
<span class="user-name"><%= comment.user.name %></span>
<% if comment.user.official? %>
@@ -53,7 +53,7 @@
<% if comment.user_id == @commentable.author_id %>
&nbsp;&bull;&nbsp;
<span class="label round is-author">
<%= t("debates.comment.author") %>
<%= t("comments.comment.author") %>
</span>
<% end %>
@@ -80,7 +80,7 @@
</span>
<div class="reply">
<%= t("debates.comment.responses", count: select_children(@comments, comment).size) %>
<%= t("comments.comment.responses", count: select_children(@comments, comment).size) %>
<% if user_signed_in? %>
<span class="divider">&nbsp;|&nbsp;</span>

View File

@@ -1,2 +1,3 @@
<div><%= t("errors.messages.blank").capitalize %></div>
<div class="alert-box alert radius">
<%= t("errors.messages.blank").capitalize %>
</div>

View File

@@ -1,7 +1,7 @@
<% cache [locale_and_user_status, parent_id, commentable_cache_key(commentable)] do %>
<% css_id = parent_or_commentable_dom_id(parent_id, commentable) %>
<div id="js-comment-form-<%= css_id %>" <%= "style='display:none'".html_safe if toggeable %>>
<%= form_for [commentable, Comment.new], remote: true do |f| %>
<%= form_for Comment.new, remote: true do |f| %>
<%= label_tag "comment-body-#{css_id}", t("comments.form.leave_comment") %>
<%= f.text_area :body, id: "comment-body-#{css_id}", maxlength: Comment.body_max_length, label: false %>
<%= f.hidden_field :commentable_type, value: commentable.class.name %>

View File

@@ -1,5 +1,5 @@
<span>
<%= t('debates.comment.votes', count: comment.total_votes) %>
<%= t('comments.comment.votes', count: comment.total_votes) %>
</span>
&nbsp;|&nbsp;
<span class="in_favor">

View File

@@ -20,7 +20,7 @@
<a class="js-add-tag-link"><%= tag.name %></a>
<% end %>
</span>
<%= f.text_field :tag_list, value: @debate.tag_list.to_s, label: false, placeholder: t("debates.form.tags_placeholder") %>
<%= f.text_field :tag_list, value: @debate.tag_list.to_s, label: false, placeholder: t("debates.form.tags_placeholder"), class: 'js-tag-list' %>
</div>
<div class="small-12 column">

View File

@@ -1,4 +1,4 @@
<% voted_classes = css_classes_for_debate_vote(@debate_votes, debate) %>
<% voted_classes = css_classes_for_vote(@debate_votes, debate) %>
<div class="votes">
<div class="in-favor inline-block">
<%= link_to vote_debate_path(debate, value: 'yes'),

View File

@@ -1,7 +1,13 @@
<section role="main">
<% content_for :header_addon do %>
<%= render "shared/search_form_header",
search_path: debates_path(page: 1),
i18n_namespace: "debates.index.search_form" %>
<% end %>
<section role="main">
<div class="wrap row">
<div id="debates" class="debates-list small-12 medium-9 column js-order-<%= @order.dasherize %>">
<div id="debates" class="debates-list small-12 medium-9 column">
<div class="filters">
<div class="small-12 medium-7 left">
<% if @search_terms %>
@@ -16,39 +22,36 @@
</h2>
<% end %>
</div>
<% if @tag_filter || @search_terms %>
<div class="small-12 medium-5 margin-top inline-block debates-order">
<h6 class="inline-block">
<%= t("debates.index.select_order") %>
</h6>
<%= render 'shared/order_selector', i18n_namespace: "debates.index" %>
</div>
<% else %>
<div class="small-12 inline-block">
<h2 class="inline-block">
<%= t("debates.index.select_order_long") %>
</h2>
<%= render 'shared/order_selector', i18n_namespace: "debates.index" %>
</div>
<% 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("debates.index.orders.#{order}") %>
</option>
<% end %>
</select>
</form>
<div class="show-for-small-only">
<%= link_to t("debates.index.start_debate"), new_debate_path, class: 'button radius expand' %>
</div>
<%= render @debates %>
<%= paginate @debates %>
</div>
<div class="show-for-small-only">
<%= link_to t("debates.index.start_debate"), new_debate_path, class: 'button radius expand' %>
</div>
<%= render @debates %>
<%= paginate @debates %>
</div>
<div class="small-12 medium-3 column">
<aside class="sidebar" role="complementary">
<%= link_to t("debates.index.start_debate"), new_debate_path, class: 'button radius expand' %>
<%= render "shared/tag_cloud" %>
<%= render "shared/tag_cloud", taggable: 'debate' %>
</aside>
</div>
</div>

View File

@@ -57,7 +57,7 @@
</span>
</div>
<%= @debate.description %>
<%= safe_html_with_links @debate.description %>
<%= render 'shared/tags', debate: @debate %>
@@ -81,4 +81,5 @@
</div>
</section>
<% end %>
<%= render "comments" %>

View File

@@ -31,10 +31,10 @@
<div class="row-full subnavigation-container">
<section class="subnavigation row">
<%#= link_to t("layouts.header.welcome"), root_path %>
<%#= link_to t("layouts.header.initiatives"), "#" %>
<div class="small-12 medium-8 column">
<%= link_to t("layouts.header.highlights"), highlights_path, class: ("active" if current_page? highlights_path) %>
<%= link_to t("layouts.header.debates"), debates_path, class: ("active" if current_page?(controller: "/debates")) %>
<%= link_to t("layouts.header.proposals"), proposals_path, class: ("active" if current_page?(controller: "/proposals")) %>
<%= link_to t("layouts.header.more_information"), "/more_information", class: ("active" if current_page?("/more_information")) %>
<%= link_to t("layouts.header.external_link_blog_url"), target: "_blank" do %>
<%= t("layouts.header.external_link_blog") %>
@@ -42,7 +42,7 @@
<% end %>
</div>
<div class="small-12 medium-4 column right">
<%= render "shared/search_form_header" %>
<%= yield :header_addon %>
</div>
</section>
</div>
@@ -51,8 +51,8 @@
<div class="row home-page">
<div class="small-12 medium-7 column">
<h1><%= t("layouts.header.open_city_title") %></h1>
<p><%= t("layouts.header.open_city_slogan") %></p>
<%= link_to t("layouts.header.see_all_debates"), debates_path, class: "button radius see-more" %>
<p><%= t("layouts.header.open_city_slogan_html") %></p>
<%= link_to t("layouts.header.see_all"), highlights_path, class: "button radius see-more warning" %>
<%= link_to t("layouts.header.more_information"), "/more_information", class: "more-info" %>
</div>

View File

@@ -14,7 +14,7 @@
<tr>
<td style="border-bottom: 1px solid #dadfe1; padding: 20px 0;">
<a href="#" target="_blank">
<%= image_tag('logo_email_gobierno_abierto.png', style: "border: 0; display: block; width: 100%;max-width: 370px") %>
<%= image_tag('logo_email_gobierno_abierto.png', style: "border: 0; display: block; width: 100%;max-width: 370px", alt: "Ayuntamiento de Madrid - Participación ciudadana, transparencia y gobierno abierto") %>
</a>
</td>
</tr>

View File

@@ -4,6 +4,13 @@
<%= link_to t("moderation.dashboard.index.title"), moderation_root_path %>
</li>
<li <%= "class=active" if controller_name == "proposals" %>>
<%= link_to moderation_proposals_path do %>
<i class="icon-proposals"></i>
<%= t("moderation.menu.proposals") %>
<% end %>
</li>
<li <%= "class=active" if controller_name == "debates" %>>
<%= link_to moderation_debates_path do %>
<i class="icon-debates"></i>

View File

@@ -0,0 +1,3 @@
var proposal_id = '<%= dom_id(@proposal) %>';
App.ModeratorProposals.add_class_faded(proposal_id);
App.ModeratorProposals.hide_moderator_actions(proposal_id);

View File

@@ -0,0 +1,77 @@
<h2><%= t("moderation.proposals.index.title") %></h2>
<%= render 'shared/filter_subnav', i18n_namespace: "moderation.proposals.index" %>
<div class="row">
<h3 class="small-8 large-8 columns"><%= page_entries_info @proposals %></h3>
<div class="small-4 large-4 columns">
<div class="right">
<%= t("moderation.proposals.index.order") %>
<%= render 'shared/order_selector', i18n_namespace: "moderation.proposals.index" %>
</div>
</div>
</div>
<%= form_tag moderate_moderation_proposals_path(request.query_parameters), method: :put do %>
<p class="right js-check">
<%= t('shared.check') %>:
<%= link_to t('shared.check_all'), '#', data: {check_all: "proposal_ids[]"} %>
|
<%= link_to t('shared.check_none'), '#', data: {check_none: "proposal_ids[]"} %>
</p>
<table>
<tr>
<th>
<%= t("moderation.proposals.index.headers.proposal") %>
</th>
<th>
<%= t("moderation.proposals.index.headers.moderate") %>
</th>
</tr>
<% @proposals.each do |proposal| %>
<tr id="proposal_<%= proposal.id %>">
<td>
<%= link_to proposal.title, proposal, target: "_blank" %>
<br>
<span class="date"><%= l proposal.updated_at.to_date %></span>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<%= proposal.flags_count %><i class="icon-flag flag-disable"></i>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<%= proposal.author.username %>
<br>
<div style="height: 4rem; overflow: hidden;">
<%= proposal.description %>
</div>
</td>
<td class="text-center">
<%= check_box_tag "proposal_ids[]", proposal.id, nil, id: "#{dom_id(proposal)}_check" %>
</td>
</tr>
<% end %>
</table>
<%= submit_tag t('moderation.proposals.index.block_authors'),
name: "block_authors",
class: "button radius alert",
data: {confirm: t('moderation.proposals.index.confirm')}
%>
<div class="right">
<%= submit_tag t('moderation.proposals.index.hide_proposals'),
name: "hide_proposals",
class: "button radius alert",
data: {confirm: t('moderation.proposals.index.confirm')}
%>
<%= submit_tag t('moderation.proposals.index.ignore_flags'),
name: "ignore_flags",
class: "button radius success",
data: {confirm: t('moderation.proposals.index.confirm')}
%>
</div>
<%= paginate @proposals %>
<% end %>

View File

@@ -1,12 +1,14 @@
<div class="page row-full">
<div class="row">
<div class="menu small-12 medium-3 column">
<p><strong>¿Cómo funciona este Portal de Gobierno Abierto?</strong></p>
<ul>
<i class="icon-angle-left left"></i>&nbsp;
<%= link_to t("debates.show.back_link"), "/more_information", class: 'left back' %>
<ul class="clear">
<li>
<a href="#i">I. Participación</a>
<ul>
<li><a href="#i1">I.1. Espacio de debate</a></li>
<li><a href="#i2">I.2. Espacio de propuestas</a></li>
</ul>
</li>
</ul>
@@ -39,6 +41,14 @@
<p>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.).</p>
<p>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 de Madrid por el cual el Ayuntamiento trabaja para la ciudadanía, y ante ella debe responder.</p>
<h3 id="i2">I.I. Espacio de propuestas</h3>
<p>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.</p>
<p>Las propuestas pueden ser apoyadas por ciudadanos empadronados en Madrid 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.</p>
<p>Una vez que una propuesta alcance una cantidad de apoyos equivalente al 2% del censo de Madrid (unos 53000), 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 de Madrid votará si se lleva a cabo o no. El plazo máximo para recabar los apoyos necesarios será de 12 meses.</p>
</div>
</div>
</div>

View File

@@ -1,7 +1,10 @@
<div class="page row-full">
<div class="row">
<div class="text small-12 column">
<h1>Utilízalo en tu municipio</h1>
<i class="icon-angle-left left"></i>&nbsp;
<%= link_to t("debates.show.back_link"), "/more_information", class: 'left back' %>
<h1 class="clear">Utilízalo en tu municipio</h1>
<p>Utilízalo en tu municipio libremente o ayúdanos a mejorarlo, es software libre.</p>
<p>Este Portal de Gobierno Abierto usa la <a href="https://github.com/ayuntamientomadrid" target="_blank">aplicación Consul</a> que es software libre, con <a href="http://www.gnu.org/licenses/agpl-3.0.html" target="_blank">licencia AGPLv3</a>, esto significa en palabras sencillas, que cualquiera puede libremente usar el código, copiarlo, verlo en detalle, modificarlo, y redistribuirlo al mundo con las modificaciones que quiera (manteniendo el que otros puedan a su vez hacer lo mismo). Porque creemos que la cultura es mejor y más rica cuando se libera.</p>

View File

@@ -21,7 +21,27 @@
<span>Participación Ciudadana, Transparencia y Gobierno Abierto</span>
</li>
</a>
<a href="/proposals_info">
<li>
¿Cómo funcionan las propuestas ciudadanas?
<span>Crea tus propias propuestas</span>
</li>
</a>
<a href="/participation_world">
<li>
Participación ciudadana directa en el mundo
<span>Sistemas de participación ciudadana que ya existen en el mundo</span>
</li>
</a>
<a href="/participation_facts">
<li>
Hechos sobre participación ciudadana y democracia directa
<span>Para perderle el miedo</span>
</li>
</a>
</ul>
<p>Si tienes problemas con la web <a href="mailto:web.gobiernoabierto@madrid.es">contacta con el servicio técnico</a>.</p>
</div>
</div>
</div>

View File

@@ -1,30 +1,32 @@
<div class="page row-full">
<div class="row">
<div class="menu small-12 medium-3 column">
<ul>
<li>
<a href="#i">I. Participación</a>
<ul>
<li><a href="#i1">I.1. Propuestas ciudadanas</a></li>
<li><a href="#i2">I.2. Presupuestos participativos</a></li>
<li><a href="#i3">I.3. Legislacion colaborativa</a></li>
<li><a href="#i4">I.4. Co-gobierno ciudadano</a></li>
<li><a href="#i5">I.5. Participación sectorial</a></li>
<li><a href="#i6">I.6. Inclusión, neutralidad y privacidad</a></li>
<li><a href="#i7">I.7. Innovación social</a></li>
</ul>
</li>
<li>
<a href="#ii">II. Transparencia</a>
<ul>
<li><a href="#ii1">II.1. Ordenanza de transparencia</a></li>
<li><a href="#ii2">II.2. Portal de transparencia</a></li>
<li><a href="#ii3">II.3. Datos abiertos</a></li>
<li><a href="#ii4">II.4. Agendas públicas</a></li>
<li><a href="#ii5">II.5. Transparencia del Lobby</a></li>
</ul>
</li>
</ul>
<i class="icon-angle-left left"></i>&nbsp;
<%= link_to t("debates.show.back_link"), "/more_information", class: 'left back' %>
<ul class="clear">
<li>
<a href="#i">I. Participación</a>
<ul>
<li><a href="#i1">I.1. Propuestas ciudadanas</a></li>
<li><a href="#i2">I.2. Presupuestos participativos</a></li>
<li><a href="#i3">I.3. Legislacion colaborativa</a></li>
<li><a href="#i4">I.4. Co-gobierno ciudadano</a></li>
<li><a href="#i5">I.5. Participación sectorial</a></li>
<li><a href="#i6">I.6. Inclusión, neutralidad y privacidad</a></li>
<li><a href="#i7">I.7. Innovación social</a></li>
</ul>
</li>
<li>
<a href="#ii">II. Transparencia</a>
<ul>
<li><a href="#ii1">II.1. Ordenanza de transparencia</a></li>
<li><a href="#ii2">II.2. Portal de transparencia</a></li>
<li><a href="#ii3">II.3. Datos abiertos</a></li>
<li><a href="#ii4">II.4. Agendas públicas</a></li>
<li><a href="#ii5">II.5. Transparencia del Lobby</a></li>
</ul>
</li>
</ul>
</div>
<div class="text small-12 medium-9 column">
<h1>Participación y Transparencia en Madrid - Próximas novedades</h1>

View File

@@ -0,0 +1,33 @@
<div class="page row-full">
<div class="row">
<div class="text small-12 medium-10 column small-centered">
<i class="icon-angle-left left"></i>&nbsp;
<%= link_to t("debates.show.back_link"), "/more_information", class: 'left back' %>
<h1 class="clear">Hechos sobre participación ciudadana y democracia directa</h1>
<br>
<h2>La democracia directa produce gente más informada y con más cultura política.</h2>
<p>El poner en marcha mecanismos de participación reales hace que la gente se preocupe por las decisiones que tiene que tomar, y que por lo tanto se informe sobre ellas. Esto se observa sistemáticamente al comparar diferentes regiones de un mismo país con diferentes grados de democracia directa, o en procesos particulares como por ejemplo la votación del Tratado por una Constitución para Europa, comparando los países que lo votaron en referéndum y los que no.</p>
<p>En ocasiones se argumenta que no se deben crear mecanismos de decisión ciudadana, porque un posible bajo nivel cultural o de conocimiento político haría que se tomaran malas decisiones. Esto se ha argumentado tradicionalmente en contra del voto femenino, del voto inmigrante, del voto de la gente sin renta, etc. pero lo que se ha demostrado es que permitir a toda esa gente decidir ha sido precisamente lo que ha permitido que escaparan de su situación de desigualdad cultural y de derechos, o al menos que la mejoraran considerablemente.</p>
<p><em>M. Benz / A. Stutzer (2004), «Are voters better informed when they have a larger say in politics?», Public Choice 119, p. 31-59</em></p>
<h2>La democracia directa es más efectiva para tomar decisiones que la representativa. Por ejemplo genera menos deuda.</h2>
<p>Los proyectos disparatados y los gastos desmesurados suelen venir por parte de políticos electos en lugar de por procesos de participación ciudadana. Los estados de Estados Unidos con sistemas de iniciativas populares efectivos generan un 7% menos de deuda que los que no los tienen, y las regiones en Suiza con referéndum obligatorios para gastos públicos importantes gastan un 19% menos que los demás.</p>
<p><em>R.K. von Weizsäcker (1992), «Staatsverschuldung und Demokratie», Kyklos 45, p. 51-67</em></p>
<p><em>L.P. Feld / J.G. Matsusaka (2003), »Budget referendums and government spending: evidence from Swiss cantons», Journal of Public Economics 87, p. 2703-2724</em></p>
<p><em>J. G. Matsusaka (2004), «For the Many or the Few. The Initiative, Public Policy, and American Democracy» University of Chicago Press</em></p>
<h2>La democracia directa protege mejor los derechos humanos y los de las minorías que la representativa</h2>
<p>Estudios de todos los referéndum suizos durante treinta años muestran que los referéndum cuando tratan sobre aumentar los derechos de las minorías se aprueban aproximadamente un 25% más que los que tratan de temas generales. En concreto, el 80% de los referéndum de este tipo a nivel federal fueron aprobados.</p>
<p>Decisiones como eliminar el derecho a la vivienda de manera efectiva, el derecho de sanidad para las personas migrantes, o el iniciar guerras contra otros países, suelen darse a través de gobiernos representativos, y no en referéndum ciudadanos.</p>
<p><em>B.S. Frey / M. Goette (1998), «Does the popular vote destroy civil rights?», American Journal of Political Science 42, p. 1343-1348</em></p>
<h2>En cualquier caso, la democracia directa no sustituye ni amenaza a la democracia representativa, la complementa. Y soluciona algunos problemas que no consigue la otra.</h2>
<p>El año que más iniciativas ciudadanas se votaron en los diferentes estados con democracia directa de Estados Unidos, estas decisiones sólo representaron el 0,6% de todas las decisiones que tomaron los políticos en esos estados.</p>
<p>Las decisiones directas de la ciudadanía no sustituyen en ningún lugar del mundo el sistema político representativo, sólo lo amplían y mejoran. Tampoco pretenden ser la solución a todos los problemas, también se toman decisiones equivocadas a través de las decisiones ciudadanas, pero se reduce el número de las decisiones erróneas que se toman teniendo sólo un sistema representativo.</p>
<p><em>M.D. Waters (2002), «Initiative and referendum in the United States: a primer», Washington: Citizen Lawmaker Press</em></p>
<h2>Si los ciudadanos son capaces de elegir entre políticos que toman decisiones buenas o malas para el país, deben ser capaces de elegir directamente entre buenas y malas decisiones.</h2>
</div>
</div>
</div>

View File

@@ -0,0 +1,50 @@
<div class="page row-full">
<div class="row">
<div class="menu small-12 medium-3 column">
<i class="icon-angle-left left"></i>&nbsp;
<%= link_to t("debates.show.back_link"), "/more_information", class: 'left back' %>
<ul class="clear">
<li>
<a href="#i">La nueva ola global de participación ciudadana</a>
</li>
<li>
<a href="#ii">Participación ciudadana directa con experiencia</a>
</li>
</ul>
</div>
<div class="text small-12 medium-9 column">
<h1>Participación ciudadana directa en el mundo</h1>
<p>En el mundo existen sistemas de participación ciudadana muy similares al que vamos a implementar en Madrid, que llevan funcionando desde hace más de cien años y en países muy diferentes. La experiencia larga y variada de dichos sistemas demuestra que lo que ponemos en marcha en Madrid tendrá un impacto muy positivo en la sociedad, como ha tenido en esos otros países.</p>
<p>Además de los ejemplos clásicos, como Suiza, asistimos a un desarrollo muy fuerte de los sistemas de participación ciudadana en todo el mundo, en particular en los últimos años, gracias especialmente a las nuevas posibilidades que nos brinda Internet. Islandia, Finlandia, Brasil, Estados Unidos, son algunos de los países que más están apostando por una participación directa de la ciudadanía en la toma de decisiones.</p>
<h2 id="i">La nueva ola global de participación ciudadana</h2>
<p>Las nuevas formas de participación se están dirigiendo principalmente a que sean la ciudadanía quien decida qué caminos debe tomar la política de su país, a través de mecanismos de <strong>iniciativas ciudadanas</strong>. Finlandia es uno de los países donde se están desarrollando nuevas herramientas similares al nuevo portal de gobierno abierto de Madrid. Su plataforma <a href="http://openministry.info/" target="_blank">Open Ministry</a> permite a la población presentar y apoyar propuestas, y ha conseguido por ejemplo que se apruebe gracias a él la ley de matrimonio igualitario. Islandia también ha tenido una gran repercusión desde 2011, cuando lanzaron su plataforma Better Reykjavik, que ha permitido que el 58% de la población participe en el proceso de propuestas, seleccionando cada mes las 15 ideas más votadas.</p>
<p>Estonia es uno de los países que gracias a una apuesta clara por las nuevas tecnologías, ha podido situarse en cabeza de Europa en el nivel de uso por parte de la ciudadanía de Internet para la interacción con el gobierno. No sólo los ciudadanos y ciudadanas resuelven diariamente todos sus trámites a través de Internet, sino que han puesto en marcha plataformas como Rahvakogu, donde después de los escándalos políticos de 2012, 50.000 personas (de un total de 1.3 millones) participaron proponiendo medidas para mejorar la situación democrática del país.</p>
<p>Otra de las principales experiencias que se están extendiendo rápidamente por todo el mundo son los <strong>presupuestos participativos</strong>. Estos consisten en mecanismos, generalmente acompañados de una plataforma en Internet, por los cuales el gobierno reserva parte de sus presupuestos de inversión (los que no están comprometidos ya en cuestiones como limpieza o servicios sociales), para que sea la ciudadanía quien decida en qué se gasta dicho dinero.</p>
<p>Islandia es uno de los países de referencia al respecto, a través de su plataforma Betri Reykjavík-Betri Hverfi (Better District). Cada año se gastan 1.8 millones de euros en alrededor de 200 proyectos propuestos por la población para los distintos barrios de Reykjavík. Las ciudadanas y ciudadanos pueden participar y seguir el proyecto a través de la plataforma digital <a href="http://www.betrireykjavik.is" target="_blank">betrireykjavik</a>, en <a href="http://www.facebook.com/Betri.Reykjavik" target="_blank">Facebook</a> o a través de centros en sus distritos o comités distritales.</p>
<p>Otras experiencias de referencia incluyen París, donde los residentes decidirán hasta el 2020 cómo se gastan 426 millones de euros (lo que corresponde al 5% del presupuesto municipal de París); Nueva York, donde el año pasado dedicaron <a href="http://council.nyc.gov/html/action/pb.shtml" target="_blank">32 millones de euros a presupuestos participativos</a>; más de 100 ciudades brasileñas donde se han manejado <a href="http://www.washingtonpost.com/blogs/monkey-cage/wp/2014/01/22/brazil-let-its-citizens-make-decisions-about-city-budgets-heres-what-happened/" target="_blank">presupuestos participativos que han oscilado entre el 5% y el 15%</a>; entre muchas otras ciudades del mundo.</p>
<h2 id="ii">Participación ciudadana directa con experiencia</h2>
<p>En diferentes países del mundo existen sistemas de participación ciudadana directa que vienen funcionando desde hace mucho tiempo sin pasar por las nuevas tecnologías. Aunque estos sistemas no tengan la agilidad de las nuevas plataformas, los procesos que se dan en ellos son básicamente los mismos, y nos aseguran la calidad y los resultados de los mecanismos que se van a poner en marcha. Comentamos a continuación tres de los casos más importantes y resaltamos algunas cifras para entender de lo que hablamos:</p>
<h3>Suiza</h3>
<p>Desde 1848 se han votado unos 600 referéndum a nivel federal (<a href="https://www.admin.ch/ch/f/pore/va/vab_2_2_4_1_gesamt.html" target="_blank">compilación en francés</a>), y se celebran cada año en todo el país del orden de 200 referéndum a todos los niveles (municipal, cantonal y federal).</p>
<p>Aproximadamente una de cada dos leyes aprobadas en el Parlamento directamente, y luego consultada a la ciudadanía, fue anulada. Por otro lado, incluso aunque las iniciativas populares no tengan éxito, el gobierno acaba concediendo parte de las demandas propuestas, y se genera una atención nacional sobre el asunto tratado. Esto hace que aproximadamente la mitad de la gente que lanzó las iniciativas que no tuvieron éxito consideren que mereció la pena el esfuerzo y se obtuvo algo que no hubiera sido posible sin la iniciativa.</p>
<h3>Estados Unidos</h3>
<p>En este país no se cuenta con procesos de iniciativas ciudadanas a nivel federal, pero 27 de los 51 estados cuentan con algún tipo sistema de democracia directa (cambiando mucho la regulación y el alcance de un estado a otro). A nivel local, aproximadamente la mitad de las ciudades cuentan con un sistema de iniciativas ciudadanas vinculantes.</p>
<p>Entre 1904 y 2000 se convocaron casi 2.000 referéndum iniciados por la ciudadanía. En 1996 en los estados con mecanismos de iniciativas ciudadanas se convocaron 96 referéndum, frente a las más de 14.000 leyes y resoluciones aprobadas por los representantes de esos estados.</p>
<h3>Alemania</h3>
<p>No existe en Alemania ningún mecanismo de democracia directa a nivel nacional, pero sí a nivel regional y local. Estos sistemas se introdujeron en su mayoría en los años 90. En 1996 el número de iniciativas ciudadanas fue de 318, reduciéndose en años posteriores, una vez tratados tantos temas pendientes anteriores a la introducción de estos mecanismos, a una media de 100 por año.</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,105 @@
<div class="page row-full">
<div class="row">
<div class="menu small-12 medium-3 column">
<i class="icon-angle-left left"></i>&nbsp;
<%= link_to t("debates.show.back_link"), "/more_information", class: 'left back' %>
<ul class="clear">
<li>
<a href="#i">Explicación detallada del proceso</a>
</li>
<li>
<a href="#ii">Preguntas Frecuentes</a>
</li>
<li>
<a href="#iii">Hojas de firmas para recoger apoyos</a>
</li>
</ul>
</div>
<div class="text small-12 medium-9 column">
<h1>¿Cómo funcionan las propuestas ciudadanas?</h1>
<p>El mecanismo de propuestas ciudadanas se resume en cuatro pasos muy sencillos:</p>
<ol>
<li><strong>¡Propones!</strong> Creas una propuesta en esta web.</li>
<li><strong>¡Apoyas!</strong> La gente hace click en el botón de apoyar tu propuesta (necesitas el apoyo del 2% de los empadronados mayores de 16 años para pasar a la siguiente fase).</li>
<li><strong>¡Decides!</strong> Si has conseguido suficientes apoyos, dejamos 45 días para que la gente pueda debatir sobre la propuesta, y después durante una semana se invita a toda la gente de Madrid a decidir si están a favor o en contra de tu propuesta, en esta misma web.</li>
<li><strong>¡Se hace!</strong> Si hay más gente a favor de tu propuesta que en contra, el gobierno del Ayuntamiento de Madrid asumirá como propia la propuesta y la llevará a cabo.</li>
</ol>
<p>Además no hace falta ni que tengas Internet, todos los pasos se pueden hacer en cualquiera de las 26 Oficinas de Atención al Ciudadano que hay por todo Madrid.</p>
<h2 id="i">Explicación detallada del proceso</h2>
<ol>
<li><strong>Creación de una propuesta</strong>. Cualquier persona (sin necesidad siquiera de estar empadronada en Madrid) puede crear una propuesta. Lo único que hay que hacer es pulsar el botón “Crear una propuesta” y rellenar los campos requeridos. La propuesta puede ser tan sencilla como una simple frase, pero te recomendamos detallarla todo lo posible, incluso añadiendo material adicional, para que sea más completa e interesante. Una vez creada aparecerá en esta web para que cualquiera pueda apoyarla.</li>
<li><strong>Apoyo de propuestas</strong>. Para apoyar una de las propuestas que aparece en la web, pulsamos el botón “apoyar esta propuesta” que aparece en cada una. Para este paso tendremos que estar empadronados en Madrid, así que al llevarlo a cabo por primera vez se nos pedirá que verifiquemos nuestra cuenta para estar seguros de este requerimiento. Se nos va a pedir que introduzcamos algunos datos para comprobar nuestra información de empadronamiento, y se nos enviará un código personal para que el proceso sea seguro. Las propuestas necesitan una cierta cantidad de apoyos para pasar a la siguiente fase; concretamente el 2% de los empadronados mayores de 16 años (que suponen 53.726 apoyos).</li>
<li><strong>Decisión sobre propuestas</strong>. Cuando una propuesta consigue los apoyos necesarios, se anuncia en la web. Desde ese momento se dejan 45 días para que todo el mundo pueda debatir e informarse sobre la propuesta. Todas las otras propuestas que hayan conseguido los apoyos necesarios en los primeros 30 días del tiempo de debate se agruparán junto a la primera para decidir sobre ellas al mismo tiempo. Pasados los 45 días se exponen estas propuestas en un espacio especial de votación de la web, donde durante una semana cualquier persona empadronada en Madrid y mayor de 16 años podrá decidir si está a favor o rechaza la propuesta. Para participar en este paso tendrás que tener tu cuenta de usuario verificada completamente, de tal forma que nos aseguremos que cada persona no tiene más de una cuenta y el proceso es seguro.</li>
<li><strong>Realización de las propuestas</strong>. En caso de que haya más gente a favor de una propuesta que rechazándola se aceptará como propuesta colectiva de la ciudadanía de Madrid, y el gobierno del Ayuntamiento de Madrid la asumirá como propia y la llevará a cabo. Para ello en un plazo máximo de un mes, se realizarán los informes técnicos correspondientes sobre su legalidad, viabilidad y coste económico, teniendo en cuenta a los sectores afectados y a la persona que haya lanzado la propuesta, para detallar la actuación correspondiente por parte del Ayuntamiento. Se publicarán en la web todos los informes realizados, y un seguimiento de las actuaciones que se lleven a cabo, para asegurar un correcto desarrollo de la propuesta.</li>
</ol>
<p>Todas las acciones relacionadas con el proceso de propuestas ciudadanas pueden realizarse a través del portal de gobierno abierto, o presencialmente en cualquiera de las 26 Oficinas de Atención al Ciudadano existentes en Madrid. <a href="http://www.madrid.es/portales/munimadrid/es/Inicio/El-Ayuntamiento/Atencion-al-ciudadano/Oficinas-de-Atencion-al-Ciudadano?vgnextfmt=default&vgnextchannel=5b99cde2e09a4310VgnVCM1000000b205a0aRCRD" target="_blank">Ver la lista completa de oficinas y su ubicación</a>.</p>
<p>El proceso de recogida de apoyos de una propuesta puede realizarse también a través de hojas de firmas, cuyo modelo puede ser <a href="/docs/formulario_propuestas_ciudadanas.pdf" target="_blank">descargado en este enlace</a>. Los apoyos recogidos de esta manera se sumarán a los apoyos ya existentes en el portal de gobierno abierto. Las hojas pueden ser entregadas en cualquiera de los Registros del Ayuntamiento, presentes en cada una de las Juntas de Distrito. <a href="http://www.madrid.es/portales/munimadrid/es/Inicio/Ayuntamiento/Oposiciones-y-Empleo/Direcciones-y-telefonos/Oficinas-de-Registro-del-Ayuntamiento-de-Madrid?vgnextfmt=default&vgnextoid=adedb88db777f010VgnVCM2000000c205a0aRCRD&vgnextchannel=ddc3ca1c5a057010VgnVCM100000dc0ca8c0RCRD" target="_blank">Ver la lista completa de Oficinas de Registro</a>.</p>
<h2 id="ii">Preguntas Frecuentes</h2>
<ol>
<li><strong>¿Cuáles son los requisitos que se solicitan para poder apoyar o votar propuestas?</strong><br>
Estar empadronado en Madrid y ser mayor de 16 años. En caso de que se haga a través de Internet se requerirá que se haya verificado la cuenta de usuario de la web (encontramos el botón de verificación en el apartado “Mi cuenta” en la esquina superior derecha), proporcionando la información del padrón, y un medio de comunicación para obtener un código seguro de participación que introduciremos en la web para validar nuestra cuenta.
</li>
<li><strong>¿Cómo va a controlarse que cada persona vote una única vez por propuesta?</strong><br>
Las votaciones presenciales en las Oficinas de Atención al Ciudadano están controladas porque la persona tiene que presentar su DNI. Para la votación a través de Internet se verifica que la cuenta de usuario corresponde a un único ciudadano/a. Para ello se le facilita un código personal seguro de verificación de la cuenta que se comunica a través de canales de comunicación privados, como son teléfonos móviles que constan en el Ayuntamiento que pertenecen a la persona adecuada, o por correo a través del buzón que figura en la dirección de empadronamiento. De esta forma nos aseguramos de que sólo esa persona ha recibido el código. En caso de que dichos medios de comunicación no estén disponibles se solicitará a la persona que acuda presencialmente a alguna de las Oficinas de Atención al Ciudadano, para obtener su código.
</li>
<li><strong>¿Se llevará a cabo cualquier propuesta que se acepte por mayoría en la web? ¿incluso aunque sean ilegales o atenten contra los derechos humanos? ¿y si no hay presupuesto para llevarla a cabo?</strong><br>
Se establecen una serie de criterios objetivos por los que una propuesta no podrá ser aceptada como, por ejemplo, que tenga fines delictivos o que atente contra derechos fundamentales y libertades públicas. Además se excluyen del proceso las propuestas sobre otras modalidades de participación tales como presupuestos participativos, procesos revocatorios, audiencia pública o iniciativas normativas populares.
<br>Este mecanismo de participación no cambia los límites ya existentes para el Ayuntamiento, lo que hace es trasladar quién toma la decisión de lo que haya que hacer, incluyendo a toda la ciudadanía en el proceso de toma de decisiones.
<br>En el caso de las propuestas que quedan fuera de las competencias municipales el Ayuntamiento no podrá llevarlas a cabo, pero emprenderá actuaciones alternativas dentro de sus capacidades que intenten cumplir con la decisión de la propuesta. Lo mismo sucede con el presupuesto para llevar a cabo la propuesta; el ayuntamiento utilizará los recursos necesarios en cuanto sea posible, de la misma manera que se costean las decisiones adoptadas sin participación ciudadana.
<br>También puede ocurrir que la actuación requiera la aprobación del Pleno, por lo que el resultado dependerá del posicionamiento de todas las fuerzas políticas respecto a la actuación presentada.</li>
<li><strong>¿Qué diferencia existe entre los debates y las propuestas?</strong><br>
Aunque ambos pueden ser apoyados, los primeros no activan ningún mecanismo de actuación concreto, mientras que las segundas pasan a una fase de decisión colectiva y en caso de que sean aprobadas, son asumidas por el Ayuntamiento.</li>
<li><strong>¿Vota toda la gente de Madrid en cada propuesta? ¿incluso aunque haga referencia a un único distrito?</strong><br>
Sí. El mecanismo actual se desarrolla a nivel de toda la ciudad. Lo que no entra en colisión con que a nivel distrital se puedan desarrollar mecanismos de participación ciudadana que afecten únicamente a cada distrito.</li>
<li><strong>¿Y si se presentan varias propuestas iguales? ¿Se plantea la posibilidad de unificarlas para evitar que se diversifiquen los apoyos y/o votos?</strong><br>
Propuestas similares pueden ser marcadas como tales en la plataforma, lo que se señala al visualizar cada una de ellas. Más allá de eso no se unifican, dejando que sea la gente la que decida si apoyar a una o a otra.</li>
<li><strong>¿Durante cuanto tiempo se pueden recoger apoyos?</strong><br>
Las propuestas pueden recoger apoyos durante 12 meses. Si en ese tiempo no consiguen alcanzar el 2% de los apoyos necesarios dejan de poder recibir más apoyos.</li>
<li><strong>¿Durante cuanto tiempo se votará cada propuesta?</strong><br>
Una vez que las propuestas reciben el apoyo del 2% pasan a la fase de decisión final donde la gente puede votar aceptando o rechazando la propuesta durante un plazo de una semana.</li>
<li><strong>¿Pueden participar asociaciones, fundaciones y ONG´s? ¿Y empresas?</strong><br>
Las propuestas tienen que ser presentadas individualmente, sin problema de que la persona que la presenta represente a una organización de cualquier tipo. Al registrar una propuesta en la web existe la posibilidad de señalar este caso como registro de asociaciones/colectivos.</li>
<li><strong>¿Cuánta gente tiene que votar una propuesta para que sea aprobada? ¿Qué quórum mínimo se necesita para que las votaciones sean vinculantes?</strong><br>
El quórum es el mínimo de participación necesaria para considerar una votación vinculante de manera legal. Ningún reglamento del Ayuntamiento puede hacer que este mecanismo sea vinculante juridicamente, porque eso está en contra de la legislación española. La vinculación con el mecanismo es política y se asume de manera personal por los concejales y la Alcaldesa. Por ello no se considerará ningún quórum.</li>
<li><strong>¿Existen mecanismos presenciales para participar? ¿Se ha planteado llegar a los ciudadanos y ciudadanas con dificultades de acceso a Internet o en situación de exclusión?</strong><br>
Todas las acciones relacionadas con el proceso de propuestas ciudadanas pueden realizarse presencialmente en cualquiera de las <a href="http://www.madrid.es/portales/munimadrid/es/Inicio/El-Ayuntamiento/Atencion-al-ciudadano/Oficinas-de-Atencion-al-Ciudadano?vgnextfmt=default&vgnextchannel=5b99cde2e09a4310VgnVCM1000000b205a0aRCRD" target="_blank">26 Oficinas de Atención al Ciudadano</a> repartidas por todos los distritos de Madrid. Además, el proceso de recogida de apoyos de una propuesta puede realizarse también a través de hojas de firmas, cuyo modelo puede ser <a href="/docs/formulario_propuestas_ciudadanas.pdf" target="_blank">descargado en este enlace</a>.
<br>Adicionalmente se ha creado en el Área de Gobierno de Participación Ciudadana, Transparencia y Gobierno Abierto el Servicio de Inclusión, Neutralidad y Privacidad que pondrá en marcha una mesa de inclusión con personal del Ayuntamiento y asociaciones que trabajan con colectivos en situación de exclusión, para diseñar mecanismos especiales para que puedan participar dichos colectivos.</li>
<li><strong>¿Cómo puede participar la gente que no esté empadronada en Madrid?</strong><br>
Presentando propuestas, participando en los debates, y difundiendo lo que ocurra en la plataforma.</li>
<li><strong>¿El voto es secreto? ¿el Ayuntamiento podrá tener acceso a los votos de los ciudadanos?</strong><br>
El voto es totalmente secreto. Se encripta de manera conjunta con diferentes autoridades externas al Ayuntamiento, de tal manera que para poder conocer la identidad de un votante todas las autoridades tendrían que realizar fraude conjuntamente.</li>
<li><strong>¿Al ser electrónico el voto no se aumenta el riesgo de fraude?</strong>
No, incluso lo reducimos. Hay una sensación subjetiva muy fuerte de diferencia de seguridad entre procesos en papel y procesos electrónicos que no se corresponde con la realidad. En las elecciones generales donde el voto es en papel, después de que las mesas hayan hecho el recuento, las papeletas de los votos se tiran a la basura, y la persona responsable se dirige en persona con el recuento en la mano al centro donde se comparten los datos. Y sin embargo pensemos por ejemplo en el sistema de tarjetas de crédito y cuentas bancarias, donde cada día confiamos la seguridad de nuestro dinero a un sistema electrónico. En el caso del Ayuntamiento, la votación está asegurada mediante diferentes entidades externas, y además cualquier votante puede tanto comprobar que su voto particular está en el recuento final, como realizar el recuento completo de todos los votos por su cuenta. Dos garantías de seguridad completamente imposibles en un votación tradicional.</li>
<li><strong>¿Cómo se sabe que una propuesta se ha cumplido?</strong><br>
Cuando se aprueba una propuesta, el Ayuntamiento publica en la página web cuáles son los pasos que se van a realizar para ponerla en práctica, incluyendo los informes que las correspondientes áreas redacten y las acciones que se emprenden. La información es completa y transparente para que puedas hacer un seguimiento de cómo evoluciona cada propuesta ciudadana.</li>
<h2 id="iii">Hojas de firmas para recoger apoyos</h2>
<p>El proceso de recogida de apoyos de una propuesta, además de en la web, puede realizarse a través de hojas de firmas, cuyo modelo puede ser <a href="/docs/formulario_propuestas_ciudadanas.pdf" target="_blank">descargado en este enlace</a>.</p>
<p>La hoja debe contener en las casillas superiores el código de la propuesta y su título, según figura en la página web específica de la propuesta, dentro del Portal de Gobierno Abierto.</p>
<p>Los apoyos recogidos de esta manera se sumarán a los apoyos ya existentes en el portal de gobierno abierto. Las hojas pueden ser entregadas en cualquiera de los Registros del Ayuntamiento, presentes en cada una de las Juntas de Distrito. <a href="http://www.madrid.es/portales/munimadrid/es/Inicio/Ayuntamiento/Oposiciones-y-Empleo/Direcciones-y-telefonos/Oficinas-de-Registro-del-Ayuntamiento-de-Madrid?vgnextfmt=default&vgnextoid=adedb88db777f010VgnVCM2000000c205a0aRCRD&vgnextchannel=ddc3ca1c5a057010VgnVCM100000dc0ca8c0RCRD" target="_blank">Ver la lista completa de Oficinas de Registro</a>.</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,10 @@
<% if can? :hide, proposal %>
<%= link_to t("admin.actions.hide").capitalize, hide_moderation_proposal_path(proposal),
method: :put, remote: true, data: { confirm: t('admin.actions.confirm') } %>
<% end %>
<% if can? :hide, proposal.author %>
&nbsp;|&nbsp;
<%= link_to t("admin.actions.hide_author").capitalize, hide_moderation_user_path(proposal.author_id),
method: :put, data: { confirm: t('admin.actions.confirm') } %>
<% end %>

View File

@@ -0,0 +1,29 @@
<% cache [locale_and_user_status, commentable_cache_key(@proposal), @all_visible_comments, @all_visible_comments.map(&:author), @proposal.comments_count, @comment_flags] do %>
<section class="row-full comments">
<div class="row">
<div id="comments" class="small-12 column">
<h2>
<%= t("proposals.show.comments_title") %>
<span>(<%= @proposal.comments_count %>)</span>
</h2>
<% if user_signed_in? %>
<%= render 'comments/form', {commentable: @proposal, parent_id: nil, toggeable: false} %>
<% else %>
<br>
<div class="alert-box radius info">
<%= t("proposals.show.login_to_comment",
signin: link_to(t("votes.signin"), new_user_session_path),
signup: link_to(t("votes.signup"), new_user_registration_path)).html_safe %>
</div>
<% end %>
<% @root_comments.each do |comment| %>
<%= render 'comments/comment', comment: comment %>
<% end %>
<%= paginate @root_comments %>
</div>
</div>
</section>
<% end %>

View File

@@ -0,0 +1,23 @@
<span class="flag-content">
<% if show_flag_action? proposal %>
<a id="flag-expand-proposal-<%= proposal.id %>" data-dropdown="flag-drop-proposal-<%= proposal.id %>" aria-controls="flag-drop-proposal-<%= proposal.id %>" aria-expanded="false" title="<%= t('shared.flag') %>">
&nbsp;<i class="icon-flag flag-disable"></i>&nbsp;&nbsp;
</a>
<ul id="flag-drop-proposal-<%= proposal.id %>" class="f-dropdown" data-dropdown-content aria-hidden="true" tabindex="-1">
<li>
<%= link_to t('shared.flag'), flag_proposal_path(proposal), method: :put, remote: true, id: "flag-proposal-#{ proposal.id }" %>
</li>
</ul>
<% end %>
<% if show_unflag_action? proposal %>
<a id="unflag-expand-proposal-<%= proposal.id %>" data-dropdown="unflag-drop-proposal-<%= proposal.id %>" aria-controls="unflag-drop-proposal-<%= proposal.id %>" aria-expanded="false" title="<%= t('shared.unflag') %>">
&nbsp;<i class="icon-flag flag-active"></i>&nbsp;&nbsp;
</a>
<ul id="unflag-drop-proposal-<%= proposal.id %>" class="f-dropdown" data-dropdown-content aria-hidden="true" tabindex="-1">
<li>
<%= link_to t('shared.unflag'), unflag_proposal_path(proposal), method: :put, remote: true, id: "unflag-proposal-#{ proposal.id }" %>
</li>
</ul>
<% end %>
</span>

View File

@@ -0,0 +1,84 @@
<%= form_for(@proposal) do |f| %>
<%= render 'shared/errors', resource: @proposal %>
<div class="row">
<div class="small-12 column">
<%= f.label :title, t("proposals.form.proposal_title") %>
<%= f.text_field :title, maxlength: Proposal.title_max_length, placeholder: t("proposals.form.proposal_title"), label: false %>
</div>
<div class="small-12 column">
<%= f.label :question, t("proposals.form.proposal_question") %>
<span class="note-marked">
<%= t("proposals.form.proposal_question_example_html") %>
</span>
<%= f.text_field :question, maxlength: Proposal.question_max_length, placeholder: t("proposals.form.proposal_question"), label: false %>
</div>
<div class="small-12 column">
<%= f.label :summary, t("proposals.form.proposal_summary") %>
<span class="note"><%= t("proposals.form.proposal_summary_note") %></span>
<%= f.text_area :summary, rows: 4, maxlength: 200, label: false,
placeholder: t('proposals.form.proposal_summary') %>
</div>
<div class="ckeditor small-12 column">
<%= f.label :description, t("proposals.form.proposal_text") %>
<%= f.cktext_area :description, maxlength: Proposal.description_max_length, ckeditor: { language: I18n.locale }, label: false %>
</div>
<div class="small-12 column">
<%= f.label :video_url, t("proposals.form.proposal_video_url") %>
<span class="note"><%= t("proposals.form.proposal_video_url_note") %></span>
<%= f.text_field :video_url, placeholder: t("proposals.form.proposal_video_url"), label: false %>
</div>
<div class="small-12 column">
<%= f.label :external_url, t("proposals.form.proposal_external_url") %>
<%= f.text_field :external_url, placeholder: t("proposals.form.proposal_external_url"), label: false %>
</div>
<div class="small-12 column">
<%= f.label :tag_list, t("proposals.form.tags_label") %>
<span class="note"><%= t("proposals.form.tags_instructions") %></span>
<span class="tags">
<% @featured_tags.each do |tag| %>
<a class="js-add-tag-link"><%= tag.name %></a>
<% end %>
</span>
<%= f.text_field :tag_list, value: @proposal.tag_list.to_s, label: false, placeholder: t("proposals.form.tags_placeholder"), class: 'js-tag-list' %>
</div>
<% if current_user.unverified? %>
<div class="small-12 column">
<%= f.label :responsible_name, t("proposals.form.proposal_responsible_name") %>
<span class="note"><%= t("proposals.form.proposal_responsible_name_note") %></span>
<%= f.text_field :responsible_name, placeholder: t("proposals.form.proposal_responsible_name"), label: false %>
</div>
<% end %>
<div class="small-12 column">
<% if @proposal.new_record? %>
<%= f.label :terms_of_service do %>
<%= f.check_box :terms_of_service, label: false %>
<span class="checkbox">
<%= t("form.accept_terms",
policy: link_to(t("form.policy"), "/privacy", target: "blank"),
conditions: link_to(t("form.conditions"), "/conditions", target: "blank")).html_safe %>
</span>
<% end %>
<% end %>
</div>
<div class="small-12 column">
<%= f.simple_captcha input_html: { required: false } %>
</div>
<div class="actions small-12 column">
<%= f.submit(class: "button radius", value: t("proposals.#{action_name}.form.submit_button")) %>
</div>
</div>
<% end %>

View 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>&nbsp;
<%= link_to t("proposals.proposal.comments", count: proposal.comments_count), proposal_path(proposal, anchor: "comments") %>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<%= l proposal.created_at.to_date %>
</p>
<div class="proposal-description">
<%= link_to proposal.summary, 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>

View File

@@ -0,0 +1 @@
$("#<%= dom_id(@proposal) %> .js-flag-actions").html('<%= j render("proposals/flag_actions", proposal: @proposal) %>');

View File

@@ -0,0 +1,51 @@
<div class="supports">
<div class="progress small-12 round">
<span class="meter" style="width: <%= progress_bar_percentage(proposal) %>%;"></span>
</div>
<span class="total-supports">
<%= t("proposals.proposal.supports", count: proposal.total_votes) %>&nbsp;
(<%= supports_percentage(proposal) %>)
<span>
<abbr title="<%= t("proposals.proposal.census_percent") %>">
<%= t("proposals.proposal.supports_necessary") %>
</abbr>
</span>
</span>
<div class="in-favor">
<% if voted_for?(@proposal_votes, proposal) %>
<div class="supported">
<%= t("proposals.proposal.already_supported") %>
</div>
<% else %>
<%= link_to vote_proposal_path(proposal, value: 'yes'),
class: "button button-support tiny radius expand",
title: t('proposals.proposal.support_title'), method: "post", remote: true do %>
<%= t("proposals.proposal.support") %>
<% end %>
<% end %>
</div>
<% if user_signed_in? && current_user.organization? %>
<div class="organizations-votes" style='display:none'>
<p>
<%= t("votes.organizations") %>
</p>
</div>
<% elsif user_signed_in? && !proposal.votable_by?(current_user)%>
<div class="anonymous-votes" style='display:none'>
<p>
<%= t("votes.verified_only",
verify_account: link_to(t("votes.verify_account"), verification_path )).html_safe %>
</p>
</div>
<% elsif !user_signed_in? %>
<div class="not-logged" style='display:none'>
<%= t("votes.unauthenticated",
signin: link_to(t("votes.signin"), new_user_session_path),
signup: link_to(t("votes.signup"), new_user_registration_path)).html_safe %>
</div>
<% end %>
</div>

View File

@@ -0,0 +1,17 @@
<div class="proposal-edit row">
<div class="small-12 column">
<%= link_to proposals_path, class: "left back clear" do %>
<i class="icon-angle-left left"></i>
<%= t("proposals.edit.back_link") %>
<% end %>
<div class="right">
<%= link_to t("proposals.edit.show_link"), @proposal %>
</div>
<h1><%= t("proposals.edit.editing") %></h1>
<%= render "form" %>
</div>
</div>

View File

@@ -0,0 +1,58 @@
<% content_for :header_addon do %>
<%= render "shared/search_form_header",
search_path: proposals_path(page: 1),
i18n_namespace: "proposals.index.search_form" %>
<% end %>
<section role="main">
<div class="wrap row">
<div id="proposals" class="proposals-list small-12 medium-9 column">
<div class="filters">
<div class="small-12 medium-7 left">
<% 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 margin-top inline-block proposals-order">
<h6 class="inline-block">
<%= t("proposals.index.select_order") %>
</h6>
<%= render 'shared/order_selector', i18n_namespace: "proposals.index" %>
</div>
<% else %>
<div class="small-12 inline-block">
<h2 class="inline-block">
<%= t("proposals.index.select_order_long") %>
</h2>
<%= render 'shared/order_selector', i18n_namespace: "proposals.index" %>
</div>
<% end %>
<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>
<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", taggable: 'proposal' %>
</aside>
</div>
</div>
</section>

View File

@@ -0,0 +1,26 @@
<div class="proposal-new row">
<div class="small-12 medium-9 column">
<%= link_to proposals_path, class: "left back" do %>
<i class="icon-angle-left left"></i>
<%= t("proposals.new.back_link") %>
<% end %>
<h1><%= t("proposals.new.start_new") %></h1>
<div class="alert-box info radius">
<%= link_to "/proposals_info", target: "_blank" do %>
<%= t("proposals.new.more_info")%>
<% end %>
</div>
<%= render "form" %>
</div>
<div class="small-12 medium-3 column">
<i class="icon-proposals right"></i>
<h2><%= t("proposals.new.recommendations_title") %></h2>
<ul class="recommendations">
<li><%= t("proposals.new.recommendation_one") %></li>
<li><%= t("proposals.new.recommendation_two") %></li>
<li><%= t("proposals.new.recommendation_three") %></li>
</ul>
</div>
</div>

View File

@@ -0,0 +1,99 @@
<% cache [locale_and_user_status(@proposal), @proposal, @proposal.author, Flag.flagged?(current_user, @proposal), @proposal_votes] do %>
<section class="proposal-show">
<div id="<%= dom_id(@proposal) %>" class="row">
<div class="small-12 medium-9 column">
<i class="icon-angle-left left"></i>&nbsp;
<%= link_to t("proposals.show.back_link"), proposals_path, class: 'left back' %>
<% if current_user && @proposal.editable_by?(current_user) %>
<%= link_to edit_proposal_path(@proposal), class: 'edit-proposal button success tiny radius right' do %>
<i class="icon-edit"></i>
<%= t("proposals.show.edit_proposal_link") %>
<% end %>
<% end %>
<h1><%= @proposal.title %></h1>
<% if @proposal.conflictive? %>
<div class="alert-box alert radius margin-top">
<strong><%= t("proposals.show.flag") %></strong>
</div>
<% end %>
<div class="proposal-info">
<%= avatar_image(@proposal.author, seed: @proposal.author_id, size: 32, class: 'author-photo') %>
<% if @proposal.author.hidden? %>
<i class="icon-deleted author-deleted"></i>
<span class="author">
<%= t("proposals.show.author_deleted") %>
</span>
<% else %>
<span class="author">
<%= @proposal.author.name %>
</span>
<% if @proposal.author.official? %>
&nbsp;&bull;&nbsp;
<span class="label round level-<%= @proposal.author.official_level %>">
<%= @proposal.author.official_position %>
</span>
<% end %>
<% end %>
<% if @proposal.author.verified_organization? %>
&nbsp;&bull;&nbsp;
<span class="label round is-association">
<%= t("shared.collective") %>
</span>
<% end %>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<%= l @proposal.created_at.to_date %>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<i class="icon-comments"></i>&nbsp;
<%= link_to t("proposals.show.comments", count: @proposal.comments_count), "#comments" %>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<%= @proposal.code %>
<span class="bullet">&nbsp;&bull;&nbsp;</span>
<span class="js-flag-actions">
<%= render 'proposals/flag_actions', proposal: @proposal %>
</span>
</div>
<div><%= @proposal.summary %></div>
<%= safe_html_with_links @proposal.description %>
<% if @proposal.external_url.present? %>
<div><%= text_with_links @proposal.external_url %></div>
<% end %>
<% if @proposal.video_url.present? %>
<div><%= text_with_links @proposal.video_url %></div>
<% end %>
<h4><%= @proposal.question %></h4>
<%= render 'shared/tags', proposal: @proposal %>
<div class='js-moderator-proposal-actions'>
<%= render 'actions', proposal: @proposal %>
</div>
</div>
<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 'proposals/votes', proposal: @proposal %>
</div>
</div>
<div class="sidebar-divider"></div>
<h3><%= t("proposals.show.share") %></h3>
<%= social_share_button_tag(@proposal.title) %>
</aside>
</div>
</section>
<% end %>
<%= render "comments" %>

View File

@@ -0,0 +1 @@
$("#<%= dom_id(@proposal) %>_votes").html('<%= j render("proposals/votes", proposal: @proposal) %>');

View File

@@ -0,0 +1,15 @@
<% # Params:
#
# i18n_namespace: for example "moderation.debates.index"
%>
<form class="inline-block">
<select class="js-location-changer js-order-selector" data-order="<%= @current_order %>" name="order-selector">
<% @valid_orders.each do |order| %>
<option <%= 'selected' if order == @current_order %>
value='<%= current_path_with_query_params(order: order, page: 1) %>'>
<%= t("#{i18n_namespace}.orders.#{order}") %>
</option>
<% end %>
</select>
</form>

View File

@@ -1,21 +1,27 @@
<% # Params:
#
# search_path: for example debates_path
# i18n_namespace: for example "debates.index.search_form"
%>
<div class="search-form">
<div class="sidebar-divider"></div>
<h3><%= t("shared.search_form.search_title") %></h3>
<h3><%= t("#{i18n_namespace}.title") %></h3>
<br>
<div class="row">
<%= form_tag debates_path, method: :get do %>
<%= form_tag search_path, method: :get do %>
<div class="small-12 columns">
<div class="row collapse">
<div class="small-9 medium-12 large-9 columns">
<input type="text" name="search" placeholder="<%= t("shared.search_form.search_placeholder") %>" class="search-form">
<input type="text" name="search" placeholder="<%= t("#{i18n_namespace}.placeholder") %>" class="search-form">
</div>
<div class="small-3 medium-12 large-3 columns">
<input type="submit" class="button secondary postfix" value="<%= t('shared.search_form.search_button') %>">
<input type="submit" class="button secondary postfix" value="<%= t("#{i18n_namespace}.button") %>">
</div>
</div>
</div>
<% end %>
</div>
</div>
</div>

View File

@@ -1,14 +1,20 @@
<% # Params:
#
# i18n_namespace: for example "debates.index.search_form"
# search_path: for example debates_path
%>
<div class="search-form">
<div class="row">
<%= form_tag debates_path, method: :get do %>
<%= form_tag search_path, method: :get do %>
<div class="small-12 columns">
<div class="row collapse">
<div class="small-10 column">
<input type="text" name="search" placeholder="<%= t("shared.search_form.search_placeholder") %>" class="search-form">
<input type="text" name="search" placeholder="<%= t("#{i18n_namespace}.placeholder") %>" class="search-form">
</div>
<div class="small-2 column">
<button type="submit" class="button postfix" title="<%= t('shared.search_form.search_button') %>">
<button type="submit" class="button postfix" title="<%= t("#{i18n_namespace}.button") %>">
<i class="icon-search"></i>
</button>
</div>

View File

@@ -3,6 +3,6 @@
<h3><%= t("shared.tags_cloud.tags") %></h3>
<br>
<% tag_cloud @tag_cloud, %w[s m l] do |tag, css_class| %>
<%= link_to sanitize("#{tag.name} <span class='label round info'>#{tag.taggings_count}</span>"), debates_path(tag: tag.name), class: css_class %>
<%= link_to sanitize("#{tag.name} <span class='label round info'>#{tag.taggings_count}</span>"), taggable_path(taggable, tag.name), class: css_class %>
<% end %>
</div>

View File

@@ -1,13 +1,15 @@
<%- limit ||= nil %>
<%- taggable = defined?(debate) ? debate : proposal %>
<% if debate.tags.any? %>
<% if taggable.tags.any? %>
<span class='tags'>
<% debate.tag_list_with_limit(limit).each do |tag| %>
<%= link_to sanitize(tag.name), debates_path(tag: tag.name) %>
<% taggable.tag_list_with_limit(limit).each do |tag| %>
<%= link_to sanitize(tag.name), send("#{taggable.class.to_s.downcase.pluralize}_path", tag: tag.name) %>
<% end %>
<% if debate.tags_count_out_of_limit(limit) > 0 %>
<%= link_to "#{debate.tags_count_out_of_limit(limit)}+", debate_path(debate) %>
<% if taggable.tags_count_out_of_limit(limit) > 0 %>
<%= link_to "#{taggable.tags_count_out_of_limit(limit)}+",
send("#{taggable.class.to_s.downcase}_path", taggable) %>
<% end %>
</span>
<% end %>
<% end %>

View File

@@ -1,4 +1,9 @@
<div class="verification account row">
<div class="alert-box success radius">
<%= t('verification.letter.create.flash.success') %>
</div>
<div class="small-12 column">
<div class="text-center">
@@ -26,6 +31,13 @@
<h1><%= t("verification.letter.edit.title") %></h1>
<%= t("verification.letter.new.explanation_html") %>
<%= link_to t("verification.letter.new.offices"), t("verification.letter.new.offices_url"),
target: "_blank", class: "button radius inline-block"
%>
<div class="small-12 medium-6">
<%= form_for @letter, as: "letter", url: letter_path, method: :put do |f| %>
<%= f.text_field :verification_code, label: t("verification.letter.edit.confirmation_code") %>

View File

@@ -1,30 +0,0 @@
<% cache [locale_and_user_status, featured_debate, @debate_votes[featured_debate.id],'featured'] do %>
<div class="small-12 medium-4 column end">
<div id="<%= dom_id featured_debate %>" class="debate-featured">
<div class="panel">
<div class="debate-content">
<span class="label left"><%= t("debates.debate.debate") %></span>
<i class="icon-debates right"></i>
<h3><%= link_to featured_debate.title, featured_debate %></h3>
<p class="debate-info">
<i class="icon-comments"></i>&nbsp;
<%= link_to t("debates.show.comments", count: featured_debate.comments_count), debate_path(featured_debate, anchor: "comments") %>
</p>
<div class="debate-description">
<%= link_to featured_debate.description, featured_debate %>
<div class="truncate"></div>
</div>
<%= render "shared/tags", debate: featured_debate, limit: 5 %>
</div>
<div class="row">
<div id="<%= dom_id(featured_debate) %>_votes" class="small-12 column">
<%= render 'debates/votes', debate: featured_debate %>
</div>
</div>
</div>
</div>
</div>
<% end %>

View File

@@ -0,0 +1,27 @@
<% cache [locale_and_user_status, @list, @debate_votes, @proposal_votes, @paginator.current_page, @paginator.total_pages] do %>
<section role="main">
<div class="wrap row">
<div id="activities" class="debates-list small-12 medium-9 column">
<div class="filters">
<div class="small-12 medium-7 left">
<h2 class="margin-top">
<%= t('welcome.signed_in_home_title') %>
</h2>
</div>
<%= render @list %>
<%= paginate @paginator %>
</div>
</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' %>
<%= link_to t("debates.index.start_debate"), new_debate_path, class: 'button radius expand' %>
</aside>
</div>
</div>
</section>
<% end %>

View File

@@ -1,12 +1,28 @@
<% cache [locale_and_user_status, @featured_debates, @debate_votes, 'featured'] do %>
<% cache [locale_and_user_status, @featured_debates, @featured_proposals, 'featured'] do %>
<section role="main">
<div class="filters row">
<div class="small-12 column">
<h2><%= t("welcome.last_debates") %></h2>
<div class="row text-center">
<div class="small-12 medium-3 column">
<%= image_tag("icon_home_debates.png") %>
<h2><%= t("welcome.debates.title") %></h2>
<p><%= t("welcome.debates.description") %></p>
</div>
<div class="small-12 medium-3 column">
<%= image_tag("icon_home_propones.png") %>
<h2><%= t("welcome.proposal.title") %></h2>
<p><%= t("welcome.proposal.description") %></p>
</div>
<div class="small-12 medium-3 column">
<%= image_tag("icon_home_decides.png") %>
<h2><%= t("welcome.decide.title") %></h2>
<p><%= t("welcome.decide.description") %></p>
</div>
<div class="small-12 medium-3 column">
<%= image_tag("icon_home_sehace.png") %>
<h2><%= t("welcome.do.title") %></h2>
<p><%= t("welcome.do.description") %></p>
</div>
</div>
<div id="featured-debates" class="row">
<%= render partial: "featured_debate", collection: @featured_debates %>
</div>
</section>
<% end %>

View File

@@ -104,11 +104,17 @@ ignore_unused:
- 'admin.officials.level_*'
- 'admin.comments.index.filter*'
- 'admin.debates.index.filter*'
- 'admin.proposals.index.filter*'
- 'admin.organizations.index.filter*'
- 'admin.users.index.filter*'
- 'moderation.comments.index.filter*'
- 'moderation.debates.index.filter*'
- 'moderation.proposals.index.filter*'
- 'moderation.proposals.index.order*'
- 'debates.index.orders.*'
- 'debates.index.search_form.*'
- 'proposals.index.orders.*'
- 'proposals.index.search_form.*'
- 'helpers.page_entries_info.*' # kaminari
- 'views.pagination.*' # kaminari
# - '{devise,kaminari,will_paginate}.*'

View File

@@ -8,3 +8,50 @@ Kaminari.configure do |config|
# config.page_method_name = :page
# config.param_name = :page
end
# Overrides for making Kaminari handle i18n pluralization correctly
#
# Remove everything below once https://github.com/amatsuda/kaminari/pull/694 is
# merged in Kaminari and we have updated
module Kaminari
module ActionViewExtension
def page_entries_info(collection, options = {})
entry_name = if options[:entry_name]
options[:entry_name].pluralize(collection.size)
else
collection.entry_name(:count => collection.size).downcase
end
if collection.total_pages < 2
t('helpers.page_entries_info.one_page.display_entries', entry_name: entry_name, count: collection.total_count)
else
first = collection.offset_value + 1
last = collection.last_page? ? collection.total_count : collection.offset_value + collection.limit_value
t('helpers.page_entries_info.more_pages.display_entries', entry_name: entry_name, first: first, last: last, total: collection.total_count)
end.html_safe
end
end
module ActiveRecordRelationMethods
def entry_name(options = {})
model_name.human(options.reverse_merge(default: model_name.human.pluralize(options[:count])))
end
end
module MongoidCriteriaMethods
def entry_name(options = {})
model_name.human(options.reverse_merge(default: model_name.human.pluralize(options[:count])))
end
end
class PaginatableArray < Array
ENTRY = 'entry'.freeze
def entry_name(options = {})
I18n.t('helpers.page_entries_info.entry', options.reverse_merge(default: ENTRY.pluralize(options[:count])))
end
end
end

View File

@@ -3,6 +3,10 @@ ActsAsVotable::Vote.class_eval do
where(votable_type: 'Debate', votable_id: debates)
end
def self.for_proposals(proposals)
where(votable_type: 'Proposal', votable_id: proposals)
end
def value
vote_flag
end

View File

@@ -16,6 +16,12 @@ en:
description: Opinion
terms_of_service: Terms of service
title: Title
proposal:
author: Author
title: Title
question: Question
description: Description
terms_of_service: Terms of service
user:
email: Email
username: Username

View File

@@ -1,12 +1,33 @@
es:
activerecord:
models:
comment: Comentario
debate: Debate
tag: Tema
user: Usuario
vote: Voto
organization: Organización
comment:
one: Comentario
other: Comentarios
debate:
one: Debate
other: Debates
tag:
one: Tema
other: Temas
user:
one: Usuario
other: Usuarios
moderator:
one: Moderador
other: Moderadores
administrator:
one: Administrador
other: Administradores
vote:
one: Voto
other: Voto
organization:
one: Organización
other: Organizaciones
proposal:
one: Propuesta ciudadana
other: Propuestas ciudadanas
attributes:
comment:
body: Comentario
@@ -16,6 +37,12 @@ es:
description: Opinión
terms_of_service: Términos de servicio
title: Título
proposal:
author: Autor
title: Título
question: Pregunta
description: Descripción
terms_of_service: Términos de servicio
user:
email: Correo electrónico
username: Nombre de usuario

View File

@@ -12,6 +12,7 @@ en:
menu:
settings: Global settings
debate_topics: Debate topics
hidden_proposals: Hidden proposals
hidden_debates: Hidden debates
hidden_comments: Hidden comments
hidden_users: Blocked users
@@ -71,6 +72,14 @@ en:
all: All
with_confirmed_hide: Confirmed
without_confirmed_hide: Pending
proposals:
index:
title: Hidden proposals
filter: Filter
filters:
all: All
with_confirmed_hide: Confirmed
without_confirmed_hide: Pending
users:
index:
title: Banned users

View File

@@ -12,6 +12,7 @@ es:
menu:
settings: Configuración global
debate_topics: Temas de debate
hidden_proposals: Propuestas ocultas
hidden_debates: Debates ocultos
hidden_comments: Comentarios ocultos
hidden_users: Usuarios bloqueados
@@ -71,6 +72,14 @@ es:
all: Todos
with_confirmed_hide: Confirmados
without_confirmed_hide: Pendientes
proposals:
index:
title: Propuestas ocultos
filter: Filtro
filters:
all: Todas
with_confirmed_hide: Confirmadas
without_confirmed_hide: Pendientes
users:
index:
title: Usuarios bloqueados

View File

@@ -12,23 +12,25 @@ en:
open: "Open"
participation: Participation
open_city_title: "The city you want, it will be the city you want."
open_city_slogan:
open_city_slogan_html:
"Start listening to Madrid.
For that we open this digital Puerta del Sol, where all the locals can meet to discuss and share everything we want."
# "Existen ciudades gobernadas directamente por sus habitantes, que <b>debaten</b> sobre temas que les preocupan, <b>proponen</b> ideas para mejorar
# sus vidas y <b>deciden</b> entre todas y todos las que se llevan a cabo. Madrid ya es una de ellas."
# open_city_text:
# "Here every voice has its place and are citizens, and no one in their name, they decide to vote debates what issues are
# most important of every time. Government officials have individual users with whom you may participate in the debates, and evaluated,
# the same level as everyone else. Because the Madrid City Council works for its citizens, and must respond to them."
# open_city_soon: "And soon... we'll open the section for citizen proposals."
see_all_debates: See all debates
see_all: See debates and proposals
my_account_link: My account
locale: "Language:"
administration: Administration
moderation: Moderation
# welcome: Welcome
highlights: "Highlights"
more_information: "More information"
debates: Debates
# initiatives: Initiatives
proposals: Proposals
footer:
description: "This portal use %{consul}, is %{open_source}. Madrid, for the whole world."
open_source: "software libre"
@@ -55,6 +57,7 @@ en:
conditions: "Terms of use"
user: account
debate: debate
proposal: proposal
verification::sms: phone
application:
alert:
@@ -73,6 +76,10 @@ en:
filter_topic:
one: " with the topic '%{topic}'"
other: " with the topic '%{topic}'"
search_form:
title: Search
button: Search
placeholder: "Search debates..."
search_results:
one: " containing '%{search_term}'"
other: " containing '%{search_term}'"
@@ -86,20 +93,6 @@ en:
zero: No votes
one: 1 vote
other: "%{count} votes"
comment:
author: Debate's author
moderator: Moderator
admin: Administrator
deleted: This comment has been deleted
user_deleted: Deleted user
responses:
zero: No Responses
one: 1 Response
other: "%{count} Responses"
votes:
zero: No votes
one: 1 vote
other: "%{count} votes"
form:
debate_title: Debate title
debate_text: Initial text for debate
@@ -143,11 +136,111 @@ en:
update:
form:
submit_button: "Save changes"
proposals:
index:
start_proposal: Start a proposal
select_order: Order by
select_order_long: Order proposals by
orders:
confidence_score: best rated
hot_score: most active
created_at: newest
most_commented: most commented
random: random
filter_topic:
one: " with the topic '%{topic}'"
other: " with the topic '%{topic}'"
search_form:
title: Search
button: Search
placeholder: "Search proposals..."
search_results:
one: " containing '%{search_term}'"
other: " containing '%{search_term}'"
proposal:
proposal: Proposal
support: "Support"
support_title: "Support this proposal"
comments:
zero: No comments
one: 1 Comment
other: "%{count} Comments"
supports:
zero: No supports
one: 1 support
other: "%{count} supports"
supports_necessary: "53.726 necessary supports"
census_percent: "2% of census"
already_supported: "You already supported this proposal!"
form:
proposal_title: Proposal title
proposal_question: Proposal question
proposal_question_example_html: "Debe ser resumida en una pregunta cuya respuesta sea Sí o No. <em>Ej. '¿Está usted de acuerdo en peatonalizar la calle Mayor?'</em>"
proposal_text: Initial text for proposal
proposal_external_url: Link to additional documentation
proposal_video_url: "Enlace a vídeo externo"
proposal_video_url_note: "Puedes añadir un enlace a YouTube o Vimeo"
proposal_summary: "Proposal summary"
proposal_summary_note: "200 chars. maximum"
proposal_responsible_name: "First and last name of the person making this proposal"
proposal_responsible_name_note: "(individualmente o como representante de un colectivo; no se mostrará públicamente)"
tags_label: Topics
tags_instructions: >
Tag this proposal. You can choose among our proposals on the list or add any other topic you want.
tags_placeholder: "Add topics writing them separated by ','"
show:
back_link: Back
author_deleted: Deleted user
comments_title: Comments
comments:
zero: No comments
one: 1 Comment
other: "%{count} Comments"
login_to_comment: "You need to %{signin} or %{signup} to comment."
edit_proposal_link: Edit
share: Share
flag: "This proposal has been flag as innapropiate for some users."
edit:
editing: Edit proposal
show_link: Show proposal
back_link: Back
form:
submit_button: "Save changes"
new:
start_new: Start a proposal
more_info: "¿Cómo funcionan las propuestas ciudadanas?"
back_link: Back
recommendations_title: Tips for creating a proposal
recommendation_one: "Do not write the title of the proposal or whole sentences in capital. On the Internet this is considered shouting. And nobody likes to scream."
recommendation_two: "Any discussion or comment that involves an illegal act will be eliminated, also intending to sabotage the proposal spaces, everything else is permitted."
recommendation_three: "Enjoy this space, voices that fill it, it is yours too."
form:
submit_button: "Start a proposal"
create:
form:
submit_button: "Start a proposal"
update:
form:
submit_button: "Save changes"
comments:
form:
leave_comment: Write a comment
comment_as_moderator: Comment as moderator
comment_as_admin: Comment as administrator
comment:
author: Author
moderator: Moderator
admin: Administrator
deleted: This comment has been deleted
user_deleted: Deleted user
responses:
zero: No Responses
one: 1 Response
other: "%{count} Responses"
votes:
zero: No votes
one: 1 vote
other: "%{count} votes"
comments_helper:
comment_link: Comment
comment_button: Publish comment
@@ -160,14 +253,15 @@ en:
unauthenticated: "You need to %{signin} or %{signup} before continuing."
signin: sign in
signup: sign up
anonymous: "This debate already has too much anonymous votes, %{verify_account} to vote."
anonymous: "Too many anonymous votes, %{verify_account} to vote."
verified_only: "Proposals can only be voted by verified users, %{verify_account}."
verify_account: verify your account
organizations: Organizations can not vote
account:
show:
title: "My account"
save_changes_submit: "Save changes"
email_on_debate_comment_label: "Receive email when someone comments on my debates"
email_on_comment_label: "Receive email when someone comments on my debates or proposals"
email_on_comment_reply_label: "Receive email when someone replies to my comments"
change_credentials_link: "Change my credentials"
avatar: "Avatar"
@@ -187,16 +281,16 @@ en:
message:
user: "secret code did not match with the image"
debate: "secret code did not match with the image"
proposal: "secret code did not match with the image"
shared:
tags_cloud:
tags: Trend
flag: Flag as inappropriate
unflag: Undo flag
collective: Collective
search_form:
search_title: Search
search_button: Search
search_placeholder: "Search on debates..."
check: Select
check_all: All
check_none: None
mailer:
comment:
subject: Someone has commented on your debate
@@ -207,7 +301,20 @@ en:
manage:
all: "You are not authorized to %{action} %{subject}."
welcome:
last_debates: Last debates
signed_in_home_title: Recent activity
signed_in_home_title: Actividad reciente
debates:
title: "Debate"
description: "Meet, discuss and share what we think is important in our city."
proposal:
title: "You propose"
description: "Open to citizens proposals on the kind of city we want to live."
decide:
title: "You decide"
description: "Citizenship decide whether to accept or reject the most supported proposals."
do:
title: "It does"
description: "If the proposal is accepted mostly, the City Council makes his own and done."
welcome:
title: Account verification
instructions_1_html: "Welcome to the public participation website."

View File

@@ -12,23 +12,25 @@ es:
open: "abierto"
participation: Participación
open_city_title: "La ciudad que quieres será la ciudad que quieras."
open_city_slogan:
"Empecemos escuchando qué tiene que decir Madrid.
Para ello abrimos esta Puerta del Sol digital, donde toda la gente de Madrid podemos encontrarnos para debatir y compartir todo lo que queramos."
open_city_slogan_html:
"Existen ciudades gobernadas directamente por sus habitantes, que <b>debaten</b> sobre temas que les preocupan, <b>proponen</b> ideas para mejorar
sus vidas y <b>deciden</b> entre todas y todos las que se llevan a cabo. Madrid ya es una de ellas."
#"Empecemos escuchando qué tiene que decir Madrid.
#Para ello abrimos esta Puerta del Sol digital, donde toda la gente de Madrid podemos encontrarnos para debatir y compartir todo lo que queramos."
# open_city_text:
# "Aquí cualquier voz tiene su espacio y son los ciudadanos y ciudadanas, y nadie en su nombre, quienes deciden votando los debates cuáles son los temas
# más importantes de cada momento. Los responsables del gobierno tienen cuentas de usuario propias con las que podrán participar en los debates, y ser evaluados
# al mismo nivel que todos los demás."
# open_city_soon: "Muy pronto abriremos la sección de propuestas ciudadanas, donde cualquiera podrá presentar propuestas y, si reciben apoyo, serán puestas en marcha por el Ayuntamiento."
see_all_debates: Ver todos los debates
see_all: Ver debates y propuestas
my_account_link: Mi cuenta
locale: "Idioma:"
administration: Administrar
moderation: Moderar
# welcome: Portada
highlights: "Portada"
more_information: "Más información"
debates: Debates
# initiatives: Iniciativas
proposals: Propuestas ciudadanas
footer:
description: "Este portal usa la %{consul} que es %{open_source}. De Madrid, para el mundo entero."
open_source: "software libre"
@@ -55,6 +57,7 @@ es:
conditions: "Condiciones de uso"
user: la cuenta
debate: el debate
proposal: la propuesta
verification::sms: el teléfono
application:
alert:
@@ -73,6 +76,10 @@ es:
filter_topic:
one: " con el tema '%{topic}'"
other: " con el tema '%{topic}'"
search_form:
title: Buscar
button: Buscar
placeholder: "Buscar debates..."
search_results:
one: " que contiene '%{search_term}'"
other: " que contienen '%{search_term}'"
@@ -86,27 +93,13 @@ es:
zero: Sin votos
one: 1 voto
other: "%{count} votos"
comment:
author: Autor del debate
moderator: Moderador
admin: Administrador
deleted: Este comentario ha sido eliminado
user_deleted: Usuario eliminado
responses:
zero: Sin respuestas
one: 1 Respuesta
other: "%{count} Respuestas"
votes:
zero: Sin votos
one: 1 voto
other: "%{count} votos"
form:
debate_title: Título del debate
debate_text: Texto inicial del debate
tags_label: Temas
tags_instructions: >
Etiqueta este debate. Puedes elegir entre nuestras propuestas o introducir las que desees.
tags_placeholder: "Escribe las etiquetas que desees separadas por ','"
tags_placeholder: "Escribe las etiquetas que desees separadas por coma (',')"
show:
back_link: Volver
author_deleted: Usuario eliminado
@@ -143,11 +136,111 @@ es:
update:
form:
submit_button: "Guardar cambios"
proposals:
index:
start_proposal: Crea una propuesta
select_order: Ordenar por
select_order_long: Estás viendo las propuestas
orders:
confidence_score: "mejor valoradas"
hot_score: "más activas"
created_at: "más nuevas"
most_commented: "más comentadas"
random: "aleatorias"
filter_topic:
one: " con el tema '%{topic}'"
other: " con el tema '%{topic}'"
search_form:
title: Search
button: Search
placeholder: "Buscar propuestas ciudadanas..."
search_results:
one: " que contiene '%{search_term}'"
other: " que contienen '%{search_term}'"
proposal:
proposal: Propuesta
support: "Apoyar"
support_title: "Apoyar esta propuesta"
comments:
zero: Sin comentarios
one: 1 Comentario
other: "%{count} Comentarios"
supports:
zero: Sin apoyos
one: 1 apoyo
other: "%{count} apoyos"
supports_necessary: "53.726 apoyos necesarios"
census_percent: "2% del Censo"
already_supported: "¡Ya has apoyado esta propuesta!"
form:
proposal_title: Título de la propuesta
proposal_question: Pregunta de la propuesta
proposal_question_example_html: "Debe ser resumida en una pregunta cuya respuesta sea Sí o No. <em>Ej. '¿Está usted de acuerdo en peatonalizar la calle Mayor?'</em>"
proposal_summary: "Resumen de la propuesta"
proposal_summary_note: "(máximo 200 caracteres)"
proposal_text: Texto desarrollado de la propuesta
proposal_external_url: Enlace a documentación adicional
proposal_video_url: "Enlace a vídeo externo"
proposal_video_url_note: "Puedes añadir un enlace a YouTube o Vimeo"
proposal_responsible_name: "Nombre y apellidos de la persona que hace esta propuesta"
proposal_responsible_name_note: "(individualmente o como representante de un colectivo; no se mostrará públicamente)"
tags_label: Temas
tags_instructions: >
Etiqueta esta propuesta. Puedes elegir entre nuestras propuestas o introducir las que desees.
tags_placeholder: "Escribe las etiquetas que desees separadas por una coma (',')"
show:
back_link: Volver
author_deleted: Usuario eliminado
comments_title: Comentarios
comments:
zero: Sin comentarios
one: 1 Comentario
other: "%{count} Comentarios"
login_to_comment: "Necesitas %{signin} o %{signup} para comentar."
edit_proposal_link: Editar
share: Compartir
flag: "Esta propuesta ha sido marcada como inapropiada por varios usuarios."
edit:
editing: Editar propuesta
show_link: Ver propuesta
back_link: Volver
form:
submit_button: "Guardar cambios"
new:
start_new: Crear una propuesta
more_info: "¿Cómo funcionan las propuestas ciudadanas?"
back_link: Volver
recommendations_title: Recomendaciones para crear una propuesta
recommendation_one: "No escribas el título de la propuesta o frases enteras en mayúsculas. En internet eso se considera gritar. Y a nadie le gusta que le griten."
recommendation_two: "Cualquier propuesta o comentario que implique una acción ilegal será eliminada, también las que tengan la intención de sabotear los espacios de propuesta, todo lo demás está permitido."
recommendation_three: "Disfruta de este espacio, de las voces que lo llenan, también es tuyo."
form:
submit_button: "Crear propuesta"
create:
form:
submit_button: "Crear propuesta"
update:
form:
submit_button: "Guardar cambios"
comments:
form:
leave_comment: Deja tu comentario
comment_as_moderator: Comentar como moderador
comment_as_admin: Comentar como administrador
comment:
author: Autor
moderator: Moderador
admin: Administrador
deleted: Este comentario ha sido eliminado
user_deleted: Usuario eliminado
responses:
zero: Sin respuestas
one: 1 Respuesta
other: "%{count} Respuestas"
votes:
zero: Sin votos
one: 1 voto
other: "%{count} votos"
comments_helper:
comment_link: Comentar
comment_button: Publicar comentario
@@ -160,7 +253,8 @@ es:
unauthenticated: "Necesitas %{signin} o %{signup} para continuar."
signin: iniciar sesión
signup: registrarte
anonymous: "Este debate ya tiene demasiados votos anónimos, para poder votar %{verify_account}."
anonymous: "Demasiados votos anónimos, para poder votar %{verify_account}."
verified_only: "Las propuestas sólo pueden ser votadas por usuarios verificados, %{verify_account}."
verify_account: verifica tu cuenta
organizations: Las organizaciones no pueden votar
account:
@@ -168,7 +262,7 @@ es:
title: "Mi cuenta"
save_changes_submit: "Guardar cambios"
change_credentials_link: "Cambiar mis datos de acceso"
email_on_debate_comment_label: "Recibir un email cuando alguien comenta en mis debates"
email_on_comment_label: "Recibir un email cuando alguien comenta en mis propuestas o debates"
email_on_comment_reply_label: "Recibir un email cuando alguien contesta a mis comentarios"
avatar: "Avatar"
personal: "Datos personales"
@@ -187,19 +281,19 @@ es:
message:
user: "el código secreto no coincide con la imagen"
debate: "el código secreto no coincide con la imagen"
proposal: "el código secreto no coincide con la imagen"
shared:
tags_cloud:
tags: Tendencias
flag: Denunciar como inapropiado
unflag: Deshacer denuncia
collective: Colectivo
search_form:
search_title: Buscar
search_button: Buscar
search_placeholder: "Buscar en debates..."
check: Seleccionar
check_all: Todos
check_none: Ninguno
mailer:
comment:
subject: Alguien ha comentado en tu debate
subject: Alguien ha comentado en tu propuesta
reply:
subject: Alguien ha respondido a tu comentario
unauthorized:
@@ -207,7 +301,19 @@ es:
manage:
all: "No tienes permiso para realizar la acción '%{action}' sobre %{subject}."
welcome:
last_debates: Últimos debates
signed_in_home_title: Actividad reciente
debates:
title: "Debates"
description: "Encontrarnos, debatir y compartir lo que nos parece importante en nuestra ciudad."
proposal:
title: "Propones"
description: "Espacio abierto para propuestas ciudadanas sobre el tipo de ciudad en el que queremos vivir."
decide:
title: "Decides"
description: "La ciudadanía decide si acepta o rechaza las propuestas más apoyadas."
do:
title: "Se hace"
description: "Si la propuesta es aceptada mayoritariamente, el Ayuntamiento la asume como propia y se hace."
welcome:
title: Verificación de cuenta
instructions_1_html: "Bienvenido a la página de participación ciudadana"

View File

@@ -8,10 +8,14 @@ en:
truncate: "&hellip;"
helpers:
page_entries_info:
entry:
zero: "entries"
one: "entry"
other: "entries"
one_page:
display_entries:
zero: "No %{entry_name} found"
one: "Displaying <b>1</b> %{entry_name}"
other: "Displaying <b>all %{count}</b> %{entry_name}"
more_pages:
display_entries: "Displaying %{entry_name} <b>%{first}&nbsp;-&nbsp;%{last}</b> of <b>%{total}</b> in total"
display_entries: "Displaying %{entry_name} <b>%{first}&nbsp;-&nbsp;%{last}</b> of <b>%{total}</b> in total"

View File

@@ -8,10 +8,14 @@ es:
truncate: "&hellip;"
helpers:
page_entries_info:
entry:
zero: "entradas"
one: "entrada"
other: "entradas"
one_page:
display_entries:
zero: "No se han encontrado %{entry_name}"
one: "Encontrado <b>1</b> %{entry_name}"
other: "Encontrados <b> %{count}</b> %{entry_name}"
one: "Hay <b>1</b> %{entry_name}"
other: "Hay <b> %{count}</b> %{entry_name}"
more_pages:
display_entries: "Se muestran <b> del %{first}&nbsp;al&nbsp;%{last}</b> de un total de <b>%{total}</b> %{entry_name}"
display_entries: "Mostrando <b>%{first}&nbsp;-&nbsp;%{last}</b> de un total de <b>%{total}</b> %{entry_name}"

View File

@@ -1,6 +1,7 @@
en:
moderation:
menu:
proposals: Proposals
flagged_debates: Debates
flagged_comments: Comments
users: Ban users
@@ -42,6 +43,25 @@ en:
all: All
pending_flag_review: Pending
with_ignored_flag: Ignored
proposals:
index:
hide_proposals: Hide proposals
block_authors: Block authors
ignore_flags: Ignore flags
title: Proposals
headers:
proposal: Proposal
moderate: Moderate
filter: Filter
filters:
all: All
pending_flag_review: Pending
with_ignored_flag: Ignored
order: Order
orders:
created_at: Newest
flags: Most flagged
confirm: Are you sure?
bulk:
index:
title: Bulk moderation

View File

@@ -1,6 +1,7 @@
es:
moderation:
menu:
proposals: Propuestas
flagged_debates: Debates
flagged_comments: Comentarios
users: Bloquear usuarios
@@ -42,6 +43,25 @@ es:
all: Todos
pending_flag_review: Pendientes
with_ignored_flag: Ignorados
proposals:
index:
hide_proposals: Ocultar Propuestas
block_authors: Bloquear autores
ignore_flags: Marcar como revisadas
title: Propuestas
headers:
proposal: Propuesta
moderate: Moderar
filter: Filtro
filters:
all: Todas
pending_flag_review: Pendientes de revisión
with_ignored_flag: Marcadas como revisadas
order: Ordenar por
orders:
created_at: Más recientes
flags: Más denunciadas
confirm: ¿Estás seguro?
bulk:
index:
title: Moderar en bloque

View File

@@ -5,4 +5,7 @@ en:
official_level_3_name: "Level 3 official positions"
official_level_4_name: "Level 4 official positions"
official_level_5_name: "Level 5 official positions"
max_ratio_anon_votes_on_debates: "Max allowed percentage of anonymous votes per debate"
max_ratio_anon_votes_on_debates: "Max allowed percentage of anonymous votes per Debate"
max_votes_for_proposal_edit: "Number of votes where a Proposal is not editable anymore"
proposal_code_prefix: "Prefix for Proposals codes"
votes_for_proposal_success: "Number of votes needed for Proposal approval"

View File

@@ -5,4 +5,7 @@ es:
official_level_3_name: "Cargos públicos de nivel 3"
official_level_4_name: "Cargos públicos de nivel 4"
official_level_5_name: "Cargos públicos de nivel 5"
max_ratio_anon_votes_on_debates: "Porcentaje máximo de votos anónimos por debate"
max_ratio_anon_votes_on_debates: "Porcentaje máximo de votos anónimos por Debate"
max_votes_for_proposal_edit: "Número de votos en que una Propuesta 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"

Some files were not shown because too many files have changed in this diff Show More