Merge branch 'master' into api-dev-PRs

Conflicts:
	Gemfile.lock
	config/routes.rb
	db/dev_seeds.rb
	db/schema.rb
This commit is contained in:
Alberto Miedes Garcés
2017-05-11 21:59:42 +02:00
499 changed files with 13406 additions and 2327 deletions

3
.gitignore vendored
View File

@@ -30,4 +30,5 @@
.DS_Store .DS_Store
.ruby-gemset .ruby-gemset
public/sitemap.xml public/sitemap.xml
public/system/

View File

@@ -12,6 +12,10 @@ require 'capistrano/delayed_job'
require 'whenever/capistrano' require 'whenever/capistrano'
require 'rvm1/capistrano3' require 'rvm1/capistrano3'
#SCM: Git
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git
# Load custom tasks from `lib/capistrano/tasks` if you have any defined # Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r } Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }

64
Gemfile
View File

@@ -1,20 +1,20 @@
source 'https://rubygems.org' source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.2.7.1' gem 'rails', '4.2.8'
# Use PostgreSQL # Use PostgreSQL
gem 'pg', '~> 0.19.0' gem 'pg', '~> 0.20.0'
# Use SCSS for stylesheets # Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0', '>= 5.0.4' gem 'sass-rails', '~> 5.0', '>= 5.0.4'
# Use Uglifier as compressor for JavaScript assets # Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 3.0.4' gem 'uglifier', '~> 3.2.0'
# Use CoffeeScript for .coffee assets and views # Use CoffeeScript for .coffee assets and views
gem 'coffee-rails', '~> 4.2.1' gem 'coffee-rails', '~> 4.2.1'
# See https://github.com/rails/execjs#readme for more supported runtimes # See https://github.com/rails/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby # gem 'therubyracer', platforms: :ruby
# Use jquery as the JavaScript library # Use jquery as the JavaScript library
gem 'jquery-rails', '~> 4.2.2' gem 'jquery-rails', '~> 4.3.1'
gem 'jquery-ui-rails' gem 'jquery-ui-rails'
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks' gem 'turbolinks'
@@ -28,42 +28,44 @@ gem 'devise_security_extension'
# gem 'bcrypt', '~> 3.1.7' # gem 'bcrypt', '~> 3.1.7'
gem 'omniauth' gem 'omniauth'
gem 'omniauth-twitter' gem 'omniauth-twitter'
gem 'omniauth-facebook', '~> 3.0.0' gem 'omniauth-facebook', '~> 4.0.0'
gem 'omniauth-google-oauth2', '~> 0.4.0' gem 'omniauth-google-oauth2', '~> 0.4.0'
gem 'kaminari' gem 'kaminari', '~> 1.0.1'
gem 'ancestry', '~> 2.2.2' gem 'ancestry', '~> 2.2.2'
gem 'acts-as-taggable-on' gem 'acts-as-taggable-on'
gem 'responders', '~> 2.3.0' gem 'responders', '~> 2.4.0'
gem 'foundation-rails', '~> 6.2.4.0' gem 'foundation-rails', '~> 6.2.4.0'
gem 'foundation_rails_helper', '~> 2.0.0' gem 'foundation_rails_helper', '~> 2.0.0'
gem 'acts_as_votable' gem 'acts_as_votable'
gem 'ckeditor', '~> 4.2.2' gem 'ckeditor', '~> 4.2.3'
gem 'invisible_captcha', '~> 0.9.2' gem 'invisible_captcha', '~> 0.9.2'
gem 'cancancan' gem 'cancancan', '~> 1.16.0'
gem 'social-share-button' gem 'social-share-button', '~> 0.10'
gem 'initialjs-rails', '0.2.0.4' gem 'initialjs-rails', '0.2.0.4'
gem 'unicorn', '~> 5.2.0' gem 'unicorn', '~> 5.3.0'
gem 'paranoia', '~> 2.2.0' gem 'paranoia', '~> 2.3.1'
gem 'rinku', '~> 2.0.2', require: 'rails_rinku' gem 'rinku', '~> 2.0.2', require: 'rails_rinku'
gem 'savon' gem 'savon'
gem 'dalli' gem 'dalli'
gem 'rollbar', '~> 2.14.0' gem 'rollbar', '~> 2.14.1'
gem 'delayed_job_active_record', '~> 4.1.0' gem 'delayed_job_active_record', '~> 4.1.0'
gem 'daemons' gem 'daemons'
gem 'devise-async' gem 'devise-async'
gem 'newrelic_rpm', '~> 3.17.2.327' gem 'newrelic_rpm', '~> 4.1.0.333'
gem 'whenever', require: false gem 'whenever', require: false
gem 'pg_search' gem 'pg_search'
gem 'sitemap_generator' gem 'sitemap_generator', '~> 5.3.1'
gem 'ahoy_matey', '~> 1.5.3' gem 'ahoy_matey', '~> 1.6.0'
gem 'groupdate', '~> 3.1.0' # group temporary data gem 'groupdate', '~> 3.2.0' # group temporary data
gem 'tolk', '~> 2.0.0' # Web interface for translations gem 'tolk', '~> 2.0.0' # Web interface for translations
gem 'browser' gem 'browser'
gem 'turnout', '~> 2.4.0' gem 'turnout', '~> 2.4.0'
gem 'redcarpet' gem 'redcarpet', '~> 3.4.0'
gem 'paperclip'
gem 'graphql', '~> 1.3.0' gem 'graphql', '~> 1.3.0'
@@ -73,29 +75,29 @@ group :development, :test do
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring' gem 'spring'
gem 'spring-commands-rspec' gem 'spring-commands-rspec'
gem 'rspec-rails', '~> 3.5' gem 'rspec-rails', '~> 3.6'
gem 'capybara' gem 'capybara', '~> 2.14.0'
gem 'factory_girl_rails' gem 'factory_girl_rails', '~> 4.8.0'
gem 'fuubar' gem 'fuubar'
gem 'launchy' gem 'launchy'
gem 'quiet_assets' gem 'quiet_assets'
gem 'letter_opener_web', '~> 1.3.0' gem 'letter_opener_web', '~> 1.3.1'
gem 'i18n-tasks' gem 'i18n-tasks', '~> 0.9.15'
gem 'capistrano', '3.5.0', require: false gem 'capistrano', '~> 3.8.1', require: false
gem 'capistrano-bundler', '~> 1.2', require: false gem 'capistrano-bundler', '~> 1.2', require: false
gem "capistrano-rails", '1.1.8', require: false gem "capistrano-rails", '~> 1.2.3', require: false
gem 'rvm1-capistrano3', require: false gem 'rvm1-capistrano3', require: false
gem 'capistrano3-delayed-job', '~> 1.0' gem 'capistrano3-delayed-job', '~> 1.7.3'
gem "bullet" gem "bullet", '~> 5.5.1'
gem "faker" gem "faker", '~> 1.7.3'
gem 'rubocop', '~> 0.45.0', require: false gem 'rubocop', '~> 0.48.1', require: false
gem 'knapsack' gem 'knapsack'
end end
group :test do group :test do
gem 'database_cleaner' gem 'database_cleaner'
gem 'poltergeist' gem 'poltergeist', '~> 1.15.0'
gem 'coveralls', require: false gem 'coveralls', '~> 0.8.21', require: false
gem 'email_spec' gem 'email_spec'
end end

View File

