Merge branch 'master' into iss-1192-test

This commit is contained in:
Raimond Garcia
2017-04-12 11:51:48 +02:00
committed by GitHub
406 changed files with 11980 additions and 1214 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 }

56
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.1.9'
# 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,10 +28,10 @@ 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.3.0'
@@ -40,30 +40,32 @@ gem 'foundation_rails_helper', '~> 2.0.0'
gem 'acts_as_votable' gem 'acts_as_votable'
gem 'ckeditor', '~> 4.2.2' gem 'ckeditor', '~> 4.2.2'
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.2.0'
gem 'paranoia', '~> 2.2.0' gem 'paranoia', '~> 2.2.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.0.0.332'
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.5.5'
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'
group :development, :test do group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console # Call 'byebug' anywhere in the code to stop execution and get a debugger console
@@ -72,28 +74,28 @@ group :development, :test do
gem 'spring' gem 'spring'
gem 'spring-commands-rspec' gem 'spring-commands-rspec'
gem 'rspec-rails', '~> 3.5' gem 'rspec-rails', '~> 3.5'
gem 'capybara' gem 'capybara', '~> 2.13.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.12'
gem 'capistrano', '3.5.0', require: false gem 'capistrano', '~> 3.8.0', 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.47.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.14.0'
gem 'coveralls', require: false gem 'coveralls', '~> 0.8.19', 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.0)
ahoy_matey (1.5.3) public_suffix (~> 2.0, >= 2.0.2)
ahoy_matey (1.5.5)
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.1.2)
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.0)
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.13.0)
addressable addressable
mime-types (>= 1.16) mime-types (>= 1.16)
nokogiri (>= 1.3.3) nokogiri (>= 1.3.3)
@@ -109,9 +108,9 @@ 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.19)
json (>= 1.8, < 3) json (>= 1.8, < 3)
simplecov (~> 0.12.0) simplecov (~> 0.12.0)
term-ansicolor (~> 1.3) term-ansicolor (~> 1.3)
@@ -151,14 +150,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,20 +172,20 @@ 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)
groupdate (3.1.1) groupdate (3.2.0)
activesupport (>= 3) 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.12)
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)
@@ -200,26 +199,35 @@ GEM
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.0.3)
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.2)
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)
@@ -230,29 +238,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.0.0.332)
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)
@@ -265,24 +274,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)
activemodel (>= 4.2.0)
activesupport (>= 4.2.0)
cocaine (~> 0.5.5)
mime-types
mimemagic (~> 0.3.0)
paranoia (2.2.1)
activerecord (>= 4.0, < 5.1) activerecord (>= 4.0, < 5.1)
parser (2.3.3.1) 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.14.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)
@@ -292,40 +308,40 @@ 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.1)
raindrops (0.17.0) raindrops (0.17.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.3.0)
railties (>= 4.2.0, < 5.1) railties (>= 4.2.0, < 5.1)
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.5.4)
rspec-support (~> 3.5.0) rspec-support (~> 3.5.0)
@@ -344,8 +360,8 @@ GEM
rspec-mocks (~> 3.5.0) rspec-mocks (~> 3.5.0)
rspec-support (~> 3.5.0) rspec-support (~> 3.5.0)
rspec-support (3.5.0) rspec-support (3.5.0)
rubocop (0.45.0) rubocop (0.47.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)
@@ -355,9 +371,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)
@@ -377,12 +393,12 @@ GEM
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)
@@ -396,7 +412,7 @@ 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.12.0)
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.4.0)
@@ -405,10 +421,10 @@ GEM
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)
@@ -421,9 +437,9 @@ GEM
tilt (>= 1.4, < 3) tilt (>= 1.4, < 3)
tzinfo (1.2.2) tzinfo (1.2.2)
thread_safe (~> 0.1) thread_safe (~> 0.1)
uglifier (3.0.4) uglifier (3.1.9)
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.2.0)
kgio (~> 2.6) kgio (~> 2.6)
raindrops (~> 0.7) raindrops (~> 0.7)
@@ -439,7 +455,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)
@@ -453,20 +469,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.5.5)
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.0)
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.13.0)
ckeditor (~> 4.2.2) ckeditor (~> 4.2.2)
coffee-rails (~> 4.2.1) coffee-rails (~> 4.2.1)
coveralls coveralls (~> 0.8.19)
daemons daemons
dalli dalli
database_cleaner database_cleaner
@@ -475,50 +491,54 @@ 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
groupdate (~> 3.1.0) groupdate (~> 3.2.0)
i18n-tasks i18n-tasks (~> 0.9.12)
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.0.0.332)
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.2.1)
pg (~> 0.20.0)
pg_search pg_search
poltergeist poltergeist (~> 1.14.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.3.0)
rinku (~> 2.0.2) rinku (~> 2.0.2)
rollbar (~> 2.14.0) rollbar (~> 2.14.1)
rspec-rails (~> 3.5) rspec-rails (~> 3.5)
rubocop (~> 0.45.0) rubocop (~> 0.47.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.1.9)
unicorn (~> 5.2.0) unicorn (~> 5.2.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,46 @@ table.investment-projects-summary {
white-space: nowrap; white-space: nowrap;
} }
} }
.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

