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' %> - -

<%= t("admin.spending_proposals.edit.classification") %>

- <%= 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") %> -
- <% @tags.each do |tag| %> - <%= tag.name %> - <% end %> +
+ <%= f.cktext_area :description, maxlength: SpendingProposal.description_max_length, ckeditor: { language: I18n.locale } %> +
+ +
+ <%= f.text_field :external_url %> +
+ +
+ <%= f.select :geozone_id, geozone_select_options, include_blank: t("geozones.none") %> +
+ +
+ <%= f.text_field :association_name, placeholder: t("spending_proposals.form.association_name") %> +
- <%= 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") %> +
+ <% @tags.each do |tag| %> + <%= tag.name %> + <% end %> +
+ <%= 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 @@

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