diff --git a/Gemfile b/Gemfile
index 675e738b1..ab00df68e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -19,7 +19,7 @@ gem 'jquery-ui-rails'
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks'
-gem 'devise', '~> 3.5.6'
+gem 'devise', '~> 3.5.7'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'
gem 'omniauth'
@@ -39,7 +39,7 @@ gem 'ckeditor', '~> 4.1.5'
gem 'cancancan'
gem 'social-share-button', git: 'https://github.com/huacnlee/social-share-button.git', ref: 'e46a6a3e82b86023bc'
gem 'initialjs-rails', '0.2.0.1'
-gem 'unicorn', '~> 5.0.1'
+gem 'unicorn', '~> 5.1.0'
gem 'paranoia'
gem 'rinku', require: 'rails_rinku'
gem 'savon'
@@ -52,13 +52,14 @@ gem 'newrelic_rpm', '~> 3.14'
gem 'whenever', require: false
gem 'pg_search'
-gem 'ahoy_matey', '~> 1.2.1'
+gem 'ahoy_matey', '~> 1.4.0'
gem 'groupdate' # group temporary data
gem 'tolk' # Web interface for translations
gem 'browser'
gem 'turnout'
gem 'redcarpet'
+
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug'
@@ -73,7 +74,7 @@ group :development, :test do
gem 'quiet_assets'
gem 'letter_opener_web', '~> 1.3.0'
gem 'i18n-tasks'
- gem 'capistrano', '3.4.0', require: false
+ gem 'capistrano', '3.4.1', require: false
gem "capistrano-bundler", '1.1.4', require: false
gem "capistrano-rails", '1.1.6', require: false
gem "capistrano-rvm", require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 18fa95bd9..12cb537d3 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -49,14 +49,15 @@ GEM
activerecord (>= 3.2, < 5)
acts_as_votable (0.10.0)
addressable (2.4.0)
- ahoy_matey (1.2.1)
+ ahoy_matey (1.4.0)
addressable
- browser (>= 0.4.0)
- errbase
+ browser (~> 2.0)
geocoder
- rails
+ rack-attack
+ railties
referer-parser (>= 0.3.0)
request_store
+ safely_block
user_agent_parser
uuidtools
akami (1.3.1)
@@ -70,15 +71,15 @@ GEM
babel-transpiler (0.7.0)
babel-source (>= 4.0, < 6)
execjs (~> 2.0)
- bcrypt (3.1.10)
- browser (1.1.0)
+ bcrypt (3.1.11)
+ browser (2.0.3)
builder (3.2.2)
bullet (5.0.0)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.9.0)
- byebug (8.2.2)
+ byebug (8.2.4)
cancancan (1.13.1)
- capistrano (3.4.0)
+ capistrano (3.4.1)
i18n
rake (>= 10.0.0)
sshkit (~> 1.3)
@@ -91,9 +92,9 @@ GEM
capistrano-rvm (0.1.2)
capistrano (~> 3.0)
sshkit (~> 1.2)
- capistrano3-delayed-job (1.6.0)
+ capistrano3-delayed-job (1.7.0)
capistrano (>= 3.0.0)
- capybara (2.6.2)
+ capybara (2.7.0)
addressable
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
@@ -125,14 +126,14 @@ GEM
tins (~> 1.6.0)
daemons (1.2.3)
dalli (2.7.6)
- database_cleaner (1.5.1)
+ database_cleaner (1.5.2)
debug_inspector (0.0.2)
delayed_job (4.1.1)
activesupport (>= 3.0, < 5.0)
delayed_job_active_record (4.1.0)
activerecord (>= 3.0, < 5)
delayed_job (>= 3.0, < 5)
- devise (3.5.6)
+ devise (3.5.7)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
@@ -154,16 +155,16 @@ GEM
errbase (0.0.3)
erubis (2.7.0)
execjs (2.6.0)
- factory_girl (4.5.0)
+ factory_girl (4.7.0)
activesupport (>= 3.0.0)
- factory_girl_rails (4.6.0)
- factory_girl (~> 4.5.0)
+ factory_girl_rails (4.7.0)
+ factory_girl (~> 4.7.0)
railties (>= 3.0.0)
faker (1.6.3)
i18n (~> 0.5)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
- foundation-rails (6.2.0.1)
+ foundation-rails (6.2.1.0)
railties (>= 3.1.0)
sass (>= 3.3.0, < 3.5)
sprockets-es6 (>= 0.9.0)
@@ -176,7 +177,7 @@ GEM
fuubar (2.0.0)
rspec (~> 3.0)
ruby-progressbar (~> 1.4)
- geocoder (1.3.1)
+ geocoder (1.3.4)
globalid (0.3.6)
activesupport (>= 4.1.0)
groupdate (2.5.2)
@@ -208,7 +209,7 @@ GEM
jquery-ui-rails (5.0.5)
railties (>= 3.2.16)
json (1.8.3)
- jwt (1.5.3)
+ jwt (1.5.4)
kaminari (0.16.3)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
@@ -223,9 +224,11 @@ GEM
railties (>= 3.2)
loofah (2.0.3)
nokogiri (>= 1.5.9)
- mail (2.6.3)
- mime-types (>= 1.16, < 3)
- mime-types (2.99.1)
+ mail (2.6.4)
+ mime-types (>= 1.16, < 4)
+ mime-types (3.0)
+ mime-types-data (~> 3.2015)
+ mime-types-data (3.2016.0221)
mini_portile2 (2.0.0)
minitest (5.8.4)
multi_json (1.11.2)
@@ -233,8 +236,8 @@ GEM
multipart-post (2.0.0)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
- net-ssh (3.0.2)
- newrelic_rpm (3.15.0.314)
+ net-ssh (3.1.1)
+ newrelic_rpm (3.15.1.316)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
nori (2.6.0)
@@ -284,6 +287,8 @@ GEM
rack (1.6.4)
rack-accept (0.4.5)
rack (>= 0.4)
+ rack-attack (4.4.1)
+ rack
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.6)
@@ -310,12 +315,12 @@ GEM
activesupport (= 4.2.6)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
- raindrops (0.15.0)
- rake (11.1.1)
+ raindrops (0.16.0)
+ rake (11.1.2)
redcarpet (3.3.4)
referer-parser (0.3.0)
- request_store (1.3.0)
- responders (2.1.1)
+ request_store (1.3.1)
+ responders (2.1.2)
railties (>= 4.2.0, < 5.1)
rinku (1.7.3)
rollbar (2.8.3)
@@ -343,7 +348,9 @@ GEM
rspec-support (3.4.1)
ruby-progressbar (1.7.5)
safe_yaml (1.0.4)
- sass (3.4.21)
+ safely_block (0.1.0)
+ errbase
+ sass (3.4.22)
sass-rails (5.0.4)
railties (>= 4.0.0, < 5.0)
sass (~> 3.1)
@@ -365,10 +372,10 @@ GEM
json (~> 1.8)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
- spring (1.6.4)
+ spring (1.7.1)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
- sprockets (3.5.2)
+ sprockets (3.6.0)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-es6 (0.9.0)
@@ -379,7 +386,7 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
- sshkit (1.8.1)
+ sshkit (1.9.0)
net-scp (>= 1.1.2)
net-ssh (>= 2.8.0)
term-ansicolor (1.3.2)
@@ -395,17 +402,16 @@ GEM
safe_yaml (>= 0.8.6)
turbolinks (2.5.3)
coffee-rails
- turnout (2.2.1)
+ turnout (2.3.0)
rack (~> 1.3)
rack-accept (~> 0.4)
+ tilt (>= 1.4, < 3)
tzinfo (1.2.2)
thread_safe (~> 0.1)
- uglifier (2.7.2)
- execjs (>= 0.3.0)
- json (>= 1.8.0)
- unicorn (5.0.1)
+ uglifier (3.0.0)
+ execjs (>= 0.3.0, < 3)
+ unicorn (5.1.0)
kgio (~> 2.6)
- rack
raindrops (~> 0.7)
uniform_notifier (1.9.0)
user_agent_parser (2.3.0)
@@ -433,13 +439,13 @@ PLATFORMS
DEPENDENCIES
acts-as-taggable-on
acts_as_votable
- ahoy_matey (~> 1.2.1)
+ ahoy_matey (~> 1.4.0)
ancestry
browser
bullet
byebug
cancancan
- capistrano (= 3.4.0)
+ capistrano (= 3.4.1)
capistrano-bundler (= 1.1.4)
capistrano-rails (= 1.1.6)
capistrano-rvm
@@ -452,7 +458,7 @@ DEPENDENCIES
dalli
database_cleaner
delayed_job_active_record (~> 4.1.0)
- devise (~> 3.5.6)
+ devise (~> 3.5.7)
devise-async
email_spec
factory_girl_rails
@@ -494,7 +500,7 @@ DEPENDENCIES
turbolinks
turnout
uglifier (>= 1.3.0)
- unicorn (~> 5.0.1)
+ unicorn (~> 5.1.0)
web-console (~> 3.0)
whenever
diff --git a/README.md b/README.md
index 251a0f8e3..e6181f38c 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ bin/rails s
```
-Prerequisites for testing: install PhantomJS >= 2.0
+Prerequisites for testing: install PhantomJS >= 1.9.8
Run the tests with:
@@ -56,10 +56,26 @@ Run the tests with:
bin/rspec
```
-## Licence
+You can use the default admin user from the seeds file:
+
+ **user:** admin@madrid.es
+ **pass:** 12345678
+
+But for some actions like voting, you will need a verified user, the seeds file also includes one:
+
+ **user:** verified@madrid.es
+ **pass:** 12345678
+
+### OAuth
+
+To test authentication services with external OAuth suppliers - right now Twitter, Facebook and Google - you'll need to create an "application" in each of the supported platforms and set the *key* and *secret* provided in your *secrets.yml*
+
+In the case of Google, verify that the APIs *Contacts API* and *Google+ API* are enabled for the application.
+
+## License
Code published under AFFERO GPL v3 (see [LICENSE-AGPLv3.txt](LICENSE-AGPLv3.txt))
## Contributions
-See [CONTRIBUTING_EN.md](CONTRIBUTING_EN.md)
\ No newline at end of file
+See [CONTRIBUTING_EN.md](CONTRIBUTING_EN.md)
diff --git a/README_ES.md b/README_ES.md
index c3fd3cdff..e0392bc51 100644
--- a/README_ES.md
+++ b/README_ES.md
@@ -47,7 +47,7 @@ Para ejecutar la aplicación en local:
bin/rails s
```
-Prerequisitos para los tests: tener instalado PhantomJS >= 2.0
+Prerequisitos para los tests: tener instalado PhantomJS >= 1.9.8
Para ejecutar los tests:
@@ -55,6 +55,17 @@ Para ejecutar los tests:
bin/rspec
```
+Puedes usar el usuario administrador por defecto del fichero seeds:
+
+ **user:** admin@madrid.es
+ **pass:** 12345678
+
+Pero para ciertas acciones, como apoyar, necesitarás un usuario verificado, el fichero seeds proporciona uno:
+
+ **user:** verified@madrid.es
+ **pass:** 12345678
+
+
### OAuth
Para probar los servicios de autenticación mediante proveedores externos OAuth — en este momento Twitter, Facebook y Google —, necesitas crear una "aplicación" en cada una de las plataformas soportadas y configurar la *key* y el *secret* proporcionados en tu *secrets.yml*
diff --git a/app/assets/fonts/icons.eot b/app/assets/fonts/icons.eot
index 7323d578f..5fe0c1d3d 100644
Binary files a/app/assets/fonts/icons.eot and b/app/assets/fonts/icons.eot differ
diff --git a/app/assets/fonts/icons.svg b/app/assets/fonts/icons.svg
index e272b9550..51044c3e2 100644
--- a/app/assets/fonts/icons.svg
+++ b/app/assets/fonts/icons.svg
@@ -48,4 +48,5 @@
+
diff --git a/app/assets/fonts/icons.ttf b/app/assets/fonts/icons.ttf
index 4db54078d..b0b156581 100644
Binary files a/app/assets/fonts/icons.ttf and b/app/assets/fonts/icons.ttf differ
diff --git a/app/assets/fonts/icons.woff b/app/assets/fonts/icons.woff
index 33d2d4dee..edcc9bc61 100644
Binary files a/app/assets/fonts/icons.woff and b/app/assets/fonts/icons.woff differ
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 68d9c636c..e24d144bb 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -43,6 +43,7 @@
//= require forms
//= require tracks
//= require valuation_spending_proposal_form
+//= require embed_video
var initialize_modules = function() {
App.Comments.initialize();
@@ -61,6 +62,7 @@ var initialize_modules = function() {
App.Forms.initialize();
App.Tracks.initialize();
App.ValuationSpendingProposalForm.initialize();
+ App.EmbedVideo.initialize();
};
$(function(){
diff --git a/app/assets/javascripts/embed_video.js.coffee b/app/assets/javascripts/embed_video.js.coffee
new file mode 100644
index 000000000..85c18ac8e
--- /dev/null
+++ b/app/assets/javascripts/embed_video.js.coffee
@@ -0,0 +1,7 @@
+App.EmbedVideo =
+
+ initialize: ->
+ $('#js-embedded-video').each ->
+ code = $(this).data("video-code")
+ $('#js-embedded-video').html(code)
+
\ No newline at end of file
diff --git a/app/assets/javascripts/votes.js.coffee b/app/assets/javascripts/votes.js.coffee
index 43469909d..e4223c644 100644
--- a/app/assets/javascripts/votes.js.coffee
+++ b/app/assets/javascripts/votes.js.coffee
@@ -5,11 +5,13 @@ App.Votes =
$("div.anonymous-votes", votes).show();
$("div.organizations-votes", votes).show();
$("div.not-logged", votes).show();
+ $("div.no-supports-allowed", votes).show();
$("div.logged", votes).hide();
, ->
$("div.anonymous-votes", votes).hide();
$("div.organizations-votes", votes).hide();
$("div.not-logged", votes).hide();
+ $("div.no-supports-allowed", votes).hide();
$("div.logged", votes).show();
initialize: ->
diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss
index e01655f6d..1ccd7c880 100644
--- a/app/assets/stylesheets/admin.scss
+++ b/app/assets/stylesheets/admin.scss
@@ -116,7 +116,7 @@ body.admin {
[class^="icon-"] {
display: inline-block;
font-size: rem-calc(24);
- padding-right: rem-calc(24);
+ padding-right: rem-calc(12);
padding-top: rem-calc(4);
padding-left: 12px\9 !important;
padding-right: 12px\9 !important;
@@ -349,3 +349,22 @@ body.admin {
margin-bottom: 0;
}
+body.admin {
+
+ .investment-projects-list.medium-9 {
+ width: 100%;
+ }
+}
+
+.admin-content .select-geozone {
+
+ a {
+ display: block;
+
+ &.active {
+ color: $brand;
+ font-weight: bold;
+ text-decoration: underline;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 822e4cd4c..68065e82a 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -11,4 +11,4 @@
@import "annotator.min";
@import "annotator_overrides";
@import "jquery-ui/datepicker";
-@import "datepicker_overrides";
\ No newline at end of file
+@import "datepicker_overrides";
diff --git a/app/assets/stylesheets/icons.scss b/app/assets/stylesheets/icons.scss
index b13b09f46..2c06f745a 100644
--- a/app/assets/stylesheets/icons.scss
+++ b/app/assets/stylesheets/icons.scss
@@ -160,3 +160,6 @@
.icon-no-notification:before {
content: "\78";
}
+.icon-whatsapp:before {
+ content: "\50";
+}
diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss
index bc6aa3694..d897a2a28 100644
--- a/app/assets/stylesheets/participation.scss
+++ b/app/assets/stylesheets/participation.scss
@@ -164,10 +164,12 @@
.progress {
background-color: rgba(255,255,255,.8);
- height: rem-calc(12);
+ height: $line-height/2;
.meter {
background: $votes-like;
+ display: block;
+ height: $line-height/2;
}
}
@@ -238,7 +240,7 @@
}
}
- .anonymous-votes, .organizations-votes {
+ .anonymous-votes, .organizations-votes, .no-supports-allowed {
background: $warning-bg;
color: $warning-color;
height: 100%;
@@ -274,7 +276,7 @@
.debate-new, .debate-edit,
.proposal-new, .proposal-edit,
-.spending-proposal-new, .spending-proposal-edit {
+.spending-proposal-new, .spending-proposal-edit {
.back {
@include back;
@@ -335,13 +337,30 @@
// 03. Show participation
// - - - - - - - - - - - - - - - - - - - - - - - - -
-.debate-show, .proposal-show {
+.debate-show, .proposal-show, .investment-project-show {
+
+ p {
+ word-wrap: break-word;
+ }
+
+ .social-share-full .social-share-button {
+ display:inline;
+ }
+
+ .whatsapp:before {
+ background-color: #43d854;
+ color: white;
+ font-size: 1.7em;
+ margin-left: rem-calc(0.5);
+ padding: rem-calc(9.5) rem-calc(9.8);
+ vertical-align: rem-calc(10);
+ }
.edit-debate, .edit-proposal {
margin-bottom: 0;
}
- .debate-info, .proposal-info {
+ .debate-info, .proposal-info, .investment-project-info {
clear: both;
color: $text-medium;
font-size: $small-font-size;
@@ -532,7 +551,7 @@
}
}
-.debate, .proposal {
+.debate, .proposal, .investment-project {
margin-bottom: 0;
margin-top: 0;
@@ -551,7 +570,7 @@
padding-bottom: rem-calc(12);
}
- .label-debate, .label-proposal {
+ .label-debate, .label-proposal, .label-investment-project {
background: none;
clear: both;
display: block;
@@ -573,6 +592,10 @@
color: $proposals;
}
+ .label-investment-project {
+ color: $budget;
+ }
+
h3 {
font-weight: bold;
margin: 0;
@@ -582,7 +605,7 @@
}
}
- .debate-content, .proposal-content {
+ .debate-content, .proposal-content, .investment-project-content {
margin: 0;
min-height: rem-calc(180);
position: relative;
@@ -592,24 +615,27 @@
}
}
- .icon-debates, .icon-proposals {
+ .icon-debates, .icon-proposals, .icon-budget {
font-size: rem-calc(18);
line-height: $line-height;
- position: absolute;
+ margin-left: rem-calc(6);
top: 0;
}
.icon-debates {
color: $debates;
- left: rem-calc(48);
}
.icon-proposals {
color: $proposals;
- left: rem-calc(72);
}
- .debate-info, .proposal-info {
+ .icon-budget {
+ color: $budget;
+ font-size: $small-font-size;
+ }
+
+ .debate-info, .proposal-info, .investment-project-info {
color: $text-medium;
font-size: $small-font-size;
margin: rem-calc(6) 0 0;
@@ -624,7 +650,7 @@
}
}
- .debate-description, .proposal-description {
+ .debate-description, .proposal-description, .investment-project-description {
color: $text;
font-size: rem-calc(13);
height: rem-calc(72);
@@ -785,6 +811,62 @@
}
}
+.investment-project, .investment-project-show {
+
+ .supports {
+ @include supports;
+ background: none;
+ border: 0;
+ border-left: 1px solid $border;
+ margin: 0 rem-calc(-12);
+ min-height: rem-calc(180);
+ padding-top: $line-height*2;
+
+ &:after {
+ content: none;
+ }
+
+ .button-support {
+ background: $budget;
+ color: white;
+
+ &:hover {
+ background: $budget-hover;
+ color: white;
+ cursor: pointer;
+ }
+
+ &:active {
+ opacity: .75;
+ }
+ }
+
+ .total-supports {
+ color: $budget;
+ font-size: $base-font-size;
+ font-weight: bold;
+ }
+
+ .supported {
+ color: $budget;
+ }
+
+ .no-supports-allowed {
+ background: rgba(69,67,114,.96);
+ color: white;
+ padding: rem-calc(12);
+ }
+
+ .no-supports-allowed p, .no-supports-allowed a {
+ color: white;
+ }
+ }
+}
+
+.investment-project-show .supports {
+ border: 0;
+}
+
.proposals-summary {
.panel {
@@ -792,6 +874,12 @@
}
}
+.investment-project .supports .total-supports.no-button,
+.investment-project-show .supports .total-supports.no-button {
+ display: block;
+ margin-top: $line-height*1.5;
+}
+
// 05. Featured
// - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/app/assets/stylesheets/variables.scss b/app/assets/stylesheets/variables.scss
index 6882442d1..5d59ebf8f 100644
--- a/app/assets/stylesheets/variables.scss
+++ b/app/assets/stylesheets/variables.scss
@@ -74,6 +74,7 @@ $proposals: #FFA42D;
$proposals-border: #CC8425;
$budget: #454372;
+$budget-hover: #7571BF;
$highlight: #E7F2FC;
$featured: #FED900;
diff --git a/app/controllers/admin/api/stats_controller.rb b/app/controllers/admin/api/stats_controller.rb
index dfedb1c54..e58d9d754 100644
--- a/app/controllers/admin/api/stats_controller.rb
+++ b/app/controllers/admin/api/stats_controller.rb
@@ -1,7 +1,9 @@
class Admin::Api::StatsController < Admin::Api::BaseController
def show
- unless params[:events].present? || params[:visits].present?
+ unless params[:events].present? ||
+ params[:visits].present? ||
+ params[:spending_proposals].present?
return render json: {}, status: :bad_request
end
@@ -18,7 +20,10 @@ class Admin::Api::StatsController < Admin::Api::BaseController
ds.add "Visits", Visit.group_by_day(:started_at).count
end
+ if params[:spending_proposals].present?
+ ds.add "Spending proposals", SpendingProposal.group_by_day(:created_at).count
+ end
+
render json: ds.build
end
-
end
diff --git a/app/controllers/admin/spending_proposals_controller.rb b/app/controllers/admin/spending_proposals_controller.rb
index 25707e66e..e7d1cc45c 100644
--- a/app/controllers/admin/spending_proposals_controller.rb
+++ b/app/controllers/admin/spending_proposals_controller.rb
@@ -7,7 +7,7 @@ class Admin::SpendingProposalsController < Admin::BaseController
load_and_authorize_resource
def index
- @spending_proposals = SpendingProposal.search(params, @current_filter).order(created_at: :desc).page(params[:page])
+ @spending_proposals = SpendingProposal.scoped_filter(params, @current_filter).order(created_at: :desc).page(params[:page])
end
def show
@@ -21,10 +21,8 @@ class Admin::SpendingProposalsController < Admin::BaseController
def update
if @spending_proposal.update(spending_proposal_params)
- path = admin_spending_proposal_path( @spending_proposal,
- { anchor: 'classification' }.merge(SpendingProposal.filter_params(params)))
-
- redirect_to path, notice: t("flash.actions.update.spending_proposal")
+ redirect_to admin_spending_proposal_path(@spending_proposal, SpendingProposal.filter_params(params)),
+ notice: t("flash.actions.update.spending_proposal")
else
render :edit
end
@@ -33,7 +31,7 @@ class Admin::SpendingProposalsController < Admin::BaseController
private
def spending_proposal_params
- params.require(:spending_proposal).permit(:administrator_id, :tag_list, valuator_ids: [])
+ params.require(:spending_proposal).permit(:title, :description, :external_url, :geozone_id, :association_name, :administrator_id, :tag_list, valuator_ids: [])
end
end
diff --git a/app/controllers/admin/stats_controller.rb b/app/controllers/admin/stats_controller.rb
index f724a054c..f21ceea9b 100644
--- a/app/controllers/admin/stats_controller.rb
+++ b/app/controllers/admin/stats_controller.rb
@@ -18,10 +18,10 @@ class Admin::StatsController < Admin::BaseController
@verified_users = User.with_hidden.level_two_or_three_verified.count
@unverified_users = User.with_hidden.unverified.count
@users = User.with_hidden.count
-
- @user_ids_who_voted_proposals =
- ActsAsVotable::Vote.where(votable_type: 'Proposal').pluck(:voter_id).uniq.count
+ @user_ids_who_voted_proposals =
+ ActsAsVotable::Vote.where(votable_type: 'Proposal').pluck(:voter_id).uniq.count
@user_ids_who_didnt_vote_proposals = @verified_users - @user_ids_who_voted_proposals
- end
+ @spending_proposals = SpendingProposal.count
+ end
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 280dd0c4d..640adf299 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -77,6 +77,10 @@ class ApplicationController < ActionController::Base
@proposal_votes = current_user ? current_user.proposal_votes(proposals) : {}
end
+ def set_spending_proposal_votes(spending_proposals)
+ @spending_proposal_votes = current_user ? current_user.spending_proposal_votes(spending_proposals) : {}
+ end
+
def set_comment_flags(comments)
@comment_flags = current_user ? current_user.comment_flags(comments) : {}
end
diff --git a/app/controllers/concerns/commentable_actions.rb b/app/controllers/concerns/commentable_actions.rb
index 855ad1de5..01066c8ad 100644
--- a/app/controllers/concerns/commentable_actions.rb
+++ b/app/controllers/concerns/commentable_actions.rb
@@ -65,8 +65,7 @@ module CommentableActions
end
end
-
- def map
+ def map
@resource = resource_model.new
@tag_cloud = tag_cloud
end
diff --git a/app/controllers/management/account_controller.rb b/app/controllers/management/account_controller.rb
new file mode 100644
index 000000000..85b9feea4
--- /dev/null
+++ b/app/controllers/management/account_controller.rb
@@ -0,0 +1,13 @@
+class Management::AccountController < Management::BaseController
+
+ before_action :only_verified_users
+
+ def show
+ end
+
+ private
+ def only_verified_users
+ check_verified_user t("management.account.alert.unverified_user")
+ end
+
+end
diff --git a/app/controllers/management/base_controller.rb b/app/controllers/management/base_controller.rb
index bced745b4..f01bbebbc 100644
--- a/app/controllers/management/base_controller.rb
+++ b/app/controllers/management/base_controller.rb
@@ -20,6 +20,16 @@ class Management::BaseController < ActionController::Base
@managed_user ||= Verification::Management::ManagedUser.find(session[:document_type], session[:document_number])
end
+ def current_user
+ managed_user
+ end
+
+ def check_verified_user(alert_msg)
+ unless current_user.level_two_or_three_verified?
+ redirect_to management_document_verifications_path, alert: alert_msg
+ end
+ end
+
def set_locale
if params[:locale] && I18n.available_locales.include?(params[:locale].to_sym)
session[:locale] = params[:locale]
diff --git a/app/controllers/management/proposals_controller.rb b/app/controllers/management/proposals_controller.rb
index 6e01a8c77..3f4284a06 100644
--- a/app/controllers/management/proposals_controller.rb
+++ b/app/controllers/management/proposals_controller.rb
@@ -2,7 +2,7 @@ class Management::ProposalsController < Management::BaseController
include HasOrders
include CommentableActions
- before_action :check_verified_user, except: :print
+ before_action :only_verified_users, except: :print
before_action :set_proposal, only: [:vote, :show]
before_action :parse_search_terms, only: :index
before_action :load_categories, only: [:new, :edit]
@@ -40,14 +40,8 @@ class Management::ProposalsController < Management::BaseController
Proposal
end
- def check_verified_user
- unless current_user.level_two_or_three_verified?
- redirect_to management_document_verifications_path, alert: t("management.proposals.alert.unverified_user")
- end
- end
-
- def current_user
- managed_user
+ def only_verified_users
+ check_verified_user t("management.proposals.alert.unverified_user")
end
### Duplicated in application_controller. Move to a concern.
diff --git a/app/controllers/management/spending_proposals_controller.rb b/app/controllers/management/spending_proposals_controller.rb
index 3a17d86b1..49378ca63 100644
--- a/app/controllers/management/spending_proposals_controller.rb
+++ b/app/controllers/management/spending_proposals_controller.rb
@@ -1,6 +1,12 @@
class Management::SpendingProposalsController < Management::BaseController
- before_action :check_verified_user
+ before_action :only_verified_users, except: :print
+ before_action :set_spending_proposal, only: [:vote, :show]
+
+ def index
+ @spending_proposals = apply_filters_and_search(SpendingProposal).order(cached_votes_up: :desc).page(params[:page]).for_render
+ set_spending_proposal_votes(@spending_proposals)
+ end
def new
@spending_proposal = SpendingProposal.new
@@ -18,23 +24,55 @@ class Management::SpendingProposalsController < Management::BaseController
end
def show
- @spending_proposal = SpendingProposal.find(params[:id])
+ set_spending_proposal_votes(@spending_proposal)
+ end
+
+ def vote
+ @spending_proposal.register_vote(current_user, 'yes')
+ set_spending_proposal_votes(@spending_proposal)
+ end
+
+ def print
+ params[:geozone] ||= 'all'
+ @spending_proposals = apply_filters_and_search(SpendingProposal).order(cached_votes_up: :desc).for_render.limit(15)
+ set_spending_proposal_votes(@spending_proposals)
end
private
+ def set_spending_proposal
+ @spending_proposal = SpendingProposal.find(params[:id])
+ end
+
def spending_proposal_params
params.require(:spending_proposal).permit(:title, :description, :external_url, :geozone_id, :terms_of_service, :captcha, :captcha_key)
end
- def check_verified_user
- unless current_user.level_two_or_three_verified?
- redirect_to management_document_verifications_path, alert: t("management.spending_proposals.alert.unverified_user")
+ def only_verified_users
+ check_verified_user t("management.spending_proposals.alert.unverified_user")
+ end
+
+ # This should not be necessary. Maybe we could create a specific show view for managers.
+ def set_spending_proposal_votes(spending_proposals)
+ @spending_proposal_votes = current_user ? current_user.spending_proposal_votes(spending_proposals) : {}
+ end
+
+ def set_geozone_name
+ if params[:geozone] == 'all'
+ @geozone_name = t('geozones.none')
+ else
+ @geozone_name = Geozone.find(params[:geozone]).name
end
end
- def current_user
- managed_user
+ def apply_filters_and_search(target)
+ target = params[:unfeasible].present? ? target.unfeasible : target.not_unfeasible
+ if params[:geozone].present?
+ target = target.by_geozone(params[:geozone])
+ set_geozone_name
+ end
+ target = target.search(params[:search]) if params[:search].present?
+ target
end
end
diff --git a/app/controllers/management/users_controller.rb b/app/controllers/management/users_controller.rb
index 53c432fd1..8ee04cfdf 100644
--- a/app/controllers/management/users_controller.rb
+++ b/app/controllers/management/users_controller.rb
@@ -18,6 +18,12 @@ class Management::UsersController < Management::BaseController
end
end
+ def erase
+ managed_user.erase(t("management.users.erased_by_manager", manager: current_manager['login'])) if current_manager.present?
+ destroy_session
+ redirect_to management_document_verifications_path, notice: t("management.users.erased_notice")
+ end
+
def logout
destroy_session
redirect_to management_root_url, notice: t("management.sessions.signed_out_managed_user")
diff --git a/app/controllers/spending_proposals_controller.rb b/app/controllers/spending_proposals_controller.rb
index 9b64c462d..9915b0b50 100644
--- a/app/controllers/spending_proposals_controller.rb
+++ b/app/controllers/spending_proposals_controller.rb
@@ -1,21 +1,28 @@
class SpendingProposalsController < ApplicationController
include FeatureFlags
- load_and_authorize_resource
-
before_action :authenticate_user!, except: [:index]
- before_action :verify_access, only: [:show]
- before_filter -> { flash.now[:notice] = flash[:notice].html_safe if flash[:html_safe] && flash[:notice] }
+ before_action -> { flash.now[:notice] = flash[:notice].html_safe if flash[:html_safe] && flash[:notice] }
+
+ load_and_authorize_resource
feature_flag :spending_proposals
+ respond_to :html, :js
+
def index
+ @spending_proposals = apply_filters_and_search(SpendingProposal).page(params[:page]).for_render
+ set_spending_proposal_votes(@spending_proposals)
end
def new
@spending_proposal = SpendingProposal.new
end
+ def show
+ set_spending_proposal_votes(@spending_proposal)
+ end
+
def create
@spending_proposal = SpendingProposal.new(spending_proposal_params)
@spending_proposal.author = current_user
@@ -29,19 +36,38 @@ class SpendingProposalsController < ApplicationController
end
def destroy
- spending_proposal = current_user.spending_proposals.find(params[:id])
+ spending_proposal = SpendingProposal.find(params[:id])
spending_proposal.destroy
redirect_to user_path(current_user, filter: 'spending_proposals'), notice: t('flash.actions.destroy.spending_proposal')
end
+ def vote
+ @spending_proposal.register_vote(current_user, 'yes')
+ set_spending_proposal_votes(@spending_proposal)
+ end
+
private
def spending_proposal_params
params.require(:spending_proposal).permit(:title, :description, :external_url, :geozone_id, :association_name, :terms_of_service, :captcha, :captcha_key)
end
- def verify_access
- raise CanCan::AccessDenied unless current_user.try(:valuator?) || current_user.try(:administrator?) || @spending_proposal.author == current_user
+ def set_geozone_name
+ if params[:geozone] == 'all'
+ @geozone_name = t('geozones.none')
+ else
+ @geozone_name = Geozone.find(params[:geozone]).name
+ end
+ end
+
+ def apply_filters_and_search(target)
+ target = params[:unfeasible].present? ? target.unfeasible : target.not_unfeasible
+ if params[:geozone].present?
+ target = target.by_geozone(params[:geozone])
+ set_geozone_name
+ end
+ target = target.search(params[:search]) if params[:search].present?
+ target
end
end
diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb
index 0bdd2a801..81eaa08cb 100644
--- a/app/controllers/users/omniauth_callbacks_controller.rb
+++ b/app/controllers/users/omniauth_callbacks_controller.rb
@@ -41,9 +41,7 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
def save_user(user)
- @user.save ||
- @user.save_requiring_finish_signup ||
- @user.save_requiring_finish_signup_without_email
+ @user.save || @user.save_requiring_finish_signup
end
end
diff --git a/app/controllers/valuation/spending_proposals_controller.rb b/app/controllers/valuation/spending_proposals_controller.rb
index f8c63c974..d3bc585b1 100644
--- a/app/controllers/valuation/spending_proposals_controller.rb
+++ b/app/controllers/valuation/spending_proposals_controller.rb
@@ -9,8 +9,9 @@ class Valuation::SpendingProposalsController < Valuation::BaseController
load_and_authorize_resource
def index
+ @geozone_filters = geozone_filters
if current_user.valuator?
- @spending_proposals = SpendingProposal.search(params_for_current_valuator, @current_filter).order(created_at: :desc).page(params[:page])
+ @spending_proposals = SpendingProposal.scoped_filter(params_for_current_valuator, @current_filter).order(cached_votes_up: :desc).page(params[:page])
else
@spending_proposals = SpendingProposal.none.page(params[:page])
end
@@ -18,6 +19,11 @@ class Valuation::SpendingProposalsController < Valuation::BaseController
def valuate
if valid_price_params? && @spending_proposal.update(valuation_params)
+
+ if @spending_proposal.unfeasible_email_pending?
+ @spending_proposal.send_unfeasible_email
+ end
+
redirect_to valuation_spending_proposal_path(@spending_proposal), notice: t('valuation.spending_proposals.notice.valuate')
else
render action: :edit
@@ -26,6 +32,25 @@ class Valuation::SpendingProposalsController < Valuation::BaseController
private
+ def geozone_filters
+ spending_proposals = SpendingProposal.by_valuator(current_user.valuator.try(:id)).valuation_open.all.to_a
+
+ [ { name: t('valuation.spending_proposals.index.geozone_filter_all'),
+ id: nil,
+ pending_count: spending_proposals.size
+ },
+ { name: t('geozones.none'),
+ id: 'all',
+ pending_count: spending_proposals.count{|x| x.geozone_id.nil?}
+ }
+ ] + Geozone.all.order(name: :asc).collect do |g|
+ { name: g.name,
+ id: g.id,
+ pending_count: spending_proposals.count{|x| x.geozone_id == g.id}
+ }
+ end.select{ |x| x[:pending_count] > 0 }
+ end
+
def valuation_params
params[:spending_proposal][:feasible] = nil if params[:spending_proposal][:feasible] == 'nil'
diff --git a/app/helpers/embed_videos_helper.rb b/app/helpers/embed_videos_helper.rb
new file mode 100644
index 000000000..b48799bf5
--- /dev/null
+++ b/app/helpers/embed_videos_helper.rb
@@ -0,0 +1,30 @@
+module EmbedVideosHelper
+
+ def embedded_video_code
+ link = @proposal.video_url
+ if link.match(/vimeo.*/)
+ server = "Vimeo"
+ elsif link.match(/youtu*.*/)
+ server = "YouTube"
+ end
+
+ if server == "Vimeo"
+ regExp = /vimeo.*(staffpicks\/|channels\/|videos\/|video\/|\/)([^#\&\?]*).*/
+ src = "https://player.vimeo.com/video/"
+ elsif server == "YouTube"
+ regExp = /youtu.*(be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
+ src = "https://www.youtube.com/embed/"
+ end
+
+ if regExp
+ match = link.match(regExp)
+ end
+
+ if match and match[2]
+ ''
+ else
+ ''
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/app/helpers/spending_proposals_helper.rb b/app/helpers/spending_proposals_helper.rb
index 43ef7311a..ba68ad42a 100644
--- a/app/helpers/spending_proposals_helper.rb
+++ b/app/helpers/spending_proposals_helper.rb
@@ -3,4 +3,14 @@ module SpendingProposalsHelper
def spending_proposal_tags_select_options
ActsAsTaggableOn::Tag.spending_proposal_tags.pluck(:name)
end
+
+ def namespaced_spending_proposal_path(spending_proposal, options={})
+ @namespace_spending_proposal_path ||= namespace
+ case @namespace_spending_proposal_path
+ when "management"
+ management_spending_proposal_path(spending_proposal, options)
+ else
+ spending_proposal_path(spending_proposal, options)
+ end
+ end
end
\ No newline at end of file
diff --git a/app/helpers/stats_helper.rb b/app/helpers/stats_helper.rb
index 5116b60dd..097711b88 100644
--- a/app/helpers/stats_helper.rb
+++ b/app/helpers/stats_helper.rb
@@ -14,4 +14,10 @@ module StatsHelper
content_tag :div, "", opt
end
+ def spending_proposals_chart_tag(opt={})
+ events = events.join(',') if events.is_a? Array
+ opt[:data] ||= {}
+ opt[:data][:graph] = admin_api_stats_path(spending_proposals: true)
+ content_tag :div, "", opt
+ end
end
diff --git a/app/helpers/valuation_helper.rb b/app/helpers/valuation_helper.rb
index 9e3a054c1..0983ea567 100644
--- a/app/helpers/valuation_helper.rb
+++ b/app/helpers/valuation_helper.rb
@@ -2,9 +2,9 @@ module ValuationHelper
def valuator_select_options(valuator=nil)
if valuator.present?
- Valuator.where.not(id: valuator.id).order('users.username asc').includes(:user).collect { |v| [ v.name, v.id ] }.prepend([valuator.name, valuator.id])
+ Valuator.where.not(id: valuator.id).order("description ASC").order("users.email ASC").includes(:user).collect { |v| [ v.description_or_email, v.id ] }.prepend([valuator.description_or_email, valuator.id])
else
- Valuator.all.order('users.username asc').includes(:user).collect { |v| [ v.name, v.id ] }
+ Valuator.all.order("description ASC").order("users.email ASC").includes(:user).collect { |v| [ v.description_or_email, v.id ] }
end
end
diff --git a/app/mailers/mailer.rb b/app/mailers/mailer.rb
index 356f2ea45..82abbb205 100644
--- a/app/mailers/mailer.rb
+++ b/app/mailers/mailer.rb
@@ -7,7 +7,7 @@ class Mailer < ApplicationMailer
@comment = comment
@commentable = comment.commentable
with_user(@commentable.author) do
- mail(to: @commentable.author.email, subject: t('mailers.comment.subject', commentable: t("activerecord.models.#{@commentable.class.name.downcase}", count: 1).downcase)) if @commentable.present? && @commentable.author.present?
+ mail(to: @commentable.author.email, subject: t('mailers.comment.subject', commentable: t("activerecord.models.#{@commentable.class.name.underscore}", count: 1).downcase)) if @commentable.present? && @commentable.author.present?
end
end
@@ -33,6 +33,15 @@ class Mailer < ApplicationMailer
end
end
+ def unfeasible_spending_proposal(spending_proposal)
+ @spending_proposal = spending_proposal
+ @author = spending_proposal.author
+
+ with_user(@author) do
+ mail(to: @author.email, subject: t('mailers.unfeasible_spending_proposal.subject', code: @spending_proposal.code))
+ end
+ end
+
private
def with_user(user, &block)
diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb
index 78cb60806..1b440795f 100644
--- a/app/models/abilities/administrator.rb
+++ b/app/models/abilities/administrator.rb
@@ -37,7 +37,7 @@ module Abilities
can :manage, Annotation
- can [:read, :update], SpendingProposal
+ can [:read, :update, :destroy], SpendingProposal
end
end
end
diff --git a/app/models/abilities/common.rb b/app/models/abilities/common.rb
index 92eacea52..5bb43725b 100644
--- a/app/models/abilities/common.rb
+++ b/app/models/abilities/common.rb
@@ -43,8 +43,8 @@ module Abilities
if user.level_two_or_three_verified?
can :vote, Proposal
can :vote_featured, Proposal
+ can :vote, SpendingProposal
can :create, SpendingProposal
- can :destroy, SpendingProposal, author_id: user.id
end
can :create, Annotation
diff --git a/app/models/spending_proposal.rb b/app/models/spending_proposal.rb
index 237da73f7..d8ae2acf0 100644
--- a/app/models/spending_proposal.rb
+++ b/app/models/spending_proposal.rb
@@ -2,8 +2,10 @@ class SpendingProposal < ActiveRecord::Base
include Measurable
include Sanitizable
include Taggable
+ include Searchable
apply_simple_captcha
+ acts_as_votable
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
belongs_to :geozone
@@ -24,12 +26,17 @@ class SpendingProposal < ActiveRecord::Base
scope :managed, -> { valuation_open.where(valuation_assignments_count: 0).where("administrator_id IS NOT ?", nil) }
scope :valuating, -> { valuation_open.where("valuation_assignments_count > 0 AND valuation_finished = ?", false) }
scope :valuation_finished, -> { where(valuation_finished: true) }
+ scope :feasible, -> { where(feasible: true) }
+ scope :unfeasible, -> { where(feasible: false) }
+ scope :not_unfeasible, -> { where("feasible IS ? OR feasible = ?", nil, true) }
scope :by_admin, -> (admin) { where(administrator_id: admin.presence) }
scope :by_tag, -> (tag_name) { tagged_with(tag_name) }
scope :by_valuator, -> (valuator) { where("valuation_assignments.valuator_id = ?", valuator.presence).joins(:valuation_assignments) }
- scope :for_render, -> { includes(:geozone, administrator: :user, valuators: :user) }
+ scope :for_render, -> { includes(:geozone) }
+
+ before_validation :set_responsible_name
def description
super.try :html_safe
@@ -39,14 +46,26 @@ class SpendingProposal < ActiveRecord::Base
params.select{|x,_| %w{geozone_id administrator_id tag_name valuator_id}.include? x.to_s }
end
- def self.search(params, current_filter)
+ def self.scoped_filter(params, current_filter)
results = self
results = results.by_geozone(params[:geozone_id]) if params[:geozone_id].present?
results = results.by_admin(params[:administrator_id]) if params[:administrator_id].present?
results = results.by_tag(params[:tag_name]) if params[:tag_name].present?
results = results.by_valuator(params[:valuator_id]) if params[:valuator_id].present?
results = results.send(current_filter) if current_filter.present?
- results.for_render
+ results.includes(:geozone, administrator: :user, valuators: :user)
+ end
+
+ def searchable_values
+ { title => 'A',
+ author.username => 'B',
+ geozone.try(:name) => 'B',
+ description => 'C'
+ }
+ end
+
+ def self.search(terms)
+ self.pg_search(terms)
end
def self.by_geozone(geozone)
@@ -68,4 +87,51 @@ class SpendingProposal < ActiveRecord::Base
end
end
+ def unfeasible_email_pending?
+ unfeasible_email_sent_at.blank? && unfeasible? && valuation_finished?
+ end
+
+ def unfeasible?
+ feasible == false
+ end
+
+ def valuation_finished?
+ valuation_finished
+ end
+
+ def total_votes
+ cached_votes_up + physical_votes
+ end
+
+ def code
+ "#{created_at.strftime('%Y')}-#{id}" + (administrator.present? ? "-A#{administrator.id}" : "")
+ end
+
+ def send_unfeasible_email
+ Mailer.unfeasible_spending_proposal(self).deliver_later
+ update(unfeasible_email_sent_at: Time.now)
+ end
+
+ def reason_for_not_being_votable_by(user)
+ return :not_voting_allowed if Setting["feature.spending_proposal_features.voting_allowed"].blank?
+ return :not_logged_in unless user
+ return :not_verified unless user.can?(:vote, SpendingProposal)
+ return :unfeasible if unfeasible?
+ return :organization if user.organization?
+ end
+
+ def votable_by?(user)
+ reason_for_not_being_votable_by(user).blank?
+ end
+
+ def register_vote(user, vote_value)
+ if votable_by?(user)
+ vote_by(voter: user, vote: vote_value)
+ end
+ end
+
+ def set_responsible_name
+ self.responsible_name = author.try(:document_number) if author.try(:document_number).present?
+ end
+
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 3b58fb607..ea889ac57 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -26,7 +26,7 @@ class User < ActiveRecord::Base
belongs_to :geozone
validates :username, presence: true, if: :username_required?
- validates :username, uniqueness: true, if: :username_required?
+ validates :username, uniqueness: { scope: :registering_with_oauth }, if: :username_required?
validates :document_number, uniqueness: { scope: :document_type }, allow_nil: true
validate :validate_username_length
@@ -83,6 +83,11 @@ class User < ActiveRecord::Base
voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value }
end
+ def spending_proposal_votes(spending_proposals)
+ voted = votes.for_spending_proposals(spending_proposals)
+ voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value }
+ end
+
def comment_flags(comments)
comment_flags = flags.for_comments(comments)
comment_flags.each_with_object({}){ |f, h| h[f.flaggable_id] = true }
@@ -149,6 +154,7 @@ class User < ActiveRecord::Base
confirmed_phone: nil,
unconfirmed_phone: nil
)
+ self.identities.destroy_all
end
def erased?
@@ -177,11 +183,11 @@ class User < ActiveRecord::Base
end
def username_required?
- !organization? && !erased? && !registering_with_oauth
+ !organization? && !erased?
end
def email_required?
- !erased? && !registering_with_oauth
+ !erased?
end
def has_official_email?
@@ -210,12 +216,21 @@ class User < ActiveRecord::Base
end
def save_requiring_finish_signup
- self.update(registering_with_oauth: true)
+ begin
+ self.registering_with_oauth = true
+ self.save(validate: false)
+ # Devise puts unique constraints for the email the db, so we must detect & handle that
+ rescue ActiveRecord::RecordNotUnique
+ self.email = nil
+ self.save(validate: false)
+ end
+ true
end
- def save_requiring_finish_signup_without_email
- self.update(registering_with_oauth: true, email: nil)
+ def ability
+ @ability ||= Ability.new(self)
end
+ delegate :can?, :cannot?, to: :ability
private
def clean_document_number
diff --git a/app/models/verification/management/document.rb b/app/models/verification/management/document.rb
index ee9e5462d..fcbc19ca4 100644
--- a/app/models/verification/management/document.rb
+++ b/app/models/verification/management/document.rb
@@ -32,7 +32,7 @@ class Verification::Management::Document
end
def under_sixteen?(response)
- 16.years.ago < string_to_date(response.date_of_birth)
+ 16.years.ago.beginning_of_day < response.date_of_birth.beginning_of_day
end
def verified?
@@ -43,4 +43,4 @@ class Verification::Management::Document
user.update(verified_at: Time.now) if user?
end
-end
\ No newline at end of file
+end
diff --git a/app/models/verification/residence.rb b/app/models/verification/residence.rb
index 0c1ed5f9a..5756af2b3 100644
--- a/app/models/verification/residence.rb
+++ b/app/models/verification/residence.rb
@@ -31,6 +31,8 @@ class Verification::Residence
user.update(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.now)
end
@@ -75,6 +77,10 @@ class Verification::Residence
@census_api_response.district_code
end
+ def gender
+ @census_api_response.gender
+ end
+
private
def call_census_api
@@ -84,7 +90,7 @@ class Verification::Residence
def residency_valid?
@census_api_response.valid? &&
@census_api_response.postal_code == postal_code &&
- @census_api_response.date_of_birth == date_to_string(date_of_birth)
+ @census_api_response.date_of_birth == date_of_birth
end
def clean_document_number
diff --git a/app/models/verification/sms.rb b/app/models/verification/sms.rb
index ba484c4f3..1a013f1d8 100644
--- a/app/models/verification/sms.rb
+++ b/app/models/verification/sms.rb
@@ -4,14 +4,9 @@ class Verification::Sms
attr_accessor :user, :phone, :confirmation_code
validates_presence_of :phone
- validates :phone, length: { is: 9 }
- validate :spanish_phone
+ validates :phone, format: { with: /\A[\d \+]+\z/ }
validate :uniqness_phone
- def spanish_phone
- errors.add(:phone, :invalid) unless phone.start_with?('6', '7')
- end
-
def uniqness_phone
errors.add(:phone, :taken) if User.where(confirmed_phone: phone).any?
end
@@ -40,4 +35,4 @@ class Verification::Sms
def generate_confirmation_code
rand.to_s[2..5]
end
-end
\ No newline at end of file
+end
diff --git a/app/views/admin/shared/_spending_proposal_search.html.erb b/app/views/admin/shared/_spending_proposal_search.html.erb
new file mode 100644
index 000000000..c3e276ebe
--- /dev/null
+++ b/app/views/admin/shared/_spending_proposal_search.html.erb
@@ -0,0 +1,10 @@
+<%= form_for(SpendingProposal.new, url: url, as: :spending_proposal, method: :get) do |f| %>
+
+
+ <%= text_field_tag :search, "", placeholder: t("admin.shared.spending_proposal_search.placeholder") %>
+
+
+ <%= f.submit t("admin.shared.spending_proposal_search.button"), class: "button success expanded" %>
+
+
+<% end %>
\ No newline at end of file
diff --git a/app/views/admin/spending_proposals/edit.html.erb b/app/views/admin/spending_proposals/edit.html.erb
index 59180eb16..1fd86da59 100644
--- a/app/views/admin/spending_proposals/edit.html.erb
+++ b/app/views/admin/spending_proposals/edit.html.erb
@@ -2,43 +2,71 @@
<%= t("admin.spending_proposals.show.back") %>
<% end %>
-<%= render 'written_by_author' %>
-
-
-
<%= form_for @spending_proposal,
url: admin_spending_proposal_path(@spending_proposal) do |f| %>
<% SpendingProposal.filter_params(params).each do |filter_name, filter_value| %>
- <%= hidden_field_tag filter_name, filter_value %>
+ <%= hidden_field_tag filter_name, filter_value %>
<% end %>
- <%= f.select(:administrator_id,
- @admins.collect{ |a| [a.name_and_email, a.id ] },
- { include_blank: t("admin.spending_proposals.edit.undefined") },
- class: "small-12 medium-6") %>
+
+
+ <%= f.text_field :title, maxlength: SpendingProposal.title_max_length %>
+
- <%= f.label :tag_list, t("admin.spending_proposals.edit.tags") %>
-
- <%= f.text_field :tag_list, value: @spending_proposal.tag_list.to_s,
- label: false,
- placeholder: t("admin.spending_proposals.edit.tags_placeholder"),
- class: 'js-tag-list' %>
- <%= f.label :valuator_ids, t("admin.spending_proposals.edit.assigned_valuators") %>
+
<%= t("admin.spending_proposals.edit.classification") %>
- <%= f.collection_check_boxes :valuator_ids, @valuators, :id, :email do |b| %>
- <%= b.label(title: valuator_label(b.object)) { b.check_box + truncate(b.object.description_or_email, length: 60) } %>
- <% end %>
+
+
+
+ <%= f.select(:administrator_id,
+ @admins.collect{ |a| [a.name_and_email, a.id ] },
+ { include_blank: t("admin.spending_proposals.edit.undefined") }) %>
+
+
+
+
+ <%= f.label :tag_list, t("admin.spending_proposals.edit.tags") %>
+
+ <%= f.text_field :tag_list, value: @spending_proposal.tag_list.to_s,
+ label: false,
+ placeholder: t("admin.spending_proposals.edit.tags_placeholder"),
+ class: 'js-tag-list' %>
+
+
+
+ <%= f.label :valuator_ids, t("admin.spending_proposals.edit.assigned_valuators") %>
+
+ <%= f.collection_check_boxes :valuator_ids, @valuators, :id, :email do |b| %>
+ <%= b.label(title: valuator_label(b.object)) { b.check_box + truncate(b.object.description_or_email, length: 60) } %>
+ <% end %>
+
+
<%= f.submit(class: "button", value: t("admin.spending_proposals.edit.submit_button")) %>
-
<% end %>
diff --git a/app/views/admin/spending_proposals/index.html.erb b/app/views/admin/spending_proposals/index.html.erb
index f50dea628..1ad804c51 100644
--- a/app/views/admin/spending_proposals/index.html.erb
+++ b/app/views/admin/spending_proposals/index.html.erb
@@ -2,21 +2,30 @@
<%= form_tag admin_spending_proposals_path, method: :get, enforce_utf8: false do %>
-
+
<%= select_tag :administrator_id,
options_for_select(admin_select_options, params[:administrator_id]),
{ prompt: t("admin.spending_proposals.index.administrator_filter_all"),
label: false,
class: "js-submit-on-change" } %>
-
+
+
+ <%= select_tag :valuator_id,
+ options_for_select(valuator_select_options, params[:valuator_id]),
+ { prompt: t("admin.spending_proposals.index.valuator_filter_all"),
+ label: false,
+ class: "js-submit-on-change" } %>
+
+
+
<%= select_tag :geozone_id,
options_for_select(geozone_select_options.unshift([t("geozones.none"), "all"]), params[:geozone_id]),
{ prompt: t("admin.spending_proposals.index.geozone_filter_all"),
label: false,
class: "js-submit-on-change" } %>
-
+
<%= select_tag :tag_name,
options_for_select(spending_proposal_tags_select_options, params[:tag_name]),
{ prompt: t("admin.spending_proposals.index.tags_filter_all"),
diff --git a/app/views/admin/spending_proposals/show.html.erb b/app/views/admin/spending_proposals/show.html.erb
index bbcf4b4fc..64313e030 100644
--- a/app/views/admin/spending_proposals/show.html.erb
+++ b/app/views/admin/spending_proposals/show.html.erb
@@ -1,16 +1,16 @@
-<%= link_to admin_spending_proposals_path(SpendingProposal.filter_params(params)) do %>
+<%= link_to admin_spending_proposals_path(SpendingProposal.filter_params(params)), data: {no_turbolink: true} do %>
<%= t("admin.spending_proposals.show.back") %>
<% end %>
<%= render 'written_by_author' %>
-
<%= t("admin.spending_proposals.show.classification") %>
+<%= link_to t("admin.spending_proposals.show.edit"),
+ edit_admin_spending_proposal_path(@spending_proposal,
+ SpendingProposal.filter_params(params)) %>
-
- <%= link_to t("admin.spending_proposals.show.edit_classification"),
- edit_admin_spending_proposal_path(@spending_proposal,
- {anchor: 'form'}.merge(SpendingProposal.filter_params(params))) %>
-
+
+
+
<%= t("admin.spending_proposals.show.classification") %>
<%= t("admin.spending_proposals.show.assigned_admin") %>:
<%= @spending_proposal.administrator.try(:name_and_email) || t("admin.spending_proposals.show.undefined") %>
@@ -31,12 +31,19 @@
<% end %>
+
+ <%= link_to t("admin.spending_proposals.show.edit_classification"),
+ edit_admin_spending_proposal_path(@spending_proposal,
+ {anchor: 'classification'}.merge(SpendingProposal.filter_params(params))) %>
+
+
<%= t("admin.spending_proposals.show.dossier") %>
+<%= render 'valuation/spending_proposals/written_by_valuators' %>
+
<%= link_to t("admin.spending_proposals.show.edit_dossier"), edit_valuation_spending_proposal_path(@spending_proposal) %>
-<%= render 'valuation/spending_proposals/written_by_valuators' %>
diff --git a/app/views/admin/stats/show.html.erb b/app/views/admin/stats/show.html.erb
index 93b6be821..9a9ecbfa8 100644
--- a/app/views/admin/stats/show.html.erb
+++ b/app/views/admin/stats/show.html.erb
@@ -84,6 +84,13 @@
+
+
+
+ <%= t "admin.stats.show.summary.spending_proposals" %>
+ <%= number_with_delimiter(@spending_proposals) %>
+
+
@@ -97,6 +104,12 @@
<%= events_chart_tag event %>
<% end %>
+
+
+
<%= t "admin.stats.show.spending_proposals_title" %>
+ <%= spending_proposals_chart_tag id: "spending_proposals" %>
+
+
diff --git a/app/views/debates/show.html.erb b/app/views/debates/show.html.erb
index 93ca49862..cfce656a2 100644
--- a/app/views/debates/show.html.erb
+++ b/app/views/debates/show.html.erb
@@ -54,8 +54,14 @@
<%= t("debates.show.share") %>
- <%= social_share_button_tag(@debate.title) %>
-
+
+ <%= social_share_button_tag("#{@debate.title} #{setting['twitter_hashtag']}") %>
+ <% if browser.device.mobile? %>
+
+
+
+ <% end %>
+
<% end %>
diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb
index d791fe7b9..921532119 100644
--- a/app/views/layouts/_header.html.erb
+++ b/app/views/layouts/_header.html.erb
@@ -44,7 +44,7 @@
-
+
<%= render "shared/subnavigation" %>
diff --git a/app/views/layouts/management.html.erb b/app/views/layouts/management.html.erb
index 75240f25d..6561573ee 100644
--- a/app/views/layouts/management.html.erb
+++ b/app/views/layouts/management.html.erb
@@ -16,55 +16,65 @@
-
+
<%= render 'shared/locale_switcher' %>
-
+
-
-