@@ -144,6 +144,10 @@ a {
padding-top: $line-height; padding-top: $line-height;
} }
.light {
background: $light;
}
.highlight { .highlight {
background: $highlight; background: $highlight;
} }
@@ -204,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;
} }
@@ -462,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;
@@ -483,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;
@@ -585,7 +613,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;
@@ -1104,11 +1133,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 {
@@ -1166,7 +1196,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;
@@ -1174,9 +1208,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);
@@ -1214,9 +1250,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);
@@ -1254,9 +1292,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);
@@ -1293,6 +1333,48 @@ 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;
}
}
.social { .social {
a { a {
@@ -1384,6 +1466,30 @@ table {
color: #CE3E26; color: #CE3E26;
} }
} }
.ssb-telegram {
background: #0088cc;
color: white;
height: $line-height;
position: relative;
width: $line-height*2;
&:before {
content: "A";
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;
}
} }
} }
@@ -1202,7 +1208,7 @@ ul.ballot-list {
} }
} }
// 07. Proposals successfull // 07. Proposals successful
// ------------------------- // -------------------------
.dark-heading { .dark-heading {
@@ -1235,7 +1241,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 +1265,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 +1289,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 +1310,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

@@ -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

@@ -129,16 +129,16 @@ module CommentableActions
when '4' when '4'
1.year.ago 1.year.ago
else else
Date.parse(params[:advanced_search][:date_min]) rescue nil Date.parse(params[:advanced_search][:date_min]) rescue 100.years.ago
end end
end end
def search_finish_date def search_finish_date
params[:advanced_search][:date_max].try(:to_date) || Date.today (params[:advanced_search][:date_max].to_date rescue Date.today) || Date.today
end end
def search_date_range def search_date_range
search_start_date.beginning_of_day..search_finish_date.end_of_day [100.years.ago, search_start_date].max.beginning_of_day..[search_finish_date, Date.today].min.end_of_day
end end
def set_search_order def set_search_order

View File

@@ -22,7 +22,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

@@ -28,8 +28,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 +103,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

@@ -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)

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

@@ -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

@@ -53,6 +53,12 @@ module Abilities
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

@@ -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 }
@@ -136,6 +137,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 +230,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?
@@ -259,7 +264,7 @@ 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

View File

@@ -10,7 +10,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

@@ -9,6 +9,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

View File

@@ -0,0 +1,26 @@
class Poll
class Officer < ActiveRecord::Base
belongs_to :user
has_many :officer_assignments, class_name: "Poll::OfficerAssignment"
has_many :failed_census_calls, foreign_key: :poll_officer_id
validates :user_id, presence: true, uniqueness: true
delegate :name, :email, to: :user
def voting_days_assigned_polls
officer_assignments.voting_days.includes(booth_assignment: :poll).
map(&:booth_assignment).
map(&:poll).uniq.compact.
sort {|x, y| y.ends_at <=> x.ends_at}
end
def final_days_assigned_polls
officer_assignments.final.includes(booth_assignment: :poll).
map(&:booth_assignment).
map(&:poll).uniq.compact.
sort {|x, y| y.ends_at <=> x.ends_at}
end
end
end

View File

@@ -0,0 +1,25 @@
class Poll
class OfficerAssignment < ActiveRecord::Base
belongs_to :officer
belongs_to :booth_assignment
has_one :recount
has_many :final_recounts
has_many :partial_results
has_many :voters
validates :officer_id, presence: true
validates :booth_assignment_id, presence: true
validates :date, presence: true, uniqueness: { scope: [:officer_id, :booth_assignment_id] }
delegate :poll_id, :booth_id, to: :booth_assignment
scope :voting_days, -> { where(final: false) }
scope :final, -> { where(final: true) }
before_create :log_user_data
def log_user_data
self.user_data_log = "#{officer.user_id} - #{officer.user.name_and_email}"
end
end
end

View File

@@ -0,0 +1,28 @@
class Poll::PartialResult < ActiveRecord::Base
VALID_ORIGINS = %w{ web booth }
belongs_to :question, -> { with_hidden }
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
belongs_to :booth_assignment
belongs_to :officer_assignment
validates :question, presence: true
validates :author, presence: true
validates :answer, presence: true
validates :answer, inclusion: {in: ->(a) { a.question.valid_answers }}
validates :origin, inclusion: {in: VALID_ORIGINS}
scope :by_author, -> (author_id) { where(author_id: author_id) }
scope :by_question, -> (question_id) { where(question_id: question_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

View File

@@ -0,0 +1,70 @@
class Poll::Question < ActiveRecord::Base
include Measurable
include Searchable
acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases
belongs_to :poll
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
has_many :comments, as: :commentable
has_many :answers
has_many :partial_results
belongs_to :proposal
validates :title, presence: true
validates :author, presence: true
validates :title, length: { minimum: 4 }
validates :description, length: { maximum: Poll::Question.description_max_length }
scope :by_poll_id, ->(poll_id) { where(poll_id: poll_id) }
scope :sort_for_list, -> { order('poll_questions.proposal_id IS NULL', :created_at)}
scope :for_render, -> { includes(:author, :proposal) }
def self.search(params)
results = self.all
results = results.by_poll_id(params[:poll_id]) if params[:poll_id].present?
results = results.pg_search(params[:search]) if params[:search].present?
results
end
def searchable_values
{ title => 'A',
proposal.try(:title) => 'A',
description => 'B',
author.username => 'C',
author_visible_name => 'C' }
end
def description
super.try :html_safe
end
def valid_answers
(super.try(:split, ',').compact || []).map(&:strip)
end
def copy_attributes_from_proposal(proposal)
if proposal.present?
self.author = proposal.author
self.author_visible_name = proposal.author.name
self.proposal_id = proposal.id
self.title = proposal.title
self.description = proposal.description
self.valid_answers = I18n.t('poll_questions.default_valid_answers')
end
end
def answerable_by?(user)
poll.answerable_by?(user)
end
def self.answerable_by(user)
return none if user.nil? || user.unverified?
where(poll_id: Poll.answerable_by(user).pluck(:id))
end
end

View File

@@ -0,0 +1,20 @@
class Poll
class Recount < 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 :officer_assignment_id, 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

59
app/models/poll/voter.rb Normal file
View File

@@ -0,0 +1,59 @@
class Poll
class Voter < ActiveRecord::Base
belongs_to :poll
belongs_to :user
belongs_to :geozone
belongs_to :booth_assignment
belongs_to :officer_assignment
validates :poll_id, presence: true
validates :user_id, presence: true
validates :document_number, presence: true, uniqueness: { scope: [:poll_id, :document_type], message: :has_voted }
before_validation :set_demographic_info, :set_document_info
def set_demographic_info
return unless user.present?
self.gender = user.gender
self.age = user.age
self.geozone = user.geozone
end
def set_document_info
return unless user.present?
self.document_type = user.document_type
self.document_number = user.document_number
end
private
def in_census?
census_api_response.valid?
end
def census_api_response
@census_api_response ||= CensusApi.new.call(document_type, document_number)
end
def fill_stats_fields
if in_census?
self.gender = census_api_response.gender
self.geozone_id = Geozone.select(:id).where(census_code: census_api_response.district_code).first.try(:id)
self.age = voter_age(census_api_response.date_of_birth)
end
end
def voter_age(dob)
if dob.blank?
nil
else
now = Time.now.utc.to_date
now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
end
end
end
end

View File

@@ -0,0 +1,23 @@
class Poll::WhiteResult < 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

View File

@@ -45,12 +45,12 @@ class Proposal < ActiveRecord::Base
scope :sort_by_relevance, -> { all } scope :sort_by_relevance, -> { all }
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) } scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
scope :sort_by_archival_date, -> { archived.sort_by_confidence_score } scope :sort_by_archival_date, -> { archived.sort_by_confidence_score }
scope :archived, -> { where("proposals.created_at <= ?", Setting["months_to_archive_proposals"].to_i.months.ago)} scope :archived, -> { where("proposals.created_at <= ?", Setting["months_to_archive_proposals"].to_i.months.ago) }
scope :not_archived, -> { where("proposals.created_at > ?", Setting["months_to_archive_proposals"].to_i.months.ago)} scope :not_archived, -> { where("proposals.created_at > ?", Setting["months_to_archive_proposals"].to_i.months.ago) }
scope :last_week, -> { where("proposals.created_at >= ?", 7.days.ago)} scope :last_week, -> { where("proposals.created_at >= ?", 7.days.ago)}
scope :retired, -> { where.not(retired_at: nil) } scope :retired, -> { where.not(retired_at: nil) }
scope :not_retired, -> { where(retired_at: nil) } scope :not_retired, -> { where(retired_at: nil) }
scope :successfull, -> { where("cached_votes_up >= ?", Proposal.votes_needed_for_success)} scope :successful, -> { where("cached_votes_up >= ?", Proposal.votes_needed_for_success) }
def to_param def to_param
"#{id}-#{title}".parameterize "#{id}-#{title}".parameterize
@@ -155,7 +155,7 @@ class Proposal < ActiveRecord::Base
Setting['votes_for_proposal_success'].to_i Setting['votes_for_proposal_success'].to_i
end end
def successfull? def successful?
total_votes >= Proposal.votes_needed_for_success total_votes >= Proposal.votes_needed_for_success
end end