@@ -1,46 +1,46 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actionmailer (4.2.7.1) actionmailer (4.2.8)
actionpack (= 4.2.7.1) actionpack (= 4.2.8)
actionview (= 4.2.7.1) actionview (= 4.2.8)
activejob (= 4.2.7.1) activejob (= 4.2.8)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.7.1) actionpack (4.2.8)
actionview (= 4.2.7.1) actionview (= 4.2.8)
activesupport (= 4.2.7.1) activesupport (= 4.2.8)
rack (~> 1.6) rack (~> 1.6)
rack-test (~> 0.6.2) rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.7.1) actionview (4.2.8)
activesupport (= 4.2.7.1) activesupport (= 4.2.8)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (4.2.7.1) activejob (4.2.8)
activesupport (= 4.2.7.1) activesupport (= 4.2.8)
globalid (>= 0.3.0) globalid (>= 0.3.0)
activemodel (4.2.7.1) activemodel (4.2.8)
activesupport (= 4.2.7.1) activesupport (= 4.2.8)
builder (~> 3.1) builder (~> 3.1)
activerecord (4.2.7.1) activerecord (4.2.8)
activemodel (= 4.2.7.1) activemodel (= 4.2.8)
activesupport (= 4.2.7.1) activesupport (= 4.2.8)
arel (~> 6.0) arel (~> 6.0)
activesupport (4.2.7.1) activesupport (4.2.8)
i18n (~> 0.7) i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1) minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4) thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1) tzinfo (~> 1.1)
acts-as-taggable-on (3.5.0) acts-as-taggable-on (4.0.0)
activerecord (>= 3.2, < 5) activerecord (>= 4.0)
acts_as_votable (0.10.0) acts_as_votable (0.10.0)
addressable (2.4.0) addressable (2.5.1)
ahoy_matey (1.5.3) public_suffix (~> 2.0, >= 2.0.2)
ahoy_matey (1.6.0)
addressable addressable
browser (~> 2.0) browser (~> 2.0)
geocoder geocoder
@@ -51,14 +51,14 @@ GEM
safely_block (>= 0.1.1) safely_block (>= 0.1.1)
user_agent_parser user_agent_parser
uuidtools uuidtools
airbrussh (1.1.1) airbrussh (1.2.0)
sshkit (>= 1.6.1, != 1.7.0) sshkit (>= 1.6.1, != 1.7.0)
akami (1.3.1) akami (1.3.1)
gyoku (>= 0.4.0) gyoku (>= 0.4.0)
nokogiri nokogiri
ancestry (2.2.2) ancestry (2.2.2)
activerecord (>= 3.0.0) activerecord (>= 3.0.0)
arel (6.0.3) arel (6.0.4)
ast (2.3.0) ast (2.3.0)
babel-source (5.8.35) babel-source (5.8.35)
babel-transpiler (0.7.0) babel-transpiler (0.7.0)
@@ -66,28 +66,27 @@ GEM
execjs (~> 2.0) execjs (~> 2.0)
bcrypt (3.1.11) bcrypt (3.1.11)
browser (2.3.0) browser (2.3.0)
builder (3.2.2) builder (3.2.3)
bullet (5.4.2) bullet (5.5.1)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
uniform_notifier (~> 1.10.0) uniform_notifier (~> 1.10.0)
byebug (9.0.6) byebug (9.0.6)
cancancan (1.15.0) cancancan (1.16.0)
capistrano (3.5.0) capistrano (3.8.1)
airbrussh (>= 1.0.0) airbrussh (>= 1.0.0)
capistrano-harrow
i18n i18n
rake (>= 10.0.0) rake (>= 10.0.0)
sshkit (>= 1.9.0) sshkit (>= 1.9.0)
capistrano-bundler (1.2.0) capistrano-bundler (1.2.0)
capistrano (~> 3.1) capistrano (~> 3.1)
sshkit (~> 1.2) sshkit (~> 1.2)
capistrano-harrow (0.5.3) capistrano-rails (1.2.3)
capistrano-rails (1.1.8)
capistrano (~> 3.1) capistrano (~> 3.1)
capistrano-bundler (~> 1.1) capistrano-bundler (~> 1.1)
capistrano3-delayed-job (1.7.2) capistrano3-delayed-job (1.7.3)
capistrano (~> 3.0, >= 3.0.0) capistrano (~> 3.0, >= 3.0.0)
capybara (2.7.1) daemons (~> 1.2.4)
capybara (2.14.0)
addressable addressable
mime-types (>= 1.16) mime-types (>= 1.16)
nokogiri (>= 1.3.3) nokogiri (>= 1.3.3)
@@ -95,11 +94,10 @@ GEM
rack-test (>= 0.5.4) rack-test (>= 0.5.4)
xpath (~> 2.0) xpath (~> 2.0)
chronic (0.10.2) chronic (0.10.2)
ckeditor (4.2.2) ckeditor (4.2.3)
cocaine cocaine
orm_adapter (~> 0.5.0) orm_adapter (~> 0.5.0)
climate_control (0.0.3) climate_control (0.1.0)
activesupport (>= 3.0)
cliver (0.3.2) cliver (0.3.2)
cocaine (0.5.8) cocaine (0.5.8)
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
@@ -109,13 +107,13 @@ GEM
coffee-script (2.4.1) coffee-script (2.4.1)
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.10.0) coffee-script-source (1.12.2)
concurrent-ruby (1.0.4) concurrent-ruby (1.0.5)
coveralls (0.8.17) coveralls (0.8.21)
json (>= 1.8, < 3) json (>= 1.8, < 3)
simplecov (~> 0.12.0) simplecov (~> 0.14.1)
term-ansicolor (~> 1.3) term-ansicolor (~> 1.3)
thor (~> 0.19.1) thor (~> 0.19.4)
tins (~> 1.6) tins (~> 1.6)
daemons (1.2.4) daemons (1.2.4)
dalli (2.7.6) dalli (2.7.6)
@@ -138,7 +136,7 @@ GEM
devise_security_extension (0.10.0) devise_security_extension (0.10.0)
devise (>= 3.0.0, < 4.0) devise (>= 3.0.0, < 4.0)
railties (>= 3.2.6, < 5.0) railties (>= 3.2.6, < 5.0)
diff-lcs (1.2.5) diff-lcs (1.3)
docile (1.1.5) docile (1.1.5)
easy_translate (0.5.0) easy_translate (0.5.0)
json json
@@ -151,14 +149,14 @@ GEM
errbase (0.0.3) errbase (0.0.3)
erubis (2.7.0) erubis (2.7.0)
execjs (2.7.0) execjs (2.7.0)
factory_girl (4.7.0) factory_girl (4.8.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
factory_girl_rails (4.7.0) factory_girl_rails (4.8.0)
factory_girl (~> 4.7.0) factory_girl (~> 4.8.0)
railties (>= 3.0.0) railties (>= 3.0.0)
faker (1.6.6) faker (1.7.3)
i18n (~> 0.5) i18n (~> 0.5)
faraday (0.9.2) faraday (0.11.0)
multipart-post (>= 1.2, < 3) multipart-post (>= 1.2, < 3)
foundation-rails (6.2.4.0) foundation-rails (6.2.4.0)
railties (>= 3.1.0) railties (>= 3.1.0)
@@ -173,23 +171,22 @@ GEM
fuubar (2.2.0) fuubar (2.2.0)
rspec-core (~> 3.0) rspec-core (~> 3.0)
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
geocoder (1.4.1) geocoder (1.4.3)
globalid (0.3.7) globalid (0.3.7)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
graphiql-rails (1.3.0) graphiql-rails (1.3.0)
rails rails
graphql (1.3.0) graphql (1.3.0)
groupdate (3.1.1) groupdate (3.2.0)
activesupport (>= 3)
gyoku (1.3.1) gyoku (1.3.1)
builder (>= 2.1.2) builder (>= 2.1.2)
hashie (3.4.6) hashie (3.5.5)
highline (1.7.8) highline (1.7.8)
htmlentities (4.3.4) htmlentities (4.3.4)
httpi (2.4.1) httpi (2.4.1)
rack rack
i18n (0.7.0) i18n (0.8.1)
i18n-tasks (0.9.6) i18n-tasks (0.9.15)
activesupport (>= 4.0.2) activesupport (>= 4.0.2)
ast (>= 2.1.0) ast (>= 2.1.0)
easy_translate (>= 0.5.0) easy_translate (>= 0.5.0)
@@ -197,32 +194,41 @@ GEM
highline (>= 1.7.3) highline (>= 1.7.3)
i18n i18n
parser (>= 2.2.3.0) parser (>= 2.2.3.0)
term-ansicolor (>= 1.3.2) rainbow (~> 2.2)
terminal-table (>= 1.5.1) terminal-table (>= 1.5.1)
initialjs-rails (0.2.0.4) initialjs-rails (0.2.0.4)
railties (>= 3.1, < 6.0) railties (>= 3.1, < 6.0)
invisible_captcha (0.9.2) invisible_captcha (0.9.2)
rails (>= 3.2.0) rails (>= 3.2.0)
jquery-rails (4.2.2) jquery-rails (4.3.1)
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0) railties (>= 4.2.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
jquery-ui-rails (5.0.5) jquery-ui-rails (6.0.1)
railties (>= 3.2.16) railties (>= 3.2.16)
json (1.8.3) json (2.1.0)
jwt (1.5.4) jwt (1.5.6)
kaminari (0.17.0) kaminari (1.0.1)
actionpack (>= 3.0.0) activesupport (>= 4.1.0)
activesupport (>= 3.0.0) kaminari-actionview (= 1.0.1)
kaminari-activerecord (= 1.0.1)
kaminari-core (= 1.0.1)
kaminari-actionview (1.0.1)
actionview
kaminari-core (= 1.0.1)
kaminari-activerecord (1.0.1)
activerecord
kaminari-core (= 1.0.1)
kaminari-core (1.0.1)
kgio (2.11.0) kgio (2.11.0)
knapsack (1.13.1) knapsack (1.13.3)
rake rake
timecop (>= 0.1.0) timecop (>= 0.1.0)
launchy (2.4.3) launchy (2.4.3)
addressable (~> 2.3) addressable (~> 2.3)
letter_opener (1.4.1) letter_opener (1.4.1)
launchy (~> 2.2) launchy (~> 2.2)
letter_opener_web (1.3.0) letter_opener_web (1.3.1)
actionmailer (>= 3.2) actionmailer (>= 3.2)
letter_opener (~> 1.0) letter_opener (~> 1.0)
railties (>= 3.2) railties (>= 3.2)
@@ -233,29 +239,30 @@ GEM
mime-types (3.1) mime-types (3.1)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521) mime-types-data (3.2016.0521)
mimemagic (0.3.2)
mini_portile2 (2.1.0) mini_portile2 (2.1.0)
minitest (5.10.1) minitest (5.10.1)
multi_json (1.12.1) multi_json (1.12.1)
multi_xml (0.5.5) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
net-scp (1.2.1) net-scp (1.2.1)
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (3.2.0) net-ssh (4.1.0)
newrelic_rpm (3.17.2.327) newrelic_rpm (4.1.0.333)
nokogiri (1.6.8.1) nokogiri (1.7.1)
mini_portile2 (~> 2.1.0) mini_portile2 (~> 2.1.0)
nori (2.6.0) nori (2.6.0)
oauth (0.5.0) oauth (0.5.1)
oauth2 (1.0.0) oauth2 (1.3.1)
faraday (>= 0.8, < 0.10) faraday (>= 0.8, < 0.12)
jwt (~> 1.0) jwt (~> 1.0)
multi_json (~> 1.3) multi_json (~> 1.3)
multi_xml (~> 0.5) multi_xml (~> 0.5)
rack (~> 1.2) rack (>= 1.2, < 3)
omniauth (1.3.1) omniauth (1.6.1)
hashie (>= 1.2, < 4) hashie (>= 3.4.6, < 3.6.0)
rack (>= 1.0, < 3) rack (>= 1.6.2, < 3)
omniauth-facebook (3.0.0) omniauth-facebook (4.0.0)
omniauth-oauth2 (~> 1.2) omniauth-oauth2 (~> 1.2)
omniauth-google-oauth2 (0.4.1) omniauth-google-oauth2 (0.4.1)
jwt (~> 1.5.2) jwt (~> 1.5.2)
@@ -268,24 +275,31 @@ GEM
omniauth-oauth2 (1.4.0) omniauth-oauth2 (1.4.0)
oauth2 (~> 1.0) oauth2 (~> 1.0)
omniauth (~> 1.2) omniauth (~> 1.2)
omniauth-twitter (1.2.1) omniauth-twitter (1.4.0)
json (~> 1.3)
omniauth-oauth (~> 1.1) omniauth-oauth (~> 1.1)
rack
orm_adapter (0.5.0) orm_adapter (0.5.0)
paranoia (2.2.0) paperclip (5.1.0)
activerecord (>= 4.0, < 5.1) activemodel (>= 4.2.0)
parser (2.3.3.1) activesupport (>= 4.2.0)
cocaine (~> 0.5.5)
mime-types
mimemagic (~> 0.3.0)
paranoia (2.3.1)
activerecord (>= 4.0, < 5.2)
parser (2.4.0.0)
ast (~> 2.2) ast (~> 2.2)
pg (0.19.0) pg (0.20.0)
pg_search (1.0.6) pg_search (2.0.1)
activerecord (>= 3.1) activerecord (>= 4.2)
activesupport (>= 3.1) activesupport (>= 4.2)
arel arel (>= 6)
poltergeist (1.10.0) poltergeist (1.15.0)
capybara (~> 2.1) capybara (~> 2.1)
cliver (~> 0.3.1) cliver (~> 0.3.1)
websocket-driver (>= 0.2.0) websocket-driver (>= 0.2.0)
powerpack (0.1.1) powerpack (0.1.1)
public_suffix (2.0.5)
quiet_assets (1.1.0) quiet_assets (1.1.0)
railties (>= 3.1, < 5.0) railties (>= 3.1, < 5.0)
rack (1.6.5) rack (1.6.5)
@@ -295,60 +309,62 @@ GEM
rack rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rails (4.2.7.1) rails (4.2.8)
actionmailer (= 4.2.7.1) actionmailer (= 4.2.8)
actionpack (= 4.2.7.1) actionpack (= 4.2.8)
actionview (= 4.2.7.1) actionview (= 4.2.8)
activejob (= 4.2.7.1) activejob (= 4.2.8)
activemodel (= 4.2.7.1) activemodel (= 4.2.8)
activerecord (= 4.2.7.1) activerecord (= 4.2.8)
activesupport (= 4.2.7.1) activesupport (= 4.2.8)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.2.7.1) railties (= 4.2.8)
sprockets-rails sprockets-rails
rails-deprecated_sanitizer (1.0.3) rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha) activesupport (>= 4.2.0.alpha)
rails-dom-testing (1.0.7) rails-dom-testing (1.0.8)
activesupport (>= 4.2.0.beta, < 5.0) activesupport (>= 4.2.0.beta, < 5.0)
nokogiri (~> 1.6.0) nokogiri (~> 1.6)
rails-deprecated_sanitizer (>= 1.0.1) rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3) rails-html-sanitizer (1.0.3)
loofah (~> 2.0) loofah (~> 2.0)
railties (4.2.7.1) railties (4.2.8)
actionpack (= 4.2.7.1) actionpack (= 4.2.8)
activesupport (= 4.2.7.1) activesupport (= 4.2.8)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.1.0) rainbow (2.2.2)
raindrops (0.17.0) rake
raindrops (0.18.0)
rake (12.0.0) rake (12.0.0)
redcarpet (3.3.4) redcarpet (3.4.0)
referer-parser (0.3.0) referer-parser (0.3.0)
request_store (1.3.1) request_store (1.3.2)
responders (2.3.0) responders (2.4.0)
railties (>= 4.2.0, < 5.1) actionpack (>= 4.2.0, < 5.3)
railties (>= 4.2.0, < 5.3)
rinku (2.0.2) rinku (2.0.2)
rollbar (2.14.0) rollbar (2.14.1)
multi_json multi_json
rspec-core (3.5.4) rspec-core (3.6.0)
rspec-support (~> 3.5.0) rspec-support (~> 3.6.0)
rspec-expectations (3.5.0) rspec-expectations (3.6.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0) rspec-support (~> 3.6.0)
rspec-mocks (3.5.0) rspec-mocks (3.6.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0) rspec-support (~> 3.6.0)
rspec-rails (3.5.2) rspec-rails (3.6.0)
actionpack (>= 3.0) actionpack (>= 3.0)
activesupport (>= 3.0) activesupport (>= 3.0)
railties (>= 3.0) railties (>= 3.0)
rspec-core (~> 3.5.0) rspec-core (~> 3.6.0)
rspec-expectations (~> 3.5.0) rspec-expectations (~> 3.6.0)
rspec-mocks (~> 3.5.0) rspec-mocks (~> 3.6.0)
rspec-support (~> 3.5.0) rspec-support (~> 3.6.0)
rspec-support (3.5.0) rspec-support (3.6.0)
rubocop (0.45.0) rubocop (0.48.1)
parser (>= 2.3.1.1, < 3.0) parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
@@ -358,9 +374,9 @@ GEM
capistrano (~> 3.0) capistrano (~> 3.0)
sshkit (>= 1.2) sshkit (>= 1.2)
safe_yaml (1.0.4) safe_yaml (1.0.4)
safely_block (0.1.1) safely_block (0.2.0)
errbase errbase
sass (3.4.22) sass (3.4.23)
sass-rails (5.0.6) sass-rails (5.0.6)
railties (>= 4.0.0, < 6) railties (>= 4.0.0, < 6)
sass (~> 3.1) sass (~> 3.1)
@@ -375,17 +391,17 @@ GEM
nokogiri (>= 1.4.0) nokogiri (>= 1.4.0)
nori (~> 2.4) nori (~> 2.4)
wasabi (~> 3.4) wasabi (~> 3.4)
simplecov (0.12.0) simplecov (0.14.1)
docile (~> 1.1.0) docile (~> 1.1.0)
json (>= 1.8, < 3) json (>= 1.8, < 3)
simplecov-html (~> 0.10.0) simplecov-html (~> 0.10.0)
simplecov-html (0.10.0) simplecov-html (0.10.0)
sitemap_generator (5.2.0) sitemap_generator (5.3.1)
builder (~> 3.0) builder (~> 3.0)
social-share-button (0.3.1) social-share-button (0.10.0)
coffee-rails coffee-rails
sass-rails spring (2.0.1)
spring (1.7.2) activesupport (>= 4.2)
spring-commands-rspec (1.0.4) spring-commands-rspec (1.0.4)
spring (>= 0.9.1) spring (>= 0.9.1)
sprockets (3.7.1) sprockets (3.7.1)
@@ -399,19 +415,19 @@ GEM
actionpack (>= 4.0) actionpack (>= 4.0)
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sshkit (1.11.4) sshkit (1.13.1)
net-scp (>= 1.1.2) net-scp (>= 1.1.2)
net-ssh (>= 2.8.0) net-ssh (>= 2.8.0)
term-ansicolor (1.4.0) term-ansicolor (1.6.0)
tins (~> 1.0) tins (~> 1.0)
terminal-table (1.7.3) terminal-table (1.7.3)
unicode-display_width (~> 1.1.1) unicode-display_width (~> 1.1.1)
thor (0.19.4) thor (0.19.4)
thread (0.2.2) thread (0.2.2)
thread_safe (0.3.5) thread_safe (0.3.6)
tilt (2.0.5) tilt (2.0.7)
timecop (0.8.1) timecop (0.8.1)
tins (1.13.0) tins (1.13.2)
tolk (2.0.0) tolk (2.0.0)
rails (>= 4.0) rails (>= 4.0)
safe_yaml (>= 0.8.6) safe_yaml (>= 0.8.6)
@@ -422,12 +438,12 @@ GEM
rack (>= 1.3, < 3) rack (>= 1.3, < 3)
rack-accept (~> 0.4) rack-accept (~> 0.4)
tilt (>= 1.4, < 3) tilt (>= 1.4, < 3)
tzinfo (1.2.2) tzinfo (1.2.3)
thread_safe (~> 0.1) thread_safe (~> 0.1)
uglifier (3.0.4) uglifier (3.2.0)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
unicode-display_width (1.1.1) unicode-display_width (1.1.3)
unicorn (5.2.0) unicorn (5.3.0)
kgio (~> 2.6) kgio (~> 2.6)
raindrops (~> 0.7) raindrops (~> 0.7)
uniform_notifier (1.10.0) uniform_notifier (1.10.0)
@@ -442,7 +458,7 @@ GEM
activemodel (>= 4.2) activemodel (>= 4.2)
debug_inspector debug_inspector
railties (>= 4.2) railties (>= 4.2)
websocket-driver (0.6.4) websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2) websocket-extensions (0.1.2)
whenever (0.9.7) whenever (0.9.7)
@@ -456,20 +472,20 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
acts-as-taggable-on acts-as-taggable-on
acts_as_votable acts_as_votable
ahoy_matey (~> 1.5.3) ahoy_matey (~> 1.6.0)
ancestry (~> 2.2.2) ancestry (~> 2.2.2)
browser browser
bullet bullet (~> 5.5.1)
byebug byebug
cancancan cancancan (~> 1.16.0)
capistrano (= 3.5.0) capistrano (~> 3.8.1)
capistrano-bundler (~> 1.2) capistrano-bundler (~> 1.2)
capistrano-rails (= 1.1.8) capistrano-rails (~> 1.2.3)
capistrano3-delayed-job (~> 1.0) capistrano3-delayed-job (~> 1.7.3)
capybara capybara (~> 2.14.0)
ckeditor (~> 4.2.2) ckeditor (~> 4.2.3)
coffee-rails (~> 4.2.1) coffee-rails (~> 4.2.1)
coveralls coveralls (~> 0.8.21)
daemons daemons
dalli dalli
database_cleaner database_cleaner
@@ -478,52 +494,56 @@ DEPENDENCIES
devise-async devise-async
devise_security_extension devise_security_extension
email_spec email_spec
factory_girl_rails factory_girl_rails (~> 4.8.0)
faker faker (~> 1.7.3)
foundation-rails (~> 6.2.4.0) foundation-rails (~> 6.2.4.0)
foundation_rails_helper (~> 2.0.0) foundation_rails_helper (~> 2.0.0)
fuubar fuubar
graphiql-rails graphiql-rails
graphql (~> 1.3.0) graphql (~> 1.3.0)
groupdate (~> 3.1.0) groupdate (~> 3.2.0)
i18n-tasks i18n-tasks (~> 0.9.15)
initialjs-rails (= 0.2.0.4) initialjs-rails (= 0.2.0.4)
invisible_captcha (~> 0.9.2) invisible_captcha (~> 0.9.2)
jquery-rails (~> 4.2.2) jquery-rails (~> 4.3.1)
jquery-ui-rails jquery-ui-rails
kaminari kaminari (~> 1.0.1)
knapsack knapsack
launchy launchy
letter_opener_web (~> 1.3.0) letter_opener_web (~> 1.3.1)
newrelic_rpm (~> 3.17.2.327) newrelic_rpm (~> 4.1.0.333)
omniauth omniauth
omniauth-facebook (~> 3.0.0) omniauth-facebook (~> 4.0.0)
omniauth-google-oauth2 (~> 0.4.0) omniauth-google-oauth2 (~> 0.4.0)
omniauth-twitter omniauth-twitter
paranoia (~> 2.2.0) paperclip
pg (~> 0.19.0) paranoia (~> 2.3.1)
pg (~> 0.20.0)
pg_search pg_search
poltergeist poltergeist (~> 1.15.0)
quiet_assets quiet_assets
rails (= 4.2.7.1) rails (= 4.2.8)
redcarpet redcarpet (~> 3.4.0)
responders (~> 2.3.0) responders (~> 2.4.0)
rinku (~> 2.0.2) rinku (~> 2.0.2)
rollbar (~> 2.14.0) rollbar (~> 2.14.1)
rspec-rails (~> 3.5) rspec-rails (~> 3.6)
rubocop (~> 0.45.0) rubocop (~> 0.48.1)
rvm1-capistrano3 rvm1-capistrano3
sass-rails (~> 5.0, >= 5.0.4) sass-rails (~> 5.0, >= 5.0.4)
savon savon
sitemap_generator sitemap_generator (~> 5.3.1)
social-share-button social-share-button (~> 0.10)
spring spring
spring-commands-rspec spring-commands-rspec
sprockets (~> 3.7.1) sprockets (~> 3.7.1)
tolk (~> 2.0.0) tolk (~> 2.0.0)
turbolinks turbolinks
turnout (~> 2.4.0) turnout (~> 2.4.0)
uglifier (>= 3.0.4) uglifier (~> 3.2.0)
unicorn (~> 5.2.0) unicorn (~> 5.3.0)
web-console (= 3.3.0) web-console (= 3.3.0)
whenever whenever
BUNDLED WITH
1.14.6

View File