View File

@@ -12,35 +12,30 @@ class Signature < ActiveRecord::Base
before_validation :clean_document_number before_validation :clean_document_number
def verified?
user_exists? || in_census?
end
def verify def verify
if verified?
assign_vote
mark_as_verified
end
end
def assign_vote
if user_exists? if user_exists?
assign_vote_to_user assign_vote_to_user
else mark_as_verified
elsif in_census?
create_user create_user
assign_vote_to_user assign_vote_to_user
mark_as_verified
end end
end end
def assign_vote_to_user def assign_vote_to_user
set_user set_user
signable.register_vote(user, "yes") if signable.is_a? Budget::Investment
signable.vote_by(voter: user, vote: 'yes') if [nil, :no_selecting_allowed].include?(signable.reason_for_not_being_selectable_by(user))
else
signable.register_vote(user, "yes")
end
assign_signature_to_vote assign_signature_to_vote
end end
def assign_signature_to_vote def assign_signature_to_vote
vote = Vote.where(votable: signable, voter: user).first vote = Vote.where(votable: signable, voter: user).first
vote.update(signature: self) vote.update(signature: self) if vote
end end
def user_exists? def user_exists?
@@ -51,11 +46,14 @@ class Signature < ActiveRecord::Base
user_params = { user_params = {
document_number: document_number, document_number: document_number,
created_from_signature: true, created_from_signature: true,
verified_at: Time.now, verified_at: Time.current,
erased_at: Time.now, erased_at: Time.current,
password: random_password, password: random_password,
terms_of_service: '1', terms_of_service: '1',
email: nil email: nil,
date_of_birth: @census_api_response.date_of_birth,
gender: @census_api_response.gender,
geozone: Geozone.where(census_code: @census_api_response.district_code).first
} }
User.create!(user_params) User.create!(user_params)
end end
@@ -70,10 +68,17 @@ class Signature < ActiveRecord::Base
end end
def in_census? def in_census?
response = document_types.detect do |document_type| document_types.detect do |document_type|
CensusApi.new.call(document_type, document_number).valid? response = CensusApi.new.call(document_type, document_number)
if response.valid?
@census_api_response = response
true
else
false
end
end end
response.present?
@census_api_response.present?
end end
def set_user def set_user

View File

@@ -2,7 +2,7 @@ class SignatureSheet < ActiveRecord::Base
belongs_to :signable, polymorphic: true belongs_to :signable, polymorphic: true
belongs_to :author, class_name: 'User', foreign_key: 'author_id' belongs_to :author, class_name: 'User', foreign_key: 'author_id'
VALID_SIGNABLES = %w( Proposal SpendingProposal ) VALID_SIGNABLES = %w( Proposal Budget::Investment SpendingProposal )
has_many :signatures has_many :signatures

View File

@@ -0,0 +1,5 @@
module SiteCustomization
def self.table_name_prefix
'site_customization_'
end
end

View File

@@ -0,0 +1,11 @@
class SiteCustomization::ContentBlock < ActiveRecord::Base
VALID_BLOCKS = %w(top_links footer)
validates :locale, presence: true, inclusion: { in: I18n.available_locales.map(&:to_s) }
validates :name, presence: true, uniqueness: { scope: :locale }, inclusion: { in: VALID_BLOCKS }
def self.block_for(name, locale)
locale ||= I18n.default_locale
find_by(name: name, locale: locale).try(:body)
end
end

View File

@@ -0,0 +1,48 @@
class SiteCustomization::Image < ActiveRecord::Base
VALID_IMAGES = {
"icon_home" => [330, 240],
"logo_header" => [80, 80],
"social-media-icon" => [200, 200],
"apple-touch-icon-200" => [200, 200]
}
has_attached_file :image
validates :name, presence: true, uniqueness: true, inclusion: { in: VALID_IMAGES.keys }
validates_attachment_content_type :image, :content_type => ["image/png"]
validate :check_image
def self.all_images
VALID_IMAGES.keys.map do |image_name|
find_by(name: image_name) || create!(name: image_name.to_s)
end
end
def self.image_path_for(filename)
image_name = filename.split(".").first
if i = find_by(name: image_name)
i.image.exists? ? i.image.url : nil
end
end
def required_width
VALID_IMAGES[name].try(:first)
end
def required_height
VALID_IMAGES[name].try(:second)
end
private
def check_image
return unless image?
dimensions = Paperclip::Geometry.from_file(image.queued_for_write[:original].path)
errors.add(:image, :image_width, required_width: required_width) unless dimensions.width == required_width
errors.add(:image, :image_height, required_height: required_height) unless dimensions.height == required_height
end
end

View File

@@ -0,0 +1,16 @@
class SiteCustomization::Page < ActiveRecord::Base
VALID_STATUSES = %w(draft published)
validates :slug, presence: true,
uniqueness: { case_sensitive: false },
format: { with: /\A[0-9a-zA-Z\-_]*\Z/, message: :slug_format }
validates :title, presence: true
validates :status, presence: true, inclusion: { in: VALID_STATUSES }
scope :published, -> { where(status: 'published').order('id DESC') }
scope :with_more_info_flag, -> { where(status: 'published', more_info_flag: true).order('id ASC') }
def url
"/#{slug}"
end
end

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