@@ -1,15 +1,19 @@
![Logo of Consul] ![Logo of Consul](https://raw.githubusercontent.com/consul/consul/master/public/consul_logo.png)
(https://raw.githubusercontent.com/consul/consul/master/public/consul_logo.png)
# Consul # Consul
Citizen Participation and Open Government Application Citizen Participation and Open Government Application
[![Join the chat at https://gitter.im/consul/consul](https://badges.gitter.im/consul/consul.svg)](https://gitter.im/consul/consul?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://travis-ci.org/consul/consul.svg?branch=master)](https://travis-ci.org/consul/consul) [![Build Status](https://travis-ci.org/consul/consul.svg?branch=master)](https://travis-ci.org/consul/consul)
[![Code Climate](https://codeclimate.com/github/consul/consul/badges/gpa.svg)](https://codeclimate.com/github/consul/consul) [![Code Climate](https://codeclimate.com/github/consul/consul/badges/gpa.svg)](https://codeclimate.com/github/consul/consul)
[![Dependency Status](https://gemnasium.com/consul/consul.svg)](https://gemnasium.com/consul/consul) [![Dependency Status](https://gemnasium.com/consul/consul.svg)](https://gemnasium.com/consul/consul)
[![Coverage Status](https://coveralls.io/repos/github/consul/consul/badge.svg?branch=master)](https://coveralls.io/github/consul/consul?branch=master) [![Coverage Status](https://coveralls.io/repos/github/consul/consul/badge.svg?branch=master)](https://coveralls.io/github/consul/consul?branch=master)
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](http://www.gnu.org/licenses/agpl-3.0)
[![Accessibility conformance](https://img.shields.io/badge/accessibility-WAI:AA-green.svg)](https://www.w3.org/WAI/eval/Overview)
[![A11y issues checked with Rocket Validator](https://rocketvalidator.com/badges/checked_with_rocket_validator.svg?url=https://rocketvalidator.com)](https://rocketvalidator.com/opensource)
[![Join the chat at https://gitter.im/consul/consul](https://badges.gitter.im/consul/consul.svg)](https://gitter.im/consul/consul?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
This is the opensource code repository of the eParticipation website originally developed for the Madrid City government eParticipation website This is the opensource code repository of the eParticipation website originally developed for the Madrid City government eParticipation website

View File

@@ -1,15 +1,20 @@
![Logotipo de Consul] ![Logotipo de Consul](https://raw.githubusercontent.com/consul/consul/master/public/consul_logo.png)
(https://raw.githubusercontent.com/consul/consul/master/public/consul_logo.png)
# Consul # Consul
Aplicación de Participación Ciudadana y Gobierno Abierto Aplicación de Participación Ciudadana y Gobierno Abierto
[![Join the chat at https://gitter.im/consul/consul](https://badges.gitter.im/consul/consul.svg)](https://gitter.im/consul/consul?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://travis-ci.org/consul/consul.svg?branch=master)](https://travis-ci.org/consul/consul) [![Build Status](https://travis-ci.org/consul/consul.svg?branch=master)](https://travis-ci.org/consul/consul)
[![Code Climate](https://codeclimate.com/github/consul/consul/badges/gpa.svg)](https://codeclimate.com/github/consul/consul) [![Code Climate](https://codeclimate.com/github/consul/consul/badges/gpa.svg)](https://codeclimate.com/github/consul/consul)
[![Dependency Status](https://gemnasium.com/consul/consul.svg)](https://gemnasium.com/consul/consul) [![Dependency Status](https://gemnasium.com/consul/consul.svg)](https://gemnasium.com/consul/consul)
[![Coverage Status](https://coveralls.io/repos/github/consul/consul/badge.svg?branch=master)](https://coveralls.io/github/consul/consul?branch=master) [![Coverage Status](https://coveralls.io/repos/github/consul/consul/badge.svg?branch=master)](https://coveralls.io/github/consul/consul?branch=master)
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](http://www.gnu.org/licenses/agpl-3.0)
[![Accessibility conformance](https://img.shields.io/badge/accessibility-WAI:AA-green.svg)](https://www.w3.org/WAI/eval/Overview)
[![A11y issues checked with Rocket Validator](https://rocketvalidator.com/badges/checked_with_rocket_validator.svg?url=https://rocketvalidator.com)](https://rocketvalidator.com/opensource)
[![Join the chat at https://gitter.im/consul/consul](https://badges.gitter.im/consul/consul.svg)](https://gitter.im/consul/consul?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Este es el repositorio de código abierto de la Aplicación de Participación Ciudadana Consul, creada originariamente por el Ayuntamiento de Madrid. Este es el repositorio de código abierto de la Aplicación de Participación Ciudadana Consul, creada originariamente por el Ayuntamiento de Madrid.

Binary file not shown.

View File

@@ -56,6 +56,9 @@
<glyph glyph-name="arrow-right" unicode="&#85;" d="M329 256c0-5-2-9-5-13l-128-128c-4-3-8-5-13-5-5 0-9 2-13 5-4 4-5 8-5 13l0 256c0 5 1 9 5 13 4 3 8 5 13 5 5 0 9-2 13-5l128-128c3-4 5-8 5-13z"/> <glyph glyph-name="arrow-right" unicode="&#85;" d="M329 256c0-5-2-9-5-13l-128-128c-4-3-8-5-13-5-5 0-9 2-13 5-4 4-5 8-5 13l0 256c0 5 1 9 5 13 4 3 8 5 13 5 5 0 9-2 13-5l128-128c3-4 5-8 5-13z"/>
<glyph glyph-name="check-circle" unicode="&#86;" d="M256 480c-124 0-224-100-224-224 0-124 100-224 224-224 124 0 224 100 224 224 0 124-100 224-224 224z m115-149l-139-179c-1-1-3-3-5-3-3 0-4 1-5 3-2 1-79 76-79 76l-2 1c0 1-1 2-1 3 0 2 1 3 1 4 1 0 1 0 1 1 8 8 24 24 25 25 1 2 2 3 4 3 3 0 5-2 6-3 1-1 45-43 45-43l111 143c1 0 2 1 4 1 1 0 2 0 3-1l31-24c0-1 1-3 1-4 0-1 0-2-1-3z"/> <glyph glyph-name="check-circle" unicode="&#86;" d="M256 480c-124 0-224-100-224-224 0-124 100-224 224-224 124 0 224 100 224 224 0 124-100 224-224 224z m115-149l-139-179c-1-1-3-3-5-3-3 0-4 1-5 3-2 1-79 76-79 76l-2 1c0 1-1 2-1 3 0 2 1 3 1 4 1 0 1 0 1 1 8 8 24 24 25 25 1 2 2 3 4 3 3 0 5-2 6-3 1-1 45-43 45-43l111 143c1 0 2 1 4 1 1 0 2 0 3-1l31-24c0-1 1-3 1-4 0-1 0-2-1-3z"/>
<glyph glyph-name="arrow-top" unicode="&#87;" d="M402 165c0-5-2-10-5-13-4-4-8-6-13-6l-256 0c-5 0-9 2-13 6-3 3-5 8-5 13 0 5 2 9 5 12l128 128c4 4 8 6 13 6 5 0 9-2 13-6l128-128c3-3 5-7 5-12z"/> <glyph glyph-name="arrow-top" unicode="&#87;" d="M402 165c0-5-2-10-5-13-4-4-8-6-13-6l-256 0c-5 0-9 2-13 6-3 3-5 8-5 13 0 5 2 9 5 12l128 128c4 4 8 6 13 6 5 0 9-2 13-6l128-128c3-3 5-7 5-12z"/>
<glyph glyph-name="help-1" unicode="&#88;" d="M345 435c-27 21-58 28-98 28-29 0-55-6-75-20-30-20-44-54-44-108l77 0c0 14-2 30 7 43 8 13 20 24 40 24 20 0 31-6 41-18 8-11 11-23 11-36 0-12-5-22-12-32-4-5-9-10-15-15 0 0-42-25-56-48-11-18-15-40-16-66 0-2 0-5 7-5 7 0 56 0 62 0 6 0 7 4 7 6 0 9 2 24 3 29 4 11 10 20 20 28l21 14c18 15 33 26 40 36 11 15 19 34 19 57 0 36-13 64-39 83z m-103-293c-26 1-47-17-48-46-1-28 19-46 45-47 27-1 48 17 49 45 1 28-19 47-46 48z"/>
<glyph glyph-name="checkmark-circle" unicode="&#89;" d="M171 296l-29-30 93-93 208 208-29 29-179-178z m251-40c0-92-74-166-166-166-92 0-166 74-166 166 0 92 74 166 166 166 16 0 31-2 46-6l32 32c-24 11-50 16-78 16-114 0-208-94-208-208 0-114 94-208 208-208 114 0 208 94 208 208z"/> <glyph glyph-name="checkmark-circle" unicode="&#89;" d="M171 296l-29-30 93-93 208 208-29 29-179-178z m251-40c0-92-74-166-166-166-92 0-166 74-166 166 0 92 74 166 166 166 16 0 31-2 46-6l32 32c-24 11-50 16-78 16-114 0-208-94-208-208 0-114 94-208 208-208 114 0 208 94 208 208z"/>
<glyph glyph-name="minus-square" unicode="&#88;" d="M357 402c17 0 32-6 45-18 12-13 19-28 19-46l0-201c0-17-7-32-19-45-13-13-28-19-45-19l-202 0c-17 0-32 6-45 19-12 13-19 28-19 45l0 201c0 18 7 33 19 46 13 12 28 18 45 18z m27-265l0 201c0 8-3 14-8 20-5 5-12 8-19 8l-202 0c-7 0-14-3-19-8-5-6-8-12-8-20l0-201c0-7 3-14 8-19 5-6 12-8 19-8l202 0c7 0 14 2 19 8 5 5 8 12 8 19z m-46 119c3 0 5-1 7-3 2-1 2-3 2-6l0-18c0-3 0-5-2-7-2-2-4-3-7-3l-164 0c-3 0-5 1-7 3-2 2-2 4-2 7l0 18c0 3 0 5 2 6 2 2 4 3 7 3z"/>
<glyph glyph-name="plus-square" unicode="&#90;" d="M347 247l0-18c0-3 0-5-2-7-2-2-4-3-7-3l-64 0 0-64c0-2-1-4-2-6-2-2-4-3-7-3l-18 0c-3 0-5 1-7 3-1 2-2 4-2 6l0 64-64 0c-3 0-5 1-7 3-2 2-2 4-2 7l0 18c0 3 0 5 2 6 2 2 4 3 7 3l64 0 0 64c0 3 1 5 2 7 2 1 4 2 7 2l18 0c3 0 5-1 7-2 1-2 2-4 2-7l0-64 64 0c3 0 5-1 7-3 2-1 2-3 2-6z m37-110l0 201c0 8-3 14-8 20-5 5-12 8-19 8l-202 0c-7 0-14-3-19-8-5-6-8-12-8-20l0-201c0-7 3-14 8-19 5-6 12-8 19-8l202 0c7 0 14 2 19 8 5 5 8 12 8 19z m37 201l0-201c0-17-7-32-19-45-13-13-28-19-45-19l-202 0c-17 0-32 6-45 19-12 13-19 28-19 45l0 201c0 18 7 33 19 46 13 12 28 18 45 18l202 0c17 0 32-6 45-18 12-13 19-28 19-46z"/>
<glyph glyph-name="expand" unicode="&#48;" d="M26 168l-26-158c0-2 1-5 3-7 0 0 0 0 0 0 2-2 5-3 7-3l158 27c3 0 6 3 7 6 1 3 0 7-3 9l-30 31 82 82c4 4 4 9 0 13l-57 57c-3 3-9 3-12 0l-83-83-31 31c-2 2-5 3-9 2-3-1-5-4-6-7z m460 176l26 158c0 2-1 5-3 7 0 0 0 0 0 0-2 2-5 3-7 3l-158-27c-3 0-6-3-7-6-1-3 0-7 3-9l30-31-82-82c-4-4-4-9 0-13l57-57c3-3 9-3 12 0l83 83 31-31c2-2 5-3 9-2 3 1 5 4 6 7z"/>
<glyph glyph-name="telegram" unicode="&#49;" d="M504 509c6-5 9-11 8-18l-73-439c-1-6-4-10-10-13-2-2-5-2-8-2-3 0-5 0-7 1l-130 53-69-84c-3-5-8-7-14-7-2 0-4 0-6 1-4 1-7 4-9 7-2 3-3 6-3 10l0 100 247 303-306-265-113 47c-7 2-10 7-11 15 0 8 3 14 9 17l476 274c2 2 5 3 9 3 4 0 7-1 10-3z"/>
</font></defs></svg> </font></defs></svg>

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -12,8 +12,8 @@
// //
//= require jquery //= require jquery
//= require jquery_ujs //= require jquery_ujs
//= require jquery-ui/datepicker //= require jquery-ui/widgets/datepicker
//= require jquery-ui/datepicker-es //= require jquery-ui/i18n/datepicker-es
//= require foundation //= require foundation
//= require turbolinks //= require turbolinks
//= require ckeditor/loader //= require ckeditor/loader
@@ -49,6 +49,7 @@
//= require fixed_bar //= require fixed_bar
//= require banners //= require banners
//= require social_share //= require social_share
//= require checkbox_toggle
//= require custom //= require custom
var initialize_modules = function() { var initialize_modules = function() {
@@ -74,6 +75,7 @@ var initialize_modules = function() {
App.FixedBar.initialize(); App.FixedBar.initialize();
App.Banners.initialize(); App.Banners.initialize();
App.SocialShare.initialize(); App.SocialShare.initialize();
App.CheckboxToggle.initialize();
}; };
$(function(){ $(function(){

View File

@@ -0,0 +1,12 @@
App.CheckboxToggle =
initialize: ->
$('[data-checkbox-toggle]').on 'change', ->
$this = $(this)
$target = $($this.data('checkbox-toggle'))
if $this.is(':checked')
$target.show()
else
$target.hide()

View File

@@ -80,6 +80,7 @@ $budget: #7E328A;
$budget-hover: #7571BF; $budget-hover: #7571BF;
$highlight: #E7F2FC; $highlight: #E7F2FC;
$light: #F5F7FA;
$featured: #FFDC5C; $featured: #FFDC5C;
$footer-border: #BFC1C3; $footer-border: #BFC1C3;

View File

@@ -5,6 +5,7 @@
// 03. List elements // 03. List elements
// 04. Stats // 04. Stats
// 05. Management // 05. Management
// 06. Polls
// //
// 01. Global styles // 01. Global styles
@@ -20,6 +21,11 @@ body.admin {
.top-links { .top-links {
background: darken($admin-color, 15%); background: darken($admin-color, 15%);
} }
.back-web {
padding-top: $line-height/4;
text-decoration: underline;
}
} }
.top-bar { .top-bar {
@@ -27,19 +33,37 @@ body.admin {
height: auto; height: auto;
} }
.top-bar-title {
h1 {
margin-bottom: 0;
}
}
form { form {
.button { .button {
margin-top: 0; margin-top: 0;
&.margin-top {
margin-top: $line-height;
}
} }
input[type="text"], textarea { input[type="text"], textarea {
width: 100%; width: 100%;
} }
.input-group input[type="text"] { .fieldset {
border-radius: 0;
margin-bottom: 0 !important; select {
height: $line-height*2;
}
.input-group input[type="text"] {
border-radius: 0;
margin-bottom: 0 !important;
}
} }
} }
@@ -48,6 +72,15 @@ body.admin {
th { th {
text-align: left; text-align: left;
&.text-center {
text-align: center;
}
&.text-right {
padding-right: $line-height;
text-align: right;
}
&.with-button { &.with-button {
line-height: $line-height*2; line-height: $line-height*2;
} }
@@ -62,9 +95,20 @@ body.admin {
} }
} }
&.fixed {
table-layout: fixed;
}
input[type="submit"] ~ a, a ~ a { input[type="submit"] ~ a, a ~ a {
margin-left: $line-height/2; margin-left: 0;
margin-right: $line-height/2; margin-right: 0;
margin-top: $line-height/2;
@include breakpoint(medium) {
margin-left: $line-height/2;
margin-right: $line-height/2;
margin-top: 0;
}
} }
} }
@@ -77,6 +121,11 @@ body.admin {
color: $admin-color; color: $admin-color;
} }
.tabs-panel {
padding-left: 0;
padding-right: 0;
}
#proposals { #proposals {
width: 100% !important; width: 100% !important;
} }
@@ -148,6 +197,13 @@ body.admin {
} }
} }
.input-group {
.input-group-button {
padding-bottom: rem-calc(16);
}
}
// 02. Sidebar // 02. Sidebar
// ----------- // -----------
@@ -155,7 +211,7 @@ body.admin {
border-right: 1px solid $border; border-right: 1px solid $border;
@include breakpoint(medium) { @include breakpoint(medium) {
padding-bottom: $line-height*3; min-height: rem-calc(1100);
} }
ul { ul {
@@ -165,10 +221,12 @@ body.admin {
padding: 0; padding: 0;
[class^="icon-"] { [class^="icon-"] {
color: $admin-color;
display: inline-block; display: inline-block;
font-size: rem-calc(24); font-size: rem-calc(24);
padding-right: rem-calc(12); line-height: $line-height;
padding-top: rem-calc(4); padding: $line-height/2 $line-height/4;
padding-left: 0;
vertical-align: middle; vertical-align: middle;
} }
@@ -177,19 +235,26 @@ body.admin {
margin: 0; margin: 0;
outline: 0; outline: 0;
ul {
margin-left: $line-height/1.5;
border-left: 1px solid $border;
padding-left: $line-height/2;
}
&.section-title {
border-bottom: 1px solid $border;
}
&.active a { &.active a {
background: #f3f6f7; background: #f3f6f7;
border-radius: rem-calc(6);
-moz-border-radius: rem-calc(6);
-webkit-border-radius: rem-calc(6);
color: $admin-color; color: $admin-color;
font-weight: bold; font-weight: bold;
} }
} }
li.section {
border-bottom: 1px dotted #d5d5d5;
border-top: 1px dotted #d5d5d5;
height: $line-height/2;
}
li a { li a {
color: $text; color: $text;
display: block; display: block;
@@ -199,10 +264,38 @@ body.admin {
&:hover { &:hover {
background: #f3f6f7; background: #f3f6f7;
border-radius: rem-calc(6);
-moz-border-radius: rem-calc(6);
-webkit-border-radius: rem-calc(6);
color: $admin-color;
text-decoration: none; text-decoration: none;
} }
} }
} }
.is-accordion-submenu-parent {
& > a::after {
border-color: $admin-color transparent transparent;
}
}
.submenu {
border-bottom: 0;
margin-left: $line-height;
li:first-child {
padding-top: $line-height/2;
}
li:last-child {
padding-bottom: $line-height/2;
}
a {
font-weight: normal;
}
}
} }
// 03. List elements // 03. List elements
@@ -403,3 +496,49 @@ table.investment-projects-summary {
white-space: nowrap; white-space: nowrap;
} }
} }
body.admin {
.geozone {
background: #ececec;
border-radius: rem-calc(6);
color: $text;
display: inline-block;
font-size: $small-font-size;
margin-bottom: $line-height/3;
padding: $line-height/4 $line-height/3;
text-decoration: none;
&:hover {
background: #e0e0e0;
}
}
}
// 06. Polls
// -----------------
.count-error {
background: $alert-bg !important;
color: $color-alert;
font-weight: bold;
}
table {
.callout {
height: $line-height*2;
line-height: $line-height*2;
padding: 0 $line-height/2;
}
}
// 07. CMS
// --------------
.cms_page_list {
[class^="icon-"] {
padding-right: $menu-icon-spacing;
vertical-align: middle;
}
}

View File

@@ -6,6 +6,7 @@
@import "admin"; @import "admin";
@import "layout"; @import "layout";
@import "participation"; @import "participation";
@import "pages";
@import "custom"; @import "custom";
@import "c3"; @import "c3";
@import "annotator.min"; @import "annotator.min";

View File

@@ -163,6 +163,12 @@
.icon-whatsapp:before { .icon-whatsapp:before {
content: "\50"; content: "\50";
} }
.icon-zip:before {
content: "\4f";
}
.icon-banner:before {
content: "\51";
}
.icon-arrow-down:before { .icon-arrow-down:before {
content: "\52"; content: "\52";
} }
@@ -184,3 +190,6 @@
.icon-checkmark-circle:before { .icon-checkmark-circle:before {
content: "\59"; content: "\59";
} }
.icon-telegram:before {
content: "\31";
}

View File

@@ -70,6 +70,11 @@ a {
} }
} }
.button.hollow {
border: 1px solid $link;
color: $link;
}
.postfix.button { .postfix.button {
padding: 0; padding: 0;
} }
@@ -139,6 +144,10 @@ a {
padding-top: $line-height; padding-top: $line-height;
} }
.light {
background: $light;
}
.highlight { .highlight {
background: $highlight; background: $highlight;
} }
@@ -199,6 +208,30 @@ a {
} }
} }
.menu.vertical {
background: white;
margin: $line-height 0;
padding: $line-height;
li {
margin-bottom: $line-height;
a {
color: $text-medium;
padding: 0;
}
h2 {
font-size: $base-font-size;
}
&.active {
border-bottom: 2px solid $brand;
color: $brand;
}
}
}
.small { .small {
font-size: $small-font-size; font-size: $small-font-size;
} }
@@ -374,6 +407,24 @@ header {
ul { ul {
margin-bottom: 0; margin-bottom: 0;
li {
display: block;
}
@include breakpoint(medium) {
li {
display: inline-block;
&:after {
content: "|";
}
&:last-child:after {
content: none;
}
}
}
} }
} }
@@ -439,6 +490,7 @@ header {
.input-group-button { .input-group-button {
line-height: $line-height*1.5; line-height: $line-height*1.5;
padding-bottom: 0;
button { button {
background: $border; background: $border;
@@ -460,7 +512,6 @@ header {
} }
.submenu { .submenu {
background: white;
border-bottom: 1px solid $border; border-bottom: 1px solid $border;
clear: both; clear: both;
margin-bottom: $line-height/2; margin-bottom: $line-height/2;
@@ -536,12 +587,8 @@ footer {
padding-left: 0; padding-left: 0;
} }
h2 a { a.title {
border-bottom: 1px solid $text-light; font-weight: bold;
display: block;
font-size: rem-calc(24);
line-height: rem-calc(31);
padding-bottom: $line-height/4;
text-decoration: none; text-decoration: none;
} }
} }
@@ -562,7 +609,8 @@ footer {
// 04. Tags // 04. Tags
// -------- // --------
.tags a , .tag-cloud a, .categories a, .geozone a, .sidebar-links a { .tags a , .tag-cloud a, .categories a, .geozone a, .sidebar-links a,
.tags span {
background: #ececec; background: #ececec;
border-radius: rem-calc(6); border-radius: rem-calc(6);
color: $text; color: $text;
@@ -1081,11 +1129,12 @@ img.avatar, img.admin-avatar, img.moderator-avatar, img.initialjs-avatar {
// -------------------- // --------------------
[class^="level-"] { [class^="level-"] {
color: white; color: black;
} }
.is-author { .is-author {
background: #008CCF; background: #00A5F1;
color: black;
} }
.is-association { .is-association {
@@ -1143,7 +1192,11 @@ table {
.button.button-twitter, .button.button-twitter,
.button.button-facebook, .button.button-facebook,
.button.button-google { .button.button-google,
.button.button-telegram {
background: white;
color: $text;
font-weight: bold;
height: $line-height*2; height: $line-height*2;
line-height: $line-height*2; line-height: $line-height*2;
padding: 0; padding: 0;
@@ -1151,9 +1204,11 @@ table {
} }
.button.button-twitter { .button.button-twitter {
background: #45B0E3; background: #ECF7FC;
border-left: 3px solid #45B0E3;
&:before { &:before {
color: #45B0E3;
content: "f"; content: "f";
font-family: "icons" !important; font-family: "icons" !important;
font-size: rem-calc(24); font-size: rem-calc(24);
@@ -1191,9 +1246,11 @@ table {
} }
.button.button-facebook { .button.button-facebook {
background: #3B5998; background: #EBEEF4;
border-left: 3px solid #3B5998;
&:before { &:before {
color: #3B5998;
content: "A"; content: "A";
font-family: "icons" !important; font-family: "icons" !important;
font-size: rem-calc(24); font-size: rem-calc(24);
@@ -1231,9 +1288,11 @@ table {
} }
.button.button-google { .button.button-google {
background: #DE4C34; background: #FCEDEA;
border-left: 3px solid #DE4C34;
&:before { &:before {
color: #DE4C34;
content: "B"; content: "B";
font-family: "icons" !important; font-family: "icons" !important;
font-size: rem-calc(24); font-size: rem-calc(24);
@@ -1270,6 +1329,55 @@ table {
} }
} }
.button.button-telegram {
background: #ECF7FC;
border-left: 3px solid #0088cc;
&:before {
color: #0088cc;
content: "1";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 0;
line-height: $line-height*2;
padding: 0 rem-calc(20);
position: absolute;
top: 0;
}
}
.ssb-telegram {
background: #0088cc;
background-image: none !important;
color: white;
height: $line-height*2 !important;
position: relative;
width: $line-height*2 !important;
&:before {
content: "1";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 50%;
line-height: $line-height*2;
margin-left: rem-calc(-11);
position: absolute;
top: 0;
}
&:hover, &:focus {
background: white;
color: #40A2D1;
}
}
@include breakpoint(medium) {
.button.button-telegram, .ssb-telegram {
display: none !important;
}
}
.social { .social {
a { a {
@@ -1361,6 +1469,30 @@ table {
color: #CE3E26; color: #CE3E26;
} }
} }
.ssb-telegram {
background: #0088cc;
color: white;
height: $line-height;
position: relative;
width: $line-height*2;
&:before {
content: "1";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 50%;
line-height: $line-height*2;
margin-left: rem-calc(-11);
position: absolute;
top: 0;
}
&:hover, &:focus {
background: white;
color: #40A2D1;
}
}
} }
// 13. Pages // 13. Pages

View File

@@ -0,0 +1,111 @@
// Table of Contents
//
// 01. Header
// 02. Navigation
// 03. Content
// 04. Sidebar
//
// 01. Header
// ----------------------
.jumbo {
margin-bottom: $line-height;
margin-top: rem-calc(-24);
padding-bottom: $line-height;
padding-top: $line-height;
&.light {
background: #ECF0F1;
}
}
.lead {
font-size: rem-calc(24);
}
// 03. Navigation
// ----------------------
.menu-pages {
list-style-type: none;
margin: 0;
li {
display: block;
@include breakpoint(medium) {
display: inline-block;
margin-right: $line-height/2;
}
}
}
// 03. Content
// ----------------------
.more-info-content {
h3 {
color: $brand;
}
.additional-info {
margin-bottom: $line-height;
}
a:not(.button) {
text-decoration: underline;
}
figure {
margin: 0;
text-align: center;
figcaption {
font-size: $small-font-size;
font-style: italic;
}
}
ul.features {
list-style-type: circle;
margin-left: $line-height;
@include breakpoint(medium) {
margin: $line-height 0 $line-height $line-height*2.5;
}
li {
margin-bottom: $line-height
}
}
.section-content {
border-top: 1px solid $medium-gray;
padding-bottom: $line-height*2;
padding-top: $line-height*2;
&:first-child {
border-top: 0;
padding-top: 0;
}
}
}
// 04. Sidebar
// ----------------------
.more-info-sidebar {
.sidebar-card {
border: 1px solid $border;
margin-bottom: $line-height;
padding: $line-height/2;
&.light {
background: #ECF0F1;
border: 0;
}
}
}

View File

@@ -6,7 +6,8 @@
// 04. List participation // 04. List participation
// 05. Featured // 05. Featured
// 06. Budget // 06. Budget
// 07. Proposals successfull // 07. Proposals successful
// 08. Polls
// //
// 01. Votes and supports // 01. Votes and supports
@@ -297,7 +298,8 @@
.debate-show, .debate-show,
.proposal-show, .proposal-show,
.investment-project-show, .investment-project-show,
.budget-investment-show { .budget-investment-show,
.polls-show {
p { p {
word-wrap: break-word; word-wrap: break-word;
@@ -455,7 +457,7 @@
} }
.bullet { .bullet {
color: $border; color: $text;
} }
.investment-project-show p, .budget-investment-show p { .investment-project-show p, .budget-investment-show p {
@@ -769,7 +771,7 @@
// ------------ // ------------
.featured-debates, .featured-proposals, .featured-debates, .featured-proposals,
.proposals-ballot, .proposals-ballot-list { .enquiries-list {
padding: $line-height/2 0; padding: $line-height/2 0;
@include breakpoint(medium) { @include breakpoint(medium) {
@@ -974,6 +976,10 @@
&.social-share-button-google_plus:hover { &.social-share-button-google_plus:hover {
color: #CE3E26; color: #CE3E26;
} }
&.social-share-button-telegram:hover {
color: #CE3E26;
}
} }
} }
@@ -1058,13 +1064,16 @@
} }
} }
h3.subtitle { .ballot-content {
border-bottom: 3px solid $budget; border: 2px solid #F9F9F9;
border-radius: rem-calc(6);
padding: $line-height/2;
}
span { .subtitle {
font-size: $base-font-size; border-left: 2px solid $budget;
font-weight: normal; margin: $line-height/2 0;
} padding-left: $line-height/2;
} }
.amount-spent { .amount-spent {
@@ -1087,7 +1096,7 @@ ul.ballot-list {
background: #f9f9f9; background: #f9f9f9;
line-height: $line-height; line-height: $line-height;
margin-bottom: $line-height/4; margin-bottom: $line-height/4;
padding: $line-height/2; padding: $line-height $line-height/2;
position: relative; position: relative;
a { a {
@@ -1095,7 +1104,6 @@ ul.ballot-list {
} }
span { span {
color: #9f9f9f;
display: block; display: block;
font-style: italic; font-style: italic;
} }
@@ -1202,7 +1210,7 @@ ul.ballot-list {
} }
} }
// 07. Proposals successfull // 07. Proposals successful
// ------------------------- // -------------------------
.dark-heading { .dark-heading {
@@ -1235,7 +1243,7 @@ ul.ballot-list {
} }
} }
.featured-proposals-ballot-banner { .featured-proposals-ballot-banner, .sucessfull-proposals-banner {
background: #2D3E50 image-url("ballot_tiny.gif") no-repeat; background: #2D3E50 image-url("ballot_tiny.gif") no-repeat;
background-position: 75% 0; background-position: 75% 0;
position: relative; position: relative;
@@ -1259,10 +1267,10 @@ ul.ballot-list {
} }
} }
.featured-proposals-ballot-banner, .sucessfull-proposals-banner,
.successfull .panel { .successful .panel {
.icon-successfull { .icon-successful {
border-right: 60px solid #FFD200; border-right: 60px solid #FFD200;
border-top: 0; border-top: 0;
border-bottom: 60px solid transparent; border-bottom: 60px solid transparent;
@@ -1283,17 +1291,7 @@ ul.ballot-list {
} }
} }
.proposals-ballot-list { .successful {
.proposal-sucessfull {
background: white;
border-top: 1px solid $border;
padding: $line-height 0;
position: relative;
}
}
.successfull {
.panel { .panel {
position: relative; position: relative;
@@ -1314,3 +1312,199 @@ ul.ballot-list {
} }
} }
} }
// 08. Polls
// ----------------------
.dark-heading {
background: #2D3E50;
color: white;
.title {
color: #92BA48;
}
.button {
background: white;
color: $brand;
}
.callout {
&.warning a {
color: $color-warning;
}
&.primary a {
color: $color-info;
}
&.alert a {
color: $color-alert;
}
}
.info {
background: #314253;
padding: $line-height;
@include breakpoint(medium) {
border-top: rem-calc(6) solid #92BA48;
}
}
a:not(.button) {
color: white;
text-decoration: underline;
}
.back, .icon-angle-left {
color: white;
}
&.polls-show-header {
min-height: $line-height*8;
}
}
.poll, .poll-question {
background: white;
border-radius: rem-calc(6);
margin-bottom: $line-height/2;
}
.poll {
padding: $line-height;
position: relative;
.icon-poll-answer {
border-top: 0;
border-bottom: 60px solid transparent;
height: 0;
position: absolute;
right: 0;
top: 0;
width: 0;
&.can-answer:after,
&.cant-answer:after,
&.not-logged-in:after,
&.already-answer:after,
&.unverified:after {
font-family: "icons" !important;
left: 34px;
position: absolute;
top: 5px;
}
&.can-answer {
border-right: 60px solid $info-bg;
&:after {
color: $color-info;
content: "\6c";
}
}
&.cant-answer {
border-right: 60px solid $alert-bg;
&:after {
color: $color-alert;
content: "\74";
}
}
&.not-logged-in {
border-right: 60px solid $info-bg;
&:after {
color: $color-info;
content: "\6f";
}
}
&.unverified {
border-right: 60px solid $warning-bg;
&:after {
color: $color-warning;
content: "\6f";
}
}
&.already-answer {
border-right: 60px solid $success-bg;
&:after {
color: $color-success;
content: "\59";
}
}
}
.dates {
color: $text-medium;
font-size: $small-font-size;
margin-bottom: $line-height/2;
}
h4 {
font-size: rem-calc(30);
line-height: $line-height*1.5;
a {
color: $text;
}
}
}
h2.questions-callout {
font-size: $base-font-size;
}
h3.section-title-divider {
border-bottom: 2px solid $brand;
color: $brand;
margin-bottom: $line-height;
}
.poll-question {
padding: 0 $line-height;
h3 {
padding-top: $line-height;
a {
color: $text;
}
}
}
.poll-question-answers {
.button {
margin-right: $line-height/4;
min-width: rem-calc(168);
&.answered {
background: #F4F8EC;
border: 2px solid #92BA48;
color: $text;
position: relative;
&:after {
background: #92BA48;
border-radius: rem-calc(20);
content: "\6c";
color: white;
font-family: "icons" !important;
font-size: rem-calc(12);
padding: $line-height/4;
position: absolute;
right: -6px;
top: -6px;
}
}
}
}

View File

@@ -1,9 +1,10 @@
class Admin::Api::StatsController < Admin::Api::BaseController class Admin::Api::StatsController < Admin::Api::BaseController
def show def show
unless params[:events].present? || unless params[:events].present? ||
params[:visits].present? || params[:visits].present? ||
params[:spending_proposals].present? params[:spending_proposals].present? ||
params[:budget_investments].present?
return render json: {}, status: :bad_request return render json: {}, status: :bad_request
end end
@@ -24,6 +25,10 @@ class Admin::Api::StatsController < Admin::Api::BaseController
ds.add "Spending proposals", SpendingProposal.group_by_day(:created_at).count ds.add "Spending proposals", SpendingProposal.group_by_day(:created_at).count
end end
if params[:budget_investments].present?
ds.add "Budget Investments", Budget::Investment.group_by_day(:created_at).count
end
render json: ds.build render json: ds.build
end end
end end

View File

@@ -0,0 +1,66 @@
class Admin::Poll::BoothAssignmentsController < Admin::BaseController
before_action :load_poll, except: [:create, :destroy]
def index
@booth_assignments = @poll.booth_assignments.includes(:booth).order('poll_booths.name').page(params[:page]).per(50)
end
def search_booths
load_search
@booths = ::Poll::Booth.search(@search)
respond_to do |format|
format.js
end
end
def show
@booth_assignment = @poll.booth_assignments.includes(:recounts, :final_recounts, :voters, officer_assignments: [officer: [:user]]).find(params[:id])
@voters_by_date = @booth_assignment.voters.group_by {|v| v.created_at.to_date}
end
def create
@booth_assignment = ::Poll::BoothAssignment.new(poll_id: booth_assignment_params[:poll_id], booth_id: booth_assignment_params[:booth_id])
if @booth_assignment.save
notice = t("admin.poll_booth_assignments.flash.create")
else
notice = t("admin.poll_booth_assignments.flash.error_create")
end
redirect_to admin_poll_booth_assignments_path(@booth_assignment.poll_id), notice: notice
end
def destroy
@booth_assignment = ::Poll::BoothAssignment.find(params[:id])
if @booth_assignment.destroy
notice = t("admin.poll_booth_assignments.flash.destroy")
else
notice = t("admin.poll_booth_assignments.flash.error_destroy")
end
redirect_to admin_poll_booth_assignments_path(@booth_assignment.poll_id), notice: notice
end
private
def load_booth_assignment
@booth_assignment = ::Poll::BoothAssignment.find(params[:id])
end
def booth_assignment_params
params.permit(:booth_id, :poll_id)
end
def load_poll
@poll = ::Poll.find(params[:poll_id])
end
def search_params
params.permit(:poll_id, :search)
end
def load_search
@search = search_params[:search]
end
end

View File

@@ -0,0 +1,39 @@
class Admin::Poll::BoothsController < Admin::BaseController
load_and_authorize_resource class: 'Poll::Booth'
def index
@booths = @booths.order(name: :asc).page(params[:page])
end
def show
end
def new
end
def create
if @booth.save
redirect_to admin_booths_path, notice: t("flash.actions.create.poll_booth")
else
render :new
end
end
def edit
end
def update
if @booth.update(booth_params)
redirect_to admin_booth_path(@booth), notice: t("flash.actions.update.poll_booth")
else
render :edit
end
end
private
def booth_params
params.require(:poll_booth).permit(:name, :location)
end
end

View File

@@ -0,0 +1,92 @@
class Admin::Poll::OfficerAssignmentsController < Admin::BaseController
before_action :load_poll
before_action :redirect_if_blank_required_params, only: [:by_officer]
before_action :load_booth_assignment, only: [:create]
def index
@officers = ::Poll::Officer.
includes(:user).
order('users.username').
where(
id: @poll.officer_assignments.select(:officer_id).distinct.map(&:officer_id)
).page(params[:page]).per(50)
end
def by_officer
@poll = ::Poll.includes(:booths).find(params[:poll_id])
@officer = ::Poll::Officer.includes(:user).find(officer_assignment_params[:officer_id])
@officer_assignments = ::Poll::OfficerAssignment.
joins(:booth_assignment).
includes(:recount, :final_recounts, booth_assignment: :booth).
where("officer_id = ? AND poll_booth_assignments.poll_id = ?", @officer.id, @poll.id).
order(:date)
end
def search_officers
load_search
@officers = User.joins(:poll_officer).search(@search).order(username: :asc)
respond_to do |format|
format.js
end
end
def create
@officer_assignment = ::Poll::OfficerAssignment.new(booth_assignment: @booth_assignment,
officer_id: create_params[:officer_id],
date: create_params[:date])
@officer_assignment.final = true if @officer_assignment.date > @booth_assignment.poll.ends_at.to_date
if @officer_assignment.save
notice = t("admin.poll_officer_assignments.flash.create")
else
notice = t("admin.poll_officer_assignments.flash.error_create")
end
redirect_to by_officer_admin_poll_officer_assignments_path(poll_id: create_params[:poll_id], officer_id: create_params[:officer_id]), notice: notice
end
def destroy
@officer_assignment = ::Poll::OfficerAssignment.includes(:booth_assignment).find(params[:id])
if @officer_assignment.destroy
notice = t("admin.poll_officer_assignments.flash.destroy")
else
notice = t("admin.poll_officer_assignments.flash.error_destroy")
end
redirect_to by_officer_admin_poll_officer_assignments_path(poll_id: @officer_assignment.poll_id, officer_id: @officer_assignment.officer_id), notice: notice
end
private
def officer_assignment_params
params.permit(:officer_id)
end
def create_params
params.permit(:poll_id, :booth_id, :date, :officer_id)
end
def load_booth_assignment
@booth_assignment = ::Poll::BoothAssignment.includes(:poll).find_by(poll_id: create_params[:poll_id], booth_id: create_params[:booth_id])
end
def load_poll
@poll = ::Poll.find(params[:poll_id])
end
def redirect_if_blank_required_params
if officer_assignment_params[:officer_id].blank?
redirect_to admin_poll_path(@poll)
end
end
def search_params
params.permit(:poll_id, :search)
end
def load_search
@search = search_params[:search]
end
end

View File

@@ -0,0 +1,39 @@
class Admin::Poll::OfficersController < Admin::BaseController
load_and_authorize_resource :officer, class: "Poll::Officer", except: [:edit, :show]
def index
@officers = @officers.page(params[:page])
end
def search
@user = User.find_by(email: params[:email])
respond_to do |format|
if @user
@officer = Poll::Officer.find_or_initialize_by(user: @user)
format.js
else
format.js { render "user_not_found" }
end
end
end
def create
@officer.user_id = params[:user_id]
@officer.save
redirect_to admin_officers_path
end
def destroy
@officer.destroy
redirect_to admin_officers_path
end
def show
end
def edit
end
end

View File

@@ -0,0 +1,86 @@
class Admin::Poll::PollsController < Admin::BaseController
load_and_authorize_resource
before_action :load_search, only: [:search_booths, :search_questions, :search_officers]
before_action :load_geozones, only: [:new, :create, :edit, :update]
def index
end
def show
@poll = Poll.includes(:questions).
order('poll_questions.title').
find(params[:id])
end
def new
end
def create
if @poll.save
redirect_to [:admin, @poll], notice: t("flash.actions.create.poll")
else
render :new
end
end
def edit
end
def update
if @poll.update(poll_params)
redirect_to [:admin, @poll], notice: t("flash.actions.update.poll")
else
render :edit
end
end
def add_question
question = ::Poll::Question.find(params[:question_id])
if question.present?
@poll.questions << question
notice = t("admin.polls.flash.question_added")
else
notice = t("admin.polls.flash.error_on_question_added")
end
redirect_to admin_poll_path(@poll), notice: notice
end
def remove_question
question = ::Poll::Question.find(params[:question_id])
if @poll.questions.include? question
@poll.questions.delete(question)
notice = t("admin.polls.flash.question_removed")
else
notice = t("admin.polls.flash.error_on_question_removed")
end
redirect_to admin_poll_path(@poll), notice: notice
end
def search_questions
@questions = ::Poll::Question.where("poll_id IS ? OR poll_id != ?", nil, @poll.id).search({search: @search}).order(title: :asc)
respond_to do |format|
format.js
end
end
private
def load_geozones
@geozones = Geozone.all.order(:name)
end
def poll_params
params.require(:poll).permit(:name, :starts_at, :ends_at, :geozone_restricted, geozone_ids: [])
end
def search_params
params.permit(:poll_id, :search)
end
def load_search
@search = search_params[:search]
end
end

View File

@@ -0,0 +1,64 @@
class Admin::Poll::QuestionsController < Admin::BaseController
load_and_authorize_resource :poll
load_and_authorize_resource :question, class: 'Poll::Question'
def index
@polls = Poll.all
@search = search_params[:search]
@questions = @questions.search(search_params).page(params[:page]).order("created_at DESC")
@proposals = Proposal.successful.sort_by_confidence_score
end
def new
@polls = Poll.all
@question.valid_answers = I18n.t('poll_questions.default_valid_answers')
proposal = Proposal.find(params[:proposal_id]) if params[:proposal_id].present?
@question.copy_attributes_from_proposal(proposal)
end
def create
@question.author = @question.proposal.try(:author) || current_user
if @question.save
redirect_to admin_question_path(@question)
else
render :new
end
end
def show
end
def edit
end
def update
if @question.update(question_params)
redirect_to admin_question_path(@question), notice: t("flash.actions.save_changes.notice")
else
render :edit
end
end
def destroy
if @question.destroy
notice = "Question destroyed succesfully"
else
notice = t("flash.actions.destroy.error")
end
redirect_to admin_questions_path, notice: notice
end
private
def question_params
params.require(:poll_question).permit(:poll_id, :title, :question, :description, :proposal_id, :valid_answers)
end
def search_params
params.permit(:poll_id, :search)
end
end

View File

@@ -0,0 +1,16 @@
class Admin::Poll::RecountsController < Admin::BaseController
before_action :load_poll
def index
@booth_assignments = @poll.booth_assignments.
includes(:booth, :recounts, :final_recounts, :voters).
order("poll_booths.name").
page(params[:page]).per(50)
end
private
def load_poll
@poll = ::Poll.find(params[:poll_id])
end
end

View File

@@ -0,0 +1,13 @@
class Admin::Poll::ResultsController < Admin::BaseController
before_action :load_poll
def index
@partial_results = @poll.partial_results
end
private
def load_poll
@poll = ::Poll.includes(:questions).find(params[:poll_id])
end
end

View File

@@ -0,0 +1,10 @@
class Admin::SiteCustomization::BaseController < Admin::BaseController
helper_method :namespace
private
def namespace
"admin"
end
end

View File

@@ -0,0 +1,40 @@
class Admin::SiteCustomization::ContentBlocksController < Admin::SiteCustomization::BaseController
load_and_authorize_resource :content_block, class: "SiteCustomization::ContentBlock"
def index
@content_blocks = SiteCustomization::ContentBlock.order(:name, :locale)
end
def create
if @content_block.save
redirect_to admin_site_customization_content_blocks_path, notice: t('admin.site_customization.content_blocks.create.notice')
else
flash.now[:error] = t('admin.site_customization.content_blocks.create.error')
render :new
end
end
def update
if @content_block.update(content_block_params)
redirect_to admin_site_customization_content_blocks_path, notice: t('admin.site_customization.content_blocks.update.notice')
else
flash.now[:error] = t('admin.site_customization.content_blocks.update.error')
render :edit
end
end
def destroy
@content_block.destroy
redirect_to admin_site_customization_content_blocks_path, notice: t('admin.site_customization.content_blocks.destroy.notice')
end
private
def content_block_params
params.require(:site_customization_content_block).permit(
:name,
:locale,
:body
)
end
end

View File

@@ -0,0 +1,43 @@
class Admin::SiteCustomization::ImagesController < Admin::SiteCustomization::BaseController
load_and_authorize_resource :image, class: "SiteCustomization::Image"
def index
@images = SiteCustomization::Image.all_images
end
def update
if params[:site_customization_image].nil?
redirect_to admin_site_customization_images_path
return
end
if @image.update(image_params)
redirect_to admin_site_customization_images_path, notice: t('admin.site_customization.images.update.notice')
else
flash.now[:error] = t('admin.site_customization.images.update.error')
@images = SiteCustomization::Image.all_images
idx = @images.index {|e| e.name == @image.name }
@images[idx] = @image
render :index
end
end
def destroy
@image.image = nil
if @image.save
redirect_to admin_site_customization_images_path, notice: t('admin.site_customization.images.destroy.notice')
else
redirect_to admin_site_customization_images_path, notice: t('admin.site_customization.images.destroy.error')
end
end
private
def image_params
params.require(:site_customization_image).permit(
:image
)
end
end

View File

@@ -0,0 +1,44 @@
class Admin::SiteCustomization::PagesController < Admin::SiteCustomization::BaseController
load_and_authorize_resource :page, class: "SiteCustomization::Page"
def index
@pages = SiteCustomization::Page.order('slug').page(params[:page])
end
def create
if @page.save
redirect_to admin_site_customization_pages_path, notice: t('admin.site_customization.pages.create.notice')
else
flash.now[:error] = t('admin.site_customization.pages.create.error')
render :new
end
end
def update
if @page.update(page_params)
redirect_to admin_site_customization_pages_path, notice: t('admin.site_customization.pages.update.notice')
else
flash.now[:error] = t('admin.site_customization.pages.update.error')
render :edit
end
end
def destroy
@page.destroy
redirect_to admin_site_customization_pages_path, notice: t('admin.site_customization.pages.destroy.notice')
end
private
def page_params
params.require(:site_customization_page).permit(
:slug,
:title,
:subtitle,
:content,
:more_info_flag,
:print_content_flag,
:status
)
end
end

View File

@@ -21,6 +21,9 @@ class Admin::StatsController < Admin::BaseController
@user_ids_who_voted_proposals = ActsAsVotable::Vote.where(votable_type: 'Proposal').distinct.count(:voter_id) @user_ids_who_voted_proposals = ActsAsVotable::Vote.where(votable_type: 'Proposal').distinct.count(:voter_id)
@user_ids_who_didnt_vote_proposals = @verified_users - @user_ids_who_voted_proposals @user_ids_who_didnt_vote_proposals = @verified_users - @user_ids_who_voted_proposals
@spending_proposals = SpendingProposal.count @spending_proposals = SpendingProposal.count
budgets_ids = Budget.where.not(phase: 'finished').pluck(:id)
@budgets = budgets_ids.size
@investments = Budget::Investment.where(budget_id: budgets_ids).count
end end
def proposal_notifications def proposal_notifications

View File

@@ -114,4 +114,10 @@ class ApplicationController < ActionController::Base
store_location_for(:user, request.path) store_location_for(:user, request.path)
end end
end end
def set_default_budget_filter
if @budget.try(:balloting?)
params[:filter] ||= "selected"
end
end
end end

View File

@@ -7,8 +7,8 @@ module Budgets
before_action :load_ballot before_action :load_ballot
before_action :load_tag_cloud before_action :load_tag_cloud
before_action :load_categories before_action :load_categories
before_action :load_investments before_action :load_investments
before_action :load_ballot_referer
load_and_authorize_resource :budget load_and_authorize_resource :budget
load_and_authorize_resource :ballot, class: "Budget::Ballot", through: :budget load_and_authorize_resource :ballot, class: "Budget::Ballot", through: :budget
@@ -73,6 +73,10 @@ module Budgets
@categories = ActsAsTaggableOn::Tag.where("kind = 'category'").order(:name) @categories = ActsAsTaggableOn::Tag.where("kind = 'category'").order(:name)
end end
def load_ballot_referer
@ballot_referer = session[:ballot_referer]
end
end end
end end
end end

View File

@@ -3,9 +3,11 @@ module Budgets
before_action :authenticate_user! before_action :authenticate_user!
load_and_authorize_resource :budget load_and_authorize_resource :budget
before_action :load_ballot before_action :load_ballot
after_action :store_referer, only: [:show]
def show def show
authorize! :show, @ballot authorize! :show, @ballot
session[:ballot_referer] = request.referer
render template: "budgets/ballot/show" render template: "budgets/ballot/show"
end end
@@ -16,5 +18,9 @@ module Budgets
@ballot = @budget.balloting? ? query.first_or_create : query.first_or_initialize @ballot = @budget.balloting? ? query.first_or_create : query.first_or_initialize
end end
def store_referer
session[:ballot_referer] = request.referer
end
end end
end end

View File

@@ -3,6 +3,9 @@ module Budgets
load_and_authorize_resource :budget load_and_authorize_resource :budget
load_and_authorize_resource :group, class: "Budget::Group" load_and_authorize_resource :group, class: "Budget::Group"
before_action :set_default_budget_filter, only: :show
has_filters %w{not_unfeasible feasible unfeasible unselected selected}, only: [:show]
def show def show
end end

View File

@@ -14,18 +14,20 @@ module Budgets
before_action :load_heading, only: [:index, :show] before_action :load_heading, only: [:index, :show]
before_action :set_random_seed, only: :index before_action :set_random_seed, only: :index
before_action :load_categories, only: [:index, :new, :create] before_action :load_categories, only: [:index, :new, :create]
before_action :set_default_budget_filter, only: :index
feature_flag :budgets feature_flag :budgets
has_orders %w{most_voted newest oldest}, only: :show has_orders %w{most_voted newest oldest}, only: :show
has_orders ->(c) { c.instance_variable_get(:@budget).investments_orders }, only: :index has_orders ->(c) { c.instance_variable_get(:@budget).investments_orders }, only: :index
has_filters %w{not_unfeasible feasible unfeasible unselected selected}, only: [:index, :show]
invisible_captcha only: [:create, :update], honeypot: :subtitle, scope: :budget_investment invisible_captcha only: [:create, :update], honeypot: :subtitle, scope: :budget_investment
respond_to :html, :js respond_to :html, :js
def index def index
@investments = @investments.apply_filters_and_search(@budget, params).send("sort_by_#{@current_order}").page(params[:page]).per(10).for_render @investments = @investments.apply_filters_and_search(@budget, params, @current_filter).send("sort_by_#{@current_order}").page(params[:page]).per(10).for_render
@investment_ids = @investments.pluck(:id) @investment_ids = @investments.pluck(:id)
load_investment_votes(@investments) load_investment_votes(@investments)
@tag_cloud = tag_cloud @tag_cloud = tag_cloud
@@ -55,7 +57,7 @@ module Budgets
end end
def destroy def destroy
investment.destroy @investment.destroy
redirect_to user_path(current_user, filter: 'budget_investments'), notice: t('flash.actions.destroy.budget_investment') redirect_to user_path(current_user, filter: 'budget_investments'), notice: t('flash.actions.destroy.budget_investment')
end end
@@ -77,7 +79,8 @@ module Budgets
def set_random_seed def set_random_seed
if params[:order] == 'random' || params[:order].blank? if params[:order] == 'random' || params[:order].blank?
params[:random_seed] ||= rand(99)/100.0 params[:random_seed] ||= rand(99)/100.0
Budget::Investment.connection.execute "select setseed(#{params[:random_seed]})" seed = Float(params[:random_seed]) rescue 0
Budget::Investment.connection.execute("select setseed(#{seed})")
else else
params[:random_seed] = nil params[:random_seed] = nil
end end
@@ -106,6 +109,7 @@ module Budgets
def tag_cloud def tag_cloud
TagCloud.new(Budget::Investment, params[:search]) TagCloud.new(Budget::Investment, params[:search])
end end
end end
end end

View File

@@ -3,6 +3,9 @@ class BudgetsController < ApplicationController
feature_flag :budgets feature_flag :budgets
load_and_authorize_resource load_and_authorize_resource
before_action :set_default_budget_filter, only: :show
has_filters %w{not_unfeasible feasible unfeasible unselected selected}, only: :show
respond_to :html, :js respond_to :html, :js
def show def show

View File

@@ -1,6 +1,7 @@
module CommentableActions module CommentableActions
extend ActiveSupport::Concern extend ActiveSupport::Concern
include Polymorphic include Polymorphic
include Search
def index def index
@resources = @search_terms.present? ? resource_model.search(@search_terms) : resource_model.all @resources = @search_terms.present? ? resource_model.search(@search_terms) : resource_model.all
@@ -100,53 +101,6 @@ module CommentableActions
end end
end end
def parse_search_terms
@search_terms = params[:search] if params[:search].present?
end
def parse_advanced_search_terms
@advanced_search_terms = params[:advanced_search] if params[:advanced_search].present?
parse_search_date
end
def parse_search_date
return unless search_by_date?
params[:advanced_search][:date_range] = search_date_range
end
def search_by_date?
params[:advanced_search] && params[:advanced_search][:date_min].present?
end
def search_start_date
case params[:advanced_search][:date_min]
when '1'
24.hours.ago
when '2'
1.week.ago
when '3'
1.month.ago
when '4'
1.year.ago
else
Date.parse(params[:advanced_search][:date_min]) rescue nil
end
end
def search_finish_date
params[:advanced_search][:date_max].try(:to_date) || Date.today
end
def search_date_range
search_start_date.beginning_of_day..search_finish_date.end_of_day
end
def set_search_order
if params[:search].present? && params[:order].blank?
params[:order] = 'relevance'
end
end
def set_resource_votes(instance) def set_resource_votes(instance)
send("set_#{resource_name}_votes", instance) send("set_#{resource_name}_votes", instance)
end end

View File

@@ -0,0 +1,57 @@
module Search
extend ActiveSupport::Concern
included do
before_action :parse_search_terms, only: [:index, :suggest]
before_action :parse_advanced_search_terms, only: :index
before_action :set_search_order, only: :index
end
def parse_search_terms
@search_terms = params[:search] if params[:search].present?
end
def parse_advanced_search_terms
@advanced_search_terms = params[:advanced_search] if params[:advanced_search].present?
parse_search_date
end
def parse_search_date
return unless search_by_date?
params[:advanced_search][:date_range] = search_date_range
end
def search_by_date?
params[:advanced_search] && params[:advanced_search][:date_min].present?
end
def search_start_date
case params[:advanced_search][:date_min]
when '1'
24.hours.ago
when '2'
1.week.ago
when '3'
1.month.ago
when '4'
1.year.ago
else
Date.parse(params[:advanced_search][:date_min]) rescue 100.years.ago
end
end
def search_finish_date
(params[:advanced_search][:date_max].to_date rescue Date.today) || Date.today
end
def search_date_range
[100.years.ago, search_start_date].max.beginning_of_day..[search_finish_date, Date.today].min.end_of_day
end
def set_search_order
if params[:search].present? && params[:order].blank?
params[:order] = 'relevance'
end
end
end

View File

@@ -3,10 +3,7 @@ class DebatesController < ApplicationController
include CommentableActions include CommentableActions
include FlagActions include FlagActions
before_action :parse_search_terms, only: [:index, :suggest]
before_action :parse_advanced_search_terms, only: :index
before_action :parse_tag_filter, only: :index before_action :parse_tag_filter, only: :index
before_action :set_search_order, only: :index
before_action :authenticate_user!, except: [:index, :show, :map] before_action :authenticate_user!, except: [:index, :show, :map]
feature_flag :debates feature_flag :debates
@@ -22,7 +19,7 @@ class DebatesController < ApplicationController
def index_customization def index_customization
@featured_debates = @debates.featured @featured_debates = @debates.featured
@proposal_successfull_exists = Proposal.successfull.exists? @proposal_successfull_exists = Proposal.successful.exists?
end end
def show def show

View File

@@ -7,6 +7,11 @@ class Management::BudgetsController < Management::BaseController
def create_investments def create_investments
@budgets = Budget.accepting.order(created_at: :desc).page(params[:page]) @budgets = Budget.accepting.order(created_at: :desc).page(params[:page])
if current_manager_administrator?
@budgets += Budget.reviewing.order(created_at: :desc) +
Budget.selecting.order(created_at: :desc)
end
end end
def support_investments def support_investments
@@ -23,4 +28,8 @@ class Management::BudgetsController < Management::BaseController
check_verified_user t("management.budget_investments.alert.unverified_user") check_verified_user t("management.budget_investments.alert.unverified_user")
end end
def current_manager_administrator?
session[:manager]["login"].match("admin")
end
end end

View File

@@ -32,7 +32,7 @@ class Management::UsersController < Management::BaseController
private private
def user_params def user_params
params.require(:user).permit(:document_type, :document_number, :username, :email) params.require(:user).permit(:document_type, :document_number, :username, :email, :date_of_birth)
end end
def destroy_session def destroy_session

View File

@@ -0,0 +1,12 @@
class Officing::BaseController < ApplicationController
layout 'admin'
before_action :authenticate_user!
before_action :verify_officer
skip_authorization_check
def verify_officer
raise CanCan::AccessDenied unless current_user.try(:poll_officer?) || current_user.try(:administrator?)
end
end

View File

@@ -0,0 +1,6 @@
class Officing::DashboardController < Officing::BaseController
def index
end
end

View File

@@ -0,0 +1,47 @@
class Officing::FinalRecountsController < Officing::BaseController
before_action :load_poll
before_action :load_officer_assignment, only: :create
def new
@officer_assignments = ::Poll::OfficerAssignment.
includes(:final_recounts, booth_assignment: [:booth]).
joins(:booth_assignment).
final.
where(id: current_user.poll_officer.officer_assignment_ids).
where("poll_booth_assignments.poll_id = ?", @poll.id).
order(date: :asc)
@final_recounts = @officer_assignments.select {|oa| oa.final_recounts.any?}.map(&:final_recounts).flatten
end
def create
@final_recount = ::Poll::FinalRecount.find_or_initialize_by(booth_assignment_id: @officer_assignment.booth_assignment_id, date: final_recount_params[:date])
@final_recount.officer_assignment_id = @officer_assignment.id
@final_recount.count = final_recount_params[:count]
if @final_recount.save
msg = { notice: t("officing.final_recounts.flash.create") }
else
msg = { alert: t("officing.final_recounts.flash.error_create") }
end
redirect_to new_officing_poll_final_recount_path(@poll), msg
end
private
def load_poll
@poll = Poll.expired.find(params[:poll_id])
end
def load_officer_assignment
@officer_assignment = current_user.poll_officer.
officer_assignments.final.find_by(id: final_recount_params[:officer_assignment_id])
if @officer_assignment.blank?
redirect_to new_officing_poll_final_recount_path(@poll), alert: t("officing.final_recounts.flash.error_create")
end
end
def final_recount_params
params.permit(:officer_assignment_id, :count, :date)
end
end

View File

@@ -0,0 +1,15 @@
class Officing::PollsController < Officing::BaseController
def index
@polls = current_user.poll_officer? ? current_user.poll_officer.voting_days_assigned_polls : []
@polls = @polls.select {|poll| poll.current?(Time.current) || poll.current?(1.day.ago)}
end
def final
@polls = current_user.poll_officer? ? current_user.poll_officer.final_days_assigned_polls : []
return unless current_user.poll_officer?
@polls = @polls.select {|poll| poll.ends_at > 1.week.ago && poll.expired?}
end
end

View File

@@ -0,0 +1,46 @@
class Officing::RecountsController < Officing::BaseController
before_action :load_poll
before_action :load_officer_assignment, only: :create
def new
@officer_assignments = ::Poll::OfficerAssignment.
includes(:recount, booth_assignment: :booth).
joins(:booth_assignment).
voting_days.
where(id: current_user.poll_officer.officer_assignment_ids).
where("poll_booth_assignments.poll_id = ?", @poll.id).
order(date: :asc)
@recounted = @officer_assignments.select {|oa| oa.recount.present?}.reverse
end
def create
@recount = ::Poll::Recount.find_or_initialize_by(booth_assignment_id: @officer_assignment.booth_assignment_id, date: @officer_assignment.date)
@recount.officer_assignment_id = @officer_assignment.id
@recount.count = recount_params[:count]
if @recount.save
msg = { notice: t("officing.recounts.flash.create") }
else
msg = { alert: t("officing.recounts.flash.error_create") }
end
redirect_to new_officing_poll_recount_path(@poll), msg
end
private
def load_poll
@poll = Poll.find(params[:poll_id])
end
def load_officer_assignment
@officer_assignment = current_user.poll_officer.
officer_assignments.find_by(id: recount_params[:officer_assignment_id])
if @officer_assignment.blank?
redirect_to new_officing_poll_recount_path(@poll), alert: t("officing.recounts.flash.error_create")
end
end
def recount_params
params.permit(:officer_assignment_id, :count)
end
end

View File

@@ -0,0 +1,37 @@
class Officing::ResidenceController < Officing::BaseController
before_action :load_officer_assignment
before_action :validate_officer_assignment, only: :create
def new
@residence = Officing::Residence.new
end
def create
@residence = Officing::Residence.new(residence_params.merge(officer: current_user.poll_officer))
if @residence.save
redirect_to new_officing_voter_path(id: @residence.user.id), notice: t("officing.residence.flash.create")
else
render :new
end
end
private
def residence_params
params.require(:residence).permit(:document_number, :document_type, :year_of_birth)
end
def load_officer_assignment
@officer_assignments = current_user.poll_officer.
officer_assignments.
voting_days.
where(date: Time.current.to_date)
end
def validate_officer_assignment
if @officer_assignments.blank?
redirect_to officing_root_path, notice: t("officing.residence.flash.not_allowed")
end
end
end

View File

@@ -0,0 +1,141 @@
class Officing::ResultsController < Officing::BaseController
before_action :load_poll
before_action :load_officer_assignments, only: :new
before_action :load_partial_results, only: :new
before_action :load_officer_assignment, only: :create
before_action :check_booth_and_date, only: :create
before_action :build_results, only: :create
def new
end
def create
@results.each { |result| result.save! }
notice = t("officing.results.flash.create")
redirect_to new_officing_poll_result_path(@poll), notice: notice
end
def index
@booth_assignment = ::Poll::BoothAssignment.includes(:booth).find(index_params[:booth_assignment_id])
if current_user.poll_officer.officer_assignments.final.
where(booth_assignment_id: @booth_assignment.id).exists?
@partial_results = ::Poll::PartialResult.includes(:question).
where(booth_assignment_id: index_params[:booth_assignment_id]).
where(date: index_params[:date])
@whites = ::Poll::WhiteResult.where(booth_assignment_id: @booth_assignment.id, date: index_params[:date]).sum(:amount)
@nulls = ::Poll::NullResult.where(booth_assignment_id: @booth_assignment.id, date: index_params[:date]).sum(:amount)
end
end
private
def check_booth_and_date
if @officer_assignment.blank?
go_back_to_new(t("officing.results.flash.error_wrong_booth"))
elsif results_params[:date].blank? ||
Date.parse(results_params[:date]) < @poll.starts_at.to_date ||
Date.parse(results_params[:date]) > @poll.ends_at.to_date
go_back_to_new(t("officing.results.flash.error_wrong_date"))
end
end
def build_results
@results = []
params[:questions].each_pair do |question_id, results|
question = @poll.questions.find(question_id)
go_back_to_new if question.blank?
results.each_pair do |answer_index, count|
if count.present?
answer = question.valid_answers[answer_index.to_i]
go_back_to_new if question.blank?
partial_result = ::Poll::PartialResult.find_or_initialize_by(booth_assignment_id: @officer_assignment.booth_assignment_id,
date: results_params[:date],
question_id: question_id,
answer: answer)
partial_result.officer_assignment_id = @officer_assignment.id
partial_result.amount = count.to_i
partial_result.author = current_user
partial_result.origin = 'booth'
@results << partial_result
end
end
end
build_white_results
build_null_results
end
def build_white_results
if results_params[:whites].present?
white_result = ::Poll::WhiteResult.find_or_initialize_by(booth_assignment_id: @officer_assignment.booth_assignment_id,
date: results_params[:date])
white_result.officer_assignment_id = @officer_assignment.id
white_result.amount = results_params[:whites].to_i
white_result.author = current_user
white_result.origin = 'booth'
@results << white_result
end
end
def build_null_results
if results_params[:nulls].present?
null_result = ::Poll::NullResult.find_or_initialize_by(booth_assignment_id: @officer_assignment.booth_assignment_id,
date: results_params[:date])
null_result.officer_assignment_id = @officer_assignment.id
null_result.amount = results_params[:nulls].to_i
null_result.author = current_user
null_result.origin = 'booth'
@results << null_result
end
end
def go_back_to_new(alert = nil)
params[:d] = results_params[:date]
params[:oa] = results_params[:officer_assignment_id]
flash.now[:alert] = (alert || t("officing.results.flash.error_create"))
load_officer_assignments
load_partial_results
render :new
end
def load_poll
@poll = ::Poll.expired.includes(:questions).find(params[:poll_id])
end
def load_officer_assignment
@officer_assignment = current_user.poll_officer.
officer_assignments.final.find_by(id: results_params[:officer_assignment_id])
end
def load_officer_assignments
@officer_assignments = ::Poll::OfficerAssignment.
includes(booth_assignment: [:booth]).
joins(:booth_assignment).
final.
where(id: current_user.poll_officer.officer_assignment_ids).
where("poll_booth_assignments.poll_id = ?", @poll.id).
order(date: :asc)
end
def load_partial_results
if @officer_assignments.present?
@partial_results = ::Poll::PartialResult.where(officer_assignment_id: @officer_assignments.map(&:id)).order(:booth_assignment_id, :date)
end
end
def results_params
params.permit(:officer_assignment_id, :date, :questions, :whites, :nulls)
end
def index_params
params.permit(:booth_assignment_id, :date)
end
end

View File

@@ -0,0 +1,25 @@
class Officing::VotersController < Officing::BaseController
respond_to :html, :js
def new
@user = User.find(params[:id])
@polls = Poll.answerable_by(@user)
end
def create
@poll = Poll.find(voter_params[:poll_id])
@user = User.find(voter_params[:user_id])
@voter = Poll::Voter.new(document_type: @user.document_type,
document_number: @user.document_number,
user: @user,
poll: @poll)
@voter.save!
end
private
def voter_params
params.require(:voter).permit(:poll_id, :user_id)
end
end

View File

@@ -2,7 +2,11 @@ class PagesController < ApplicationController
skip_authorization_check skip_authorization_check
def show def show
render action: params[:id] if @custom_page = SiteCustomization::Page.published.find_by(slug: params[:id])
render action: :custom_page
else
render action: params[:id]
end
rescue ActionView::MissingTemplate rescue ActionView::MissingTemplate
head 404 head 404
end end

View File

@@ -0,0 +1,27 @@
class Polls::QuestionsController < ApplicationController
load_and_authorize_resource :poll
load_and_authorize_resource :question, class: 'Poll::Question'
has_orders %w{most_voted newest oldest}, only: :show
def show
@commentable = @question.proposal.present? ? @question.proposal : @question
@comment_tree = CommentTree.new(@commentable, params[:page], @current_order)
set_comment_flags(@comment_tree.comments)
question_answer = @question.answers.where(author_id: current_user.try(:id)).first
@answers_by_question_id = {@question.id => question_answer.try(:answer)}
end
def answer
answer = @question.answers.find_or_initialize_by(author: current_user)
answer.answer = params[:answer]
answer.save!
answer.record_voter_participation
@answers_by_question_id = {@question.id => params[:answer]}
end
end

View File

@@ -0,0 +1,23 @@
class PollsController < ApplicationController
load_and_authorize_resource
has_filters %w{current expired incoming}
::Poll::Answer # trigger autoload
def index
@polls = @polls.send(@current_filter).includes(:geozones).sort_for_list.page(params[:page])
end
def show
@questions = @poll.questions.for_render.sort_for_list
@answers_by_question_id = {}
poll_answers = ::Poll::Answer.by_question(@poll.question_ids).by_author(current_user.try(:id))
poll_answers.each do |answer|
@answers_by_question_id[answer.question_id] = answer.answer
end
end
end

View File

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

View File

@@ -2,10 +2,7 @@ class ProposalsController < ApplicationController
include CommentableActions include CommentableActions
include FlagActions include FlagActions
before_action :parse_search_terms, only: [:index, :suggest]
before_action :parse_advanced_search_terms, only: :index
before_action :parse_tag_filter, only: :index before_action :parse_tag_filter, only: :index
before_action :set_search_order, only: :index
before_action :load_categories, only: [:index, :new, :edit, :map, :summary] before_action :load_categories, only: [:index, :new, :edit, :map, :summary]
before_action :load_geozones, only: [:edit, :map, :summary] before_action :load_geozones, only: [:edit, :map, :summary]
before_action :authenticate_user!, except: [:index, :show, :map, :summary] before_action :authenticate_user!, except: [:index, :show, :map, :summary]
@@ -28,8 +25,8 @@ class ProposalsController < ApplicationController
def index_customization def index_customization
discard_archived discard_archived
load_retired load_retired
load_proposal_ballots load_successful_proposals
load_featured unless @proposal_successfull_exists load_featured unless @proposal_successful_exists
end end
def vote def vote
@@ -103,8 +100,8 @@ class ProposalsController < ApplicationController
end end
end end
def load_proposal_ballots def load_successful_proposals
@proposal_successfull_exists = Proposal.successfull.exists? @proposal_successful_exists = Proposal.successful.exists?
end end
end end

View File

@@ -26,6 +26,7 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController
@investment.send_unfeasible_email @investment.send_unfeasible_email
end end
Activity.log(current_user, :valuate, @investment)
redirect_to valuation_budget_budget_investment_path(@budget, @investment), notice: t('valuation.budget_investments.notice.valuate') redirect_to valuation_budget_budget_investment_path(@budget, @investment), notice: t('valuation.budget_investments.notice.valuate')
else else
render action: :edit render action: :edit

View File

@@ -4,6 +4,38 @@ module AdminHelper
render "/#{namespace}/menu" render "/#{namespace}/menu"
end end
def namespaced_root_path
"/#{namespace}"
end
def namespaced_header_title
t("#{namespace}.header.title")
end
def menu_tags?
["tags"].include? controller_name
end
def menu_moderated_content?
["proposals", "debates", "comments", "users"].include? controller_name
end
def menu_budget?
["spending_proposals"].include? controller_name
end
def menu_polls?
["polls", "questions", "officers", "booths", "officer_assignments", "booth_assignments", "recounts", "results"].include? controller_name
end
def menu_profiles?
["organizations", "officials", "moderators", "valuators", "managers"].include? controller_name
end
def menu_banners?
["banners"].include? controller_name
end
def official_level_options def official_level_options
options = [["", 0]] options = [["", 0]]
(1..5).each do |i| (1..5).each do |i|
@@ -16,10 +48,14 @@ module AdminHelper
Administrator.all.order('users.username asc').includes(:user).collect { |v| [ v.name, v.id ] } Administrator.all.order('users.username asc').includes(:user).collect { |v| [ v.name, v.id ] }
end end
def admin_submit_action(resource)
resource.persisted? ? "edit" : "new"
end
private private
def namespace def namespace
controller.class.parent.name.downcase controller.class.parent.name.downcase.gsub("::", "/")
end end
end end

View File

@@ -47,4 +47,12 @@ module ApplicationHelper
"<span class='icon-angle-left'></span>".html_safe + t("shared.back") "<span class='icon-angle-left'></span>".html_safe + t("shared.back")
end end
end end
def image_path_for(filename)
SiteCustomization::Image.image_path_for(filename) || filename
end
def content_block(name, locale)
SiteCustomization::ContentBlock.block_for(name, locale)
end
end end

View File

@@ -1,7 +1,7 @@
module BannersHelper module BannersHelper
def has_banners def has_banners?
@banners.count > 0 @banners.present? && @banners.count > 0
end end
end end

View File

@@ -10,7 +10,7 @@ module BudgetsHelper
def namespaced_budget_investment_path(investment, options={}) def namespaced_budget_investment_path(investment, options={})
case namespace case namespace
when "management::budgets" when "management/budgets"
management_budget_investment_path(investment.budget, investment, options) management_budget_investment_path(investment.budget, investment, options)
else else
budget_investment_path(investment.budget, investment, options) budget_investment_path(investment.budget, investment, options)
@@ -19,7 +19,7 @@ module BudgetsHelper
def namespaced_budget_investment_vote_path(investment, options={}) def namespaced_budget_investment_vote_path(investment, options={})
case namespace case namespace
when "management::budgets" when "management/budgets"
vote_management_budget_investment_path(investment.budget, investment, options) vote_management_budget_investment_path(investment.budget, investment, options)
else else
vote_budget_investment_path(investment.budget, investment, options) vote_budget_investment_path(investment.budget, investment, options)
@@ -38,4 +38,8 @@ module BudgetsHelper
def current_ballot def current_ballot
Budget::Ballot.where(user: current_user, budget: @budget).first Budget::Ballot.where(user: current_user, budget: @budget).first
end end
def investment_tags_select_options
Budget::Investment.tags_on(:valuation).order(:name).select(:name).distinct
end
end end

View File

@@ -23,6 +23,8 @@ module CommentsHelper
def commentable_path(comment) def commentable_path(comment)
if comment.commentable_type == "Budget::Investment" if comment.commentable_type == "Budget::Investment"
budget_investment_path(comment.commentable.budget_id, comment.commentable) budget_investment_path(comment.commentable.budget_id, comment.commentable)
elsif comment.commentable_type == "Poll::Question"
question_path(comment.commentable)
else else
comment.commentable comment.commentable
end end

View File

@@ -2,6 +2,7 @@ module EmbedVideosHelper
def embedded_video_code def embedded_video_code
link = @proposal.video_url link = @proposal.video_url
title = t('proposals.show.embed_video_title', proposal: @proposal.title)
if link.match(/vimeo.*/) if link.match(/vimeo.*/)
server = "Vimeo" server = "Vimeo"
elsif link.match(/youtu*.*/) elsif link.match(/youtu*.*/)
@@ -21,7 +22,7 @@ module EmbedVideosHelper
end end
if match and match[2] if match and match[2]
'<iframe src="' + src + match[2] + '" frameborder="0" allowfullscreen></iframe>' '<iframe src="' + src + match[2] + '" style="border:0;" allowfullscreen title="' + title + '"></iframe>'
else else
'' ''
end end

View File

@@ -0,0 +1,7 @@
module OfficersHelper
def officer_label(officer)
truncate([officer.name, officer.email].compact.join(' - '), length: 100)
end
end

View File

@@ -0,0 +1,36 @@
module OfficingHelper
def officer_assignments_select_options(officer_assignments)
options = []
officer_assignments.each do |oa|
options << ["#{oa.booth_assignment.booth.name}: #{l(oa.date.to_date, format: :long)}", oa.id]
end
options_for_select(options)
end
def booths_for_officer_select_options(officer_assignments)
options = []
officer_assignments.each do |oa|
options << ["#{oa.booth_assignment.booth.name}", oa.id]
end
options.sort! {|x,y| x[0]<=>y[0]}
options_for_select(options, params[:oa])
end
def recount_to_compare_with_final_recount(final_recount)
recount = final_recount.booth_assignment.recounts.select {|r| r.date == final_recount.date}.first
recount.present? ? recount.count : "-"
end
def system_recount_to_compare_with_final_recount(final_recount)
final_recount.booth_assignment.voters.select {|v| v.created_at.to_date == final_recount.date}.size
end
def answer_result_value(question_id, answer_index)
return nil if params.blank?
return nil if params[:questions].blank?
return nil if params[:questions][question_id.to_s].blank?
params[:questions][question_id.to_s][answer_index.to_s]
end
end

View File

@@ -0,0 +1,7 @@
module PollFinalRecountsHelper
def final_recount_for_date(final_recounts, date)
final_recounts.select {|f| f.date == date}.first
end
end

View File

@@ -0,0 +1,15 @@
module PollRecountsHelper
def recount_for_date(recounts, date)
recounts.select {|r| r.date == date}.first
end
def booth_assignment_sum_recounts(ba)
ba.recounts.any? ? ba.recounts.to_a.sum(&:count) : nil
end
def booth_assignment_sum_final_recounts(ba)
ba.final_recounts.any? ? ba.final_recounts.to_a.sum(&:count) :nil
end
end

View File

@@ -0,0 +1,49 @@
module PollsHelper
def poll_select_options(include_all=nil)
options = @polls.collect {|poll|
[poll.name, current_path_with_query_params(poll_id: poll.id)]
}
options << all_polls if include_all
options_for_select(options, request.fullpath)
end
def all_polls
[I18n.t("polls.all"), admin_questions_path]
end
def poll_dates(poll)
if poll.starts_at.blank? || poll.ends_at.blank?
I18n.t("polls.no_dates")
else
I18n.t("polls.dates", open_at: l(poll.starts_at.to_date), closed_at: l(poll.ends_at.to_date))
end
end
def poll_dates_select_options(poll)
options = []
(poll.starts_at.to_date..poll.ends_at.to_date).each do |date|
options << [l(date, format: :long), l(date)]
end
options_for_select(options, params[:d])
end
def poll_final_recount_option(poll)
final_date = poll.ends_at.to_date + 1.day
options_for_select([[I18n.t("polls.final_date"), l(final_date)]])
end
def poll_booths_select_options(poll)
options = []
poll.booths.each do |booth|
options << [booth_name_with_location(booth), booth.id]
end
options_for_select(options)
end
def booth_name_with_location(booth)
location = booth.location.blank? ? "" : " (#{booth.location})"
booth.name + location
end
end

View File

@@ -2,7 +2,7 @@ module SignatureSheetsHelper
def signable_options def signable_options
[[t("activerecord.models.proposal", count: 1), Proposal], [[t("activerecord.models.proposal", count: 1), Proposal],
[t("activerecord.models.spending_proposal", count: 1), SpendingProposal]] [t("activerecord.models.budget/investment", count: 1), Budget::Investment]]
end end
end end

View File

@@ -21,4 +21,11 @@ module StatsHelper
content_tag :div, "", opt content_tag :div, "", opt
end end
def budget_investments_chart_tag(opt={})
events = events.join(',') if events.is_a? Array
opt[:data] ||= {}
opt[:data][:graph] = admin_api_stats_path(budget_investments: true)
content_tag :div, "", opt
end
end end

View File

@@ -32,7 +32,7 @@ module Abilities
can :mark_featured, Debate can :mark_featured, Debate
can :unmark_featured, Debate can :unmark_featured, Debate
can :comment_as_administrator, [Debate, Comment, Proposal, Budget::Investment] can :comment_as_administrator, [Debate, Comment, Proposal, Poll::Question, Budget::Investment]
can [:search, :create, :index, :destroy], ::Moderator can [:search, :create, :index, :destroy], ::Moderator
can [:search, :create, :index, :summary], ::Valuator can [:search, :create, :index, :summary], ::Valuator
@@ -50,7 +50,20 @@ module Abilities
can :create, Budget::ValuatorAssignment can :create, Budget::ValuatorAssignment
can [:search, :edit, :update, :create, :index, :destroy], Banner can [:search, :edit, :update, :create, :index, :destroy], Banner
can [:index, :create, :edit, :update, :destroy], Geozone can [:index, :create, :edit, :update, :destroy], Geozone
can [:read, :create, :update, :destroy, :add_question, :remove_question, :search_booths, :search_questions, :search_officers], Poll
can [:read, :create, :update, :destroy], Poll::Booth
can [:search, :create, :index, :destroy], ::Poll::Officer
can [:create, :destroy], ::Poll::BoothAssignment
can [:create, :destroy], ::Poll::OfficerAssignment
can [:read, :create, :update], Poll::Question
can :destroy, Poll::Question # , comments_count: 0, votes_up: 0
can :manage, SiteCustomization::Page
can :manage, SiteCustomization::Image
can :manage, SiteCustomization::ContentBlock
end end
end end
end end

View File

@@ -46,12 +46,19 @@ module Abilities
can :create, SpendingProposal can :create, SpendingProposal
can :create, Budget::Investment, budget: { phase: "accepting" } can :create, Budget::Investment, budget: { phase: "accepting" }
can :destroy, Budget::Investment, budget: { phase: ["accepting", "reviewing"] }, author_id: user.id
can :vote, Budget::Investment, budget: { phase: "selecting" } can :vote, Budget::Investment, budget: { phase: "selecting" }
can [:show, :create], Budget::Ballot, budget: { phase: "balloting" } can [:show, :create], Budget::Ballot, budget: { phase: "balloting" }
can [:create, :destroy], Budget::Ballot::Line, budget: { phase: "balloting" } can [:create, :destroy], Budget::Ballot::Line, budget: { phase: "balloting" }
can :create, DirectMessage can :create, DirectMessage
can :show, DirectMessage, sender_id: user.id can :show, DirectMessage, sender_id: user.id
can :answer, Poll do |poll|
poll.answerable_by?(user)
end
can :answer, Poll::Question do |question|
question.answerable_by?(user)
end
end end
can [:create, :show], ProposalNotification, proposal: { author_id: user.id } can [:create, :show], ProposalNotification, proposal: { author_id: user.id }

View File

@@ -6,6 +6,8 @@ module Abilities
can [:read, :map], Debate can [:read, :map], Debate
can [:read, :map, :summary], Proposal can [:read, :map, :summary], Proposal
can :read, Comment can :read, Comment
can :read, Poll
can :read, Poll::Question
can [:read, :welcome], Budget can [:read, :welcome], Budget
can :read, Budget::Investment can :read, Budget::Investment
can :read, SpendingProposal can :read, SpendingProposal

View File

@@ -5,7 +5,7 @@ module Abilities
def initialize(user) def initialize(user)
self.merge Abilities::Moderation.new(user) self.merge Abilities::Moderation.new(user)
can :comment_as_moderator, [Debate, Comment, Proposal, Budget::Investment] can :comment_as_moderator, [Debate, Comment, Proposal, Budget::Investment, Poll::Question]
end end
end end
end end

View File

@@ -2,7 +2,7 @@ class Activity < ActiveRecord::Base
belongs_to :actionable, -> { with_hidden }, polymorphic: true belongs_to :actionable, -> { with_hidden }, polymorphic: true
belongs_to :user, -> { with_hidden } belongs_to :user, -> { with_hidden }
VALID_ACTIONS = %w( hide block restore ) VALID_ACTIONS = %w( hide block restore valuate )
validates :action, inclusion: {in: VALID_ACTIONS} validates :action, inclusion: {in: VALID_ACTIONS}
@@ -10,6 +10,7 @@ class Activity < ActiveRecord::Base
scope :on_debates, -> { where(actionable_type: 'Debate') } scope :on_debates, -> { where(actionable_type: 'Debate') }
scope :on_users, -> { where(actionable_type: 'User') } scope :on_users, -> { where(actionable_type: 'User') }
scope :on_comments, -> { where(actionable_type: 'Comment') } scope :on_comments, -> { where(actionable_type: 'Comment') }
scope :on_budget_investments, -> { where(actionable_type: 'Budget::Investment') }
scope :for_render, -> { includes(user: [:moderator, :administrator]).includes(:actionable) } scope :for_render, -> { includes(user: [:moderator, :administrator]).includes(:actionable) }
def self.log(user, action, actionable) def self.log(user, action, actionable)

View File

@@ -35,6 +35,10 @@ class Budget < ActiveRecord::Base
2000 2000
end end
def self.title_max_length
80
end
def accepting? def accepting?
phase == "accepting" phase == "accepting"
end end
@@ -63,6 +67,10 @@ class Budget < ActiveRecord::Base
phase == "finished" phase == "finished"
end end
def balloting_or_later?
balloting? || reviewing_ballots? || finished?
end
def on_hold? def on_hold?
reviewing? || valuating? || reviewing_ballots? reviewing? || valuating? || reviewing_ballots?
end end

View File

@@ -66,7 +66,8 @@ class Budget
end end
def heading_for_group(group) def heading_for_group(group)
self.headings.where(group: group).first return nil unless has_lines_in_group?(group)
self.investments.where(group: group).first.heading
end end
end end

View File

@@ -2,7 +2,7 @@ class Budget
class Ballot class Ballot
class Line < ActiveRecord::Base class Line < ActiveRecord::Base
belongs_to :ballot belongs_to :ballot
belongs_to :investment belongs_to :investment, counter_cache: :ballot_lines_count
belongs_to :heading belongs_to :heading
belongs_to :group belongs_to :group
belongs_to :budget belongs_to :budget

View File

@@ -25,6 +25,7 @@ class Budget
validates :description, presence: true validates :description, presence: true
validates :heading_id, presence: true validates :heading_id, presence: true
validates_presence_of :unfeasibility_explanation, if: :unfeasibility_explanation_required? validates_presence_of :unfeasibility_explanation, if: :unfeasibility_explanation_required?
validates_presence_of :price, if: :price_required?
validates :title, length: { in: 4..Budget::Investment.title_max_length } validates :title, length: { in: 4..Budget::Investment.title_max_length }
validates :description, length: { maximum: Budget::Investment.description_max_length } validates :description, length: { maximum: Budget::Investment.description_max_length }
@@ -45,7 +46,8 @@ class Budget
scope :not_unfeasible, -> { where.not(feasibility: "unfeasible") } scope :not_unfeasible, -> { where.not(feasibility: "unfeasible") }
scope :undecided, -> { where(feasibility: "undecided") } scope :undecided, -> { where(feasibility: "undecided") }
scope :with_supports, -> { where('cached_votes_up > 0') } scope :with_supports, -> { where('cached_votes_up > 0') }
scope :selected, -> { where(selected: true) } scope :selected, -> { feasible.where(selected: true) }
scope :unselected, -> { feasible.where(selected: false) }
scope :last_week, -> { where("created_at >= ?", 7.days.ago)} scope :last_week, -> { where("created_at >= ?", 7.days.ago)}
scope :by_group, -> (group_id) { where(group_id: group_id) } scope :by_group, -> (group_id) { where(group_id: group_id) }
@@ -75,34 +77,6 @@ class Budget
results.includes(:heading, :group, :budget, administrator: :user, valuators: :user) results.includes(:heading, :group, :budget, administrator: :user, valuators: :user)
end end
def self.limit_results(results, budget, max_per_heading, max_for_no_heading)
return results if max_per_heading <= 0 && max_for_no_heading <= 0
ids = []
if max_per_heading > 0
budget.headings.pluck(:id).each do |hid|
ids += Investment.where(heading_id: hid).order(confidence_score: :desc).limit(max_per_heading).pluck(:id)
end
end
if max_for_no_heading > 0
ids += Investment.no_heading.order(confidence_score: :desc).limit(max_for_no_heading).pluck(:id)
end
conditions = ["investments.id IN (?)"]
values = [ids]
if max_per_heading == 0
conditions << "investments.heading_id IS NOT ?"
values << nil
elsif max_for_no_heading == 0
conditions << "investments.heading_id IS ?"
values << nil
end
results.where(conditions.join(' OR '), *values)
end
def searchable_values def searchable_values
{ title => 'A', { title => 'A',
author.username => 'B', author.username => 'B',
@@ -136,6 +110,10 @@ class Budget
unfeasible? && valuation_finished? unfeasible? && valuation_finished?
end end
def price_required?
feasible? && valuation_finished?
end
def unfeasible_email_pending? def unfeasible_email_pending?
unfeasible_email_sent_at.blank? && unfeasible? && valuation_finished? unfeasible_email_sent_at.blank? && unfeasible? && valuation_finished?
end end
@@ -225,7 +203,7 @@ class Budget
def should_show_aside? def should_show_aside?
(budget.selecting? && !unfeasible?) || (budget.selecting? && !unfeasible?) ||
(budget.balloting? && feasible?) || (budget.balloting? && feasible?) ||
(budget.valuating? && feasible?) (budget.valuating? && !unfeasible?)
end end
def should_show_votes? def should_show_votes?
@@ -240,17 +218,25 @@ class Budget
budget.balloting? budget.balloting?
end end
def should_show_price?
feasible? &&
selected? &&
(budget.reviewing_ballots? || budget.finished?)
end
def should_show_price_info?
feasible? &&
price_explanation.present? &&
(budget.balloting? || budget.reviewing_ballots? || budget.finished?)
end
def formatted_price def formatted_price
budget.formatted_amount(price) budget.formatted_amount(price)
end end
def self.apply_filters_and_search(budget, params) def self.apply_filters_and_search(budget, params, current_filter=nil)
investments = all investments = all
if budget.balloting? investments = investments.send(current_filter) if current_filter.present?
investments = investments.selected
else
investments = params[:unfeasible].present? ? investments.unfeasible : investments.not_unfeasible
end
investments = investments.by_heading(params[:heading_id]) if params[:heading_id].present? investments = investments.by_heading(params[:heading_id]) if params[:heading_id].present?
investments = investments.search(params[:search]) if params[:search].present? investments = investments.search(params[:search]) if params[:search].present?
investments investments
@@ -259,8 +245,9 @@ class Budget
private private
def set_denormalized_ids def set_denormalized_ids
self.group_id ||= self.heading.try(:group_id) self.group_id = self.heading.try(:group_id) if self.heading_id_changed?
self.budget_id ||= self.heading.try(:group).try(:budget_id) self.budget_id ||= self.heading.try(:group).try(:budget_id)
end end
end end
end end

View File

@@ -12,7 +12,8 @@ class Comment < ActiveRecord::Base
validates :body, presence: true validates :body, presence: true
validates :user, presence: true validates :user, presence: true
validates_inclusion_of :commentable_type, in: ["Debate", "Proposal", "Budget::Investment"]
validates_inclusion_of :commentable_type, in: ["Debate", "Proposal", "Budget::Investment", "Poll::Question"]
validate :validate_body_length validate :validate_body_length

View File

@@ -12,7 +12,7 @@ module Searchable
}, },
ignoring: :accents, ignoring: :accents,
ranked_by: '(:tsearch)', ranked_by: '(:tsearch)',
order_within_rank: "#{self.table_name}.cached_votes_up DESC" order_within_rank: (self.column_names.include?('cached_votes_up') ? "#{self.table_name}.cached_votes_up DESC" : nil)
} }
end end

View File

@@ -1,3 +1,4 @@
class FailedCensusCall < ActiveRecord::Base class FailedCensusCall < ActiveRecord::Base
belongs_to :user, counter_cache: true belongs_to :user, counter_cache: true
belongs_to :poll_officer, class_name: 'Poll::Officer', counter_cache: true
end end

View File

@@ -12,6 +12,10 @@ class Geozone < ActiveRecord::Base
Geozone.pluck(:name) Geozone.pluck(:name)
end end
def self.city
where(name: 'city').first
end
def safe_to_destroy? def safe_to_destroy?
Geozone.reflect_on_all_associations(:has_many).all? do |association| Geozone.reflect_on_all_associations(:has_many).all? do |association|
association.klass.where(geozone: self).empty? association.klass.where(geozone: self).empty?

View File

@@ -0,0 +1,126 @@
class Officing::Residence
include ActiveModel::Model
include ActiveModel::Validations::Callbacks
attr_accessor :user, :officer, :document_number, :document_type, :year_of_birth
before_validation :call_census_api
validates_presence_of :document_number
validates_presence_of :document_type
validates_presence_of :year_of_birth
validate :allowed_age
validate :residence_in_madrid
def initialize(attrs={})
super
clean_document_number
end
def save
return false unless valid?
if user_exists?
self.user = find_user_by_document
self.user.update(verified_at: Time.current)
else
user_params = {
document_number: document_number,
document_type: document_type,
geozone: self.geozone,
date_of_birth: date_of_birth.to_datetime,
gender: gender,
residence_verified_at: Time.current,
verified_at: Time.current,
erased_at: Time.current,
password: random_password,
terms_of_service: '1',
email: nil
}
self.user = User.create!(user_params)
end
end
def store_failed_census_call
FailedCensusCall.create({
user: user,
document_number: document_number,
document_type: document_type,
year_of_birth: year_of_birth,
poll_officer: officer
})
end
def user_exists?
find_user_by_document.present?
end
def find_user_by_document
User.where(document_number: document_number,
document_type: document_type).first
end
def residence_in_madrid
return if errors.any?
unless residency_valid?
store_failed_census_call
errors.add(:residence_in_madrid, false)
end
end
def allowed_age
return if errors[:year_of_birth].any?
return unless @census_api_response.valid?
unless allowed_age?
errors.add(:year_of_birth, I18n.t('verification.residence.new.error_not_allowed_age'))
end
end
def allowed_age?
Age.in_years(date_of_birth) >= User.minimum_required_age
end
def geozone
Geozone.where(census_code: district_code).first
end
def district_code
@census_api_response.district_code
end
def gender
@census_api_response.gender
end
def date_of_birth
@census_api_response.date_of_birth
end
private
def call_census_api
@census_api_response = CensusApi.new.call(document_type, document_number)
end
def residency_valid?
@census_api_response.valid? &&
@census_api_response.date_of_birth.year.to_s == year_of_birth.to_s
end
def census_year_of_birth
@census_api_response.date_of_birth.year
end
def clean_document_number
self.document_number = self.document_number.gsub(/[^a-z0-9]+/i, "").upcase unless self.document_number.blank?
end
def random_password
(0...20).map { ('a'..'z').to_a[rand(26)] }.join
end
end

65
app/models/poll.rb Normal file
View File

@@ -0,0 +1,65 @@
class Poll < ActiveRecord::Base
has_many :booth_assignments, class_name: "Poll::BoothAssignment"
has_many :booths, through: :booth_assignments
has_many :partial_results, through: :booth_assignments
has_many :white_results, through: :booth_assignments
has_many :null_results, through: :booth_assignments
has_many :voters
has_many :officer_assignments, through: :booth_assignments
has_many :officers, through: :officer_assignments
has_many :questions
has_and_belongs_to_many :geozones
validates :name, presence: true
validate :date_range
scope :current, -> { where('starts_at <= ? and ? <= ends_at', Time.current, Time.current) }
scope :incoming, -> { where('? < starts_at', Time.current) }
scope :expired, -> { where('ends_at < ?', Time.current) }
scope :published, -> { where('published = ?', true) }
scope :by_geozone_id, ->(geozone_id) { where(geozones: {id: geozone_id}.joins(:geozones)) }
scope :sort_for_list, -> { order(:geozone_restricted, :starts_at, :name) }
def current?(timestamp = DateTime.current)
starts_at <= timestamp && timestamp <= ends_at
end
def incoming?(timestamp = DateTime.current)
timestamp < starts_at
end
def expired?(timestamp = DateTime.current)
ends_at < timestamp
end
def answerable_by?(user)
user.present? &&
user.level_two_or_three_verified? &&
current? &&
(!geozone_restricted || geozone_ids.include?(user.geozone_id))
end
def self.answerable_by(user)
return none if user.nil? || user.unverified?
current.joins('LEFT JOIN "geozones_polls" ON "geozones_polls"."poll_id" = "polls"."id"')
.where('geozone_restricted = ? OR geozones_polls.geozone_id = ?', false, user.geozone_id)
end
def votable_by?(user)
!document_has_voted?(user.document_number, user.document_type)
end
def document_has_voted?(document_number, document_type)
voters.where(document_number: document_number, document_type: document_type).exists?
end
def date_range
unless starts_at.present? && ends_at.present? && starts_at <= ends_at
errors.add(:starts_at, I18n.t('errors.messages.invalid_date_range'))
end
end
end

19
app/models/poll/answer.rb Normal file
View File

@@ -0,0 +1,19 @@
class Poll::Answer < ActiveRecord::Base
belongs_to :question, -> { with_hidden }
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
delegate :poll, :poll_id, to: :question
validates :question, presence: true
validates :author, presence: true
validates :answer, presence: true
validates :answer, inclusion: {in: ->(a) { a.question.valid_answers }}
scope :by_author, -> (author_id) { where(author_id: author_id) }
scope :by_question, -> (question_id) { where(question_id: question_id) }
def record_voter_participation
Poll::Voter.create!(user: author, poll: poll)
end
end

13
app/models/poll/booth.rb Normal file
View File

@@ -0,0 +1,13 @@
class Poll
class Booth < ActiveRecord::Base
has_many :booth_assignments, class_name: "Poll::BoothAssignment"
has_many :polls, through: :booth_assignments
validates :name, presence: true, uniqueness: true
def self.search(terms)
return Booth.none if terms.blank?
Booth.where("name ILIKE ? OR location ILIKE ?", "%#{terms}%", "%#{terms}%")
end
end
end

View File

@@ -0,0 +1,15 @@
class Poll
class BoothAssignment < ActiveRecord::Base
belongs_to :booth
belongs_to :poll
has_many :officer_assignments, class_name: "Poll::OfficerAssignment", dependent: :destroy
has_many :recounts, class_name: "Poll::Recount", dependent: :destroy
has_many :final_recounts, class_name: "Poll::FinalRecount", dependent: :destroy
has_many :officers, through: :officer_assignments
has_many :voters
has_many :partial_results
has_many :white_results
has_many :null_results
end
end

View File

@@ -0,0 +1,19 @@
class Poll
class FinalRecount < ActiveRecord::Base
belongs_to :booth_assignment, class_name: "Poll::BoothAssignment"
belongs_to :officer_assignment, class_name: "Poll::OfficerAssignment"
validates :booth_assignment_id, presence: true
validates :date, presence: true, uniqueness: {scope: :booth_assignment_id}
validates :count, presence: true, numericality: {only_integer: true}
before_save :update_logs
def update_logs
if self.count_changed? && self.count_was.present?
self.count_log += ":#{self.count_was.to_s}"
self.officer_assignment_id_log += ":#{self.officer_assignment_id_was.to_s}"
end
end
end
end

View File

@@ -0,0 +1,23 @@
class Poll::NullResult < ActiveRecord::Base
VALID_ORIGINS = %w{ web booth }
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
belongs_to :booth_assignment
belongs_to :officer_assignment
validates :author, presence: true
validates :origin, inclusion: {in: VALID_ORIGINS}
scope :by_author, -> (author_id) { where(author_id: author_id) }
before_save :update_logs
def update_logs
if self.amount_changed? && self.amount_was.present?
self.amount_log += ":#{self.amount_was.to_s}"
self.officer_assignment_id_log += ":#{self.officer_assignment_id_was.to_s}"
self.author_id_log += ":#{self.author_id_was.to_s}"
end
end
end

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