diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 443782795..8258cf9c9 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -461,7 +461,7 @@ Style/Lambda: # Offense count: 1 # Cop supports --auto-correct. -Style/MethodCallParentheses: +Style/MethodCallWithoutArgsParentheses: Exclude: - 'app/controllers/management/document_verifications_controller.rb' diff --git a/Gemfile b/Gemfile index a5ec32008..8dc4cc5ff 100644 --- a/Gemfile +++ b/Gemfile @@ -64,6 +64,7 @@ gem 'tolk', '~> 2.0.0' # Web interface for translations gem 'browser' gem 'turnout', '~> 2.4.0' gem 'redcarpet', '~> 3.4.0' +gem 'rubyzip', '~> 1.2.0' gem 'paperclip' gem 'rails-assets-markdown-it', source: 'https://rails-assets.org' diff --git a/Gemfile.lock b/Gemfile.lock index b1540cfe1..77386e543 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -371,6 +371,7 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.8.1) + rubyzip (1.2.1) rvm1-capistrano3 (1.4.0) capistrano (~> 3.0) sshkit (>= 1.2) @@ -530,6 +531,7 @@ DEPENDENCIES rollbar (~> 2.14.1) rspec-rails (~> 3.6) rubocop (~> 0.48.1) + rubyzip (~> 1.2.0) rvm1-capistrano3 sass-rails (~> 5.0, >= 5.0.4) savon @@ -547,4 +549,4 @@ DEPENDENCIES whenever BUNDLED WITH - 1.14.6 + 1.15.0 diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index e9f6c23ae..06d3e1983 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -97,6 +97,10 @@ body.admin { } } + td.break { + word-break: break-word; + } + &.fixed { table-layout: fixed; } diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss index e55d83f12..d232ae0d3 100644 --- a/app/assets/stylesheets/layout.scss +++ b/app/assets/stylesheets/layout.scss @@ -1141,10 +1141,8 @@ img.avatar, img.admin-avatar, img.moderator-avatar, img.initialjs-avatar { // 10. Officials levels // -------------------- -[class^="level-"] { - color: black; -} - +.level-1, .level-2, .level-3, +.level-4, .level-5, .is-author, .is-association { color: black; } diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index 5658a4e6b..4c0925659 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -206,9 +206,9 @@ .participation-not-allowed { background: $warning-bg; color: $color-warning; - height: 100%; left: 0; line-height: $line-height; + min-height: 100%; padding: $line-height $line-height/2; position: absolute; text-align: center; diff --git a/app/controllers/admin/administrators_controller.rb b/app/controllers/admin/administrators_controller.rb new file mode 100644 index 000000000..938a7570d --- /dev/null +++ b/app/controllers/admin/administrators_controller.rb @@ -0,0 +1,37 @@ +class Admin::AdministratorsController < Admin::BaseController + load_and_authorize_resource + + def index + @administrators = @administrators.page(params[:page]) + end + + def search + @user = User.find_by(email: params[:email]) + + respond_to do |format| + if @user + @administrator = Administrator.find_or_initialize_by(user: @user) + format.js + else + format.js { render "user_not_found" } + end + end + end + + def create + @administrator.user_id = params[:user_id] + @administrator.save + + redirect_to admin_administrators_path + end + + def destroy + if current_user.id == @administrator.user_id + flash[:error] = I18n.t("admin.administrators.administrator.restricted_removal") + else + @administrator.destroy + end + + redirect_to admin_administrators_path + end +end diff --git a/app/controllers/admin/newsletters_controller.rb b/app/controllers/admin/newsletters_controller.rb new file mode 100644 index 000000000..60895dc0c --- /dev/null +++ b/app/controllers/admin/newsletters_controller.rb @@ -0,0 +1,12 @@ +class Admin::NewslettersController < Admin::BaseController + + def index + end + + def users + zip = NewsletterZip.new('emails') + zip.create + send_file(File.join(zip.path), type: 'application/zip') + end + +end \ No newline at end of file diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb index 3f0d4db4b..8ca439a1c 100644 --- a/app/helpers/admin_helper.rb +++ b/app/helpers/admin_helper.rb @@ -29,7 +29,7 @@ module AdminHelper end def menu_profiles? - ["organizations", "officials", "moderators", "valuators", "managers"].include? controller_name + ["administrators", "organizations", "officials", "moderators", "valuators", "managers"].include? controller_name end def menu_banners? diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index f09333ff0..af44066be 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -34,6 +34,7 @@ module Abilities can :comment_as_administrator, [Debate, Comment, Proposal, Poll::Question, Budget::Investment, Legislation::Question, Legislation::Annotation] + can [:search, :create, :index, :destroy], ::Administrator can [:search, :create, :index, :destroy], ::Moderator can [:search, :create, :index, :summary], ::Valuator can [:search, :create, :index, :destroy], ::Manager diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index 56a50deec..7aabf7093 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -145,7 +145,7 @@ class Budget return :not_selected unless selected? return :no_ballots_allowed unless budget.balloting? return :different_heading_assigned unless ballot.valid_heading?(heading) - return :not_enough_money if ballot.present? && !enough_money?(ballot) + return :not_enough_money_html if ballot.present? && !enough_money?(ballot) end def permission_problem(user) diff --git a/app/models/poll/voter.rb b/app/models/poll/voter.rb index 8fe612151..b05839501 100644 --- a/app/models/poll/voter.rb +++ b/app/models/poll/voter.rb @@ -50,10 +50,10 @@ class Poll if dob.blank? nil else - now = Time.now.utc.to_date + now = Time.current.to_date now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1) end end end -end \ No newline at end of file +end diff --git a/app/models/user.rb b/app/models/user.rb index 89bc4ce87..e55bd00e5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -53,6 +53,7 @@ class User < ActiveRecord::Base scope :moderators, -> { joins(:moderator) } scope :organizations, -> { joins(:organization) } scope :officials, -> { where("official_level > 0") } + scope :newsletter, -> { where(newsletter: true) } scope :for_render, -> { includes(:organization) } scope :by_document, -> (document_type, document_number) { where(document_type: document_type, document_number: document_number) } scope :email_digest, -> { where(email_digest: true) } diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index 56543ba28..5d454fcd8 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -95,6 +95,10 @@ <%= t("admin.menu.title_profiles") %> diff --git a/app/views/admin/administrators/_administrator.html.erb b/app/views/admin/administrators/_administrator.html.erb new file mode 100644 index 000000000..d948e1eed --- /dev/null +++ b/app/views/admin/administrators/_administrator.html.erb @@ -0,0 +1,26 @@ +
+ + + + + + + + +
+ <%= administrator.name %> + + <%= administrator.email %> + + <% if administrator.persisted? %> + <%= link_to t('admin.administrators.administrator.delete'), + admin_administrator_path(administrator), + method: :delete, + class: "button hollow alert" %> + <% else %> + <%= link_to t('admin.administrators.administrator.add'),{ controller: "admin/administrators", action: :create, user_id: administrator.user_id }, + method: :post, + class: "button success" %> + <% end %> +
+
diff --git a/app/views/admin/administrators/index.html.erb b/app/views/admin/administrators/index.html.erb new file mode 100644 index 000000000..1cfbf5079 --- /dev/null +++ b/app/views/admin/administrators/index.html.erb @@ -0,0 +1,46 @@ +

<%= t("admin.administrators.index.title") %>

+ +
+ <%= form_tag search_admin_administrators_path, method: :get, remote: true do %> +
+ <%= text_field_tag :email, '', placeholder: t('admin.administrators.search.email_placeholder') %> +
+
+ <%= submit_tag t('admin.administrators.search.search'), class: 'button' %> +
+ <% end %> +
+ +
+ +

<%= page_entries_info @administrators %>

+ + + <% @administrators.each do |administrator| %> + + + + + + <% end %> +
+ <%= administrator.name %> + + <%= administrator.email %> + + <% if administrator.persisted? %> + <%= link_to t('admin.administrators.administrator.delete'), + admin_administrator_path(administrator), + method: :delete, + class: "button hollow alert" + %> + <% else %> + <%= link_to t('admin.administrators.administrator.add'), + { controller: "admin/administrators", action: :create, + user_id: administrator.user_id }, + method: :post, + class: "button success" %> + <% end %> +
+ +<%= paginate @administrators %> diff --git a/app/views/admin/administrators/search.js.erb b/app/views/admin/administrators/search.js.erb new file mode 100644 index 000000000..1c30d9595 --- /dev/null +++ b/app/views/admin/administrators/search.js.erb @@ -0,0 +1 @@ +$("#search-result").html("<%= j render 'administrator', administrator: @administrator %>"); diff --git a/app/views/admin/administrators/user_not_found.js.erb b/app/views/admin/administrators/user_not_found.js.erb new file mode 100644 index 000000000..ba707fc9d --- /dev/null +++ b/app/views/admin/administrators/user_not_found.js.erb @@ -0,0 +1 @@ +$("#search-result").html("
<%= j t('admin.administrators.search.user_not_found') %>
"); diff --git a/app/views/admin/geozones/index.html.erb b/app/views/admin/geozones/index.html.erb index 5a524e46e..555d00ebe 100644 --- a/app/views/admin/geozones/index.html.erb +++ b/app/views/admin/geozones/index.html.erb @@ -20,7 +20,7 @@ <%= geozone.name %> <%= geozone.external_code %> <%= geozone.census_code %> - <%= geozone.html_map_coordinates %> + <%= geozone.html_map_coordinates %> <%= link_to t("admin.geozones.index.edit"), edit_admin_geozone_path(geozone), class: 'edit-banner button hollow' %> diff --git a/app/views/admin/newsletters/index.html.erb b/app/views/admin/newsletters/index.html.erb new file mode 100644 index 000000000..b7b43c6fc --- /dev/null +++ b/app/views/admin/newsletters/index.html.erb @@ -0,0 +1,3 @@ +

<%= t("admin.newsletters.index.title") %>

+ +<%= link_to t("admin.newsletters.index.button"), users_admin_newsletters_path, class: "button" %> \ No newline at end of file diff --git a/app/views/admin/stats/show.html.erb b/app/views/admin/stats/show.html.erb index 137f3ce81..5df7b5df2 100644 --- a/app/views/admin/stats/show.html.erb +++ b/app/views/admin/stats/show.html.erb @@ -20,29 +20,29 @@

- <%= t "admin.stats.show.summary.debates" %>
+ <%= t "admin.stats.show.summary.debates" %>
<%= number_with_delimiter(@debates) %>

- <%= t "admin.stats.show.summary.proposals" %>
+ <%= t "admin.stats.show.summary.proposals" %>
<%= number_with_delimiter(@proposals) %>

- <%= t "admin.stats.show.summary.comments" %>
+ <%= t "admin.stats.show.summary.comments" %>
<%= number_with_delimiter(@comments) %>

<% if feature?(:budgets) %>

- <%= t "admin.stats.show.summary.budgets" %>
+ <%= t "admin.stats.show.summary.budgets" %>
<%= number_with_delimiter(@budgets) %>

<% end %> @@ -50,56 +50,56 @@

- <%= t "admin.stats.show.summary.debate_votes" %>
+ <%= t "admin.stats.show.summary.debate_votes" %>
<%= number_with_delimiter(@debate_votes) %>

- <%= t "admin.stats.show.summary.comment_votes" %>
+ <%= t "admin.stats.show.summary.comment_votes" %>
<%= number_with_delimiter(@comment_votes) %>

- <%= t "admin.stats.show.summary.votes" %>
+ <%= t "admin.stats.show.summary.votes" %>
<%= number_with_delimiter(@votes) %>

- <%= t "admin.stats.show.summary.user_level_two" %>
+ <%= t "admin.stats.show.summary.user_level_two" %>
<%= number_with_delimiter(@user_level_two) %>

- <%= t "admin.stats.show.summary.user_level_three" %>
+ <%= t "admin.stats.show.summary.user_level_three" %>
<%= number_with_delimiter(@user_level_three) %>

- <%= t "admin.stats.show.summary.verified_users_who_didnt_vote_proposals" %>
+ <%= t "admin.stats.show.summary.verified_users_who_didnt_vote_proposals" %>
<%=number_with_delimiter(@user_ids_who_didnt_vote_proposals)%> @@ -109,7 +109,7 @@ <% if feature?(:spending_proposals) %>

diff --git a/app/views/budgets/investments/_ballot.html.erb b/app/views/budgets/investments/_ballot.html.erb index 0ad43ab43..605caebc7 100644 --- a/app/views/budgets/investments/_ballot.html.erb +++ b/app/views/budgets/investments/_ballot.html.erb @@ -50,8 +50,9 @@ signup: link_to(t("votes.signup"), new_user_registration_path), my_heading: link_to(investment.heading.name, budget_investments_path(budget_id: investment.budget_id, - heading_id: investment.heading_id)) - ).html_safe %> + heading_id: investment.heading_id)), + change_ballot: link_to(t("budgets.ballots.reasons_for_not_balloting.change_ballot"), + budget_ballot_path(@budget))).html_safe %>

diff --git a/app/views/budgets/investments/_header.html.erb b/app/views/budgets/investments/_header.html.erb index 2ed01d989..42e133d5a 100644 --- a/app/views/budgets/investments/_header.html.erb +++ b/app/views/budgets/investments/_header.html.erb @@ -42,6 +42,12 @@ @assigned_heading.name, budget_investments_path(@budget, heading_id: @assigned_heading.try(:id))) ) %> +
+ + <%= t("budgets.investments.header.change_ballot", + check_ballot: link_to(t("budgets.investments.header.check_ballot_link"), + budget_ballot_path(@budget))).html_safe %> +
<% end %> diff --git a/app/views/budgets/investments/_investment_show.html.erb b/app/views/budgets/investments/_investment_show.html.erb index f9a371585..a6a4fe8cb 100644 --- a/app/views/budgets/investments/_investment_show.html.erb +++ b/app/views/budgets/investments/_investment_show.html.erb @@ -2,7 +2,7 @@
- <%= back_link_to budget_investments_path(investment.budget) %> + <%= back_link_to budget_investments_path(investment.budget, heading_id: investment.heading) %>

<%= investment.title %>

diff --git a/app/views/budgets/investments/_sidebar.html.erb b/app/views/budgets/investments/_sidebar.html.erb index 06d3b9d81..4ae9104d5 100644 --- a/app/views/budgets/investments/_sidebar.html.erb +++ b/app/views/budgets/investments/_sidebar.html.erb @@ -41,11 +41,19 @@

<% elsif @assigned_heading.present? %> -

<%= t("budgets.investments.index.sidebar.different_heading_assigned_html", +

+ <%= t("budgets.investments.index.sidebar.different_heading_assigned_html", heading_link: link_to( @assigned_heading.name, budget_investments_path(@budget, heading_id: @assigned_heading.try(:id))) - ) %>

+ ) %> +
+ + <%= t("budgets.investments.index.sidebar.change_ballot", + check_ballot: link_to(t("budgets.investments.index.sidebar.check_ballot_link"), + budget_ballot_path(@budget))).html_safe %> + +

<% else %>

<%= t("budgets.investments.index.sidebar.zero") %>

<% end %> diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb index 2352c5181..c618de6c4 100644 --- a/app/views/layouts/admin.html.erb +++ b/app/views/layouts/admin.html.erb @@ -31,7 +31,7 @@ <%= render 'layouts/admin_header' %>
-
+
diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index 04efd6464..49cb23ff0 100755 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -340,8 +340,10 @@ en: hidden_debates: Hidden debates hidden_proposals: Hidden proposals hidden_users: Hidden users + administrators: Administrators managers: Managers moderators: Moderators + newsletter: Newsletters valuators: Valuators poll_officers: Poll officers polls: Polls @@ -364,6 +366,17 @@ en: title_banners: Banners title_site_customization: Site customization legislation: Collaborative Legislation + administrators: + index: + title: Administrators + administrator: + add: Add + delete: Delete + restricted_removal: "Sorry, you can't remove yourself from the administrators" + search: + email_placeholder: Search user by email + search: Search + user_not_found: User not found moderators: index: title: Moderators @@ -374,6 +387,10 @@ en: email_placeholder: Search user by email search: Search user_not_found: User not found + newsletters: + index: + title: Newsletters + button: Download zip with users list valuators: index: title: Valuators @@ -629,7 +646,7 @@ en: placeholder: Search spending proposals by title or description user_search: button: Search - placeholder: Search user by name or email' + placeholder: Search user by name or email search_results: "Search results" no_search_results: "No results found." spending_proposals: diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index bf2300d36..64e899312 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -318,6 +318,17 @@ es: comments_count: Número de comentarios question_option_fields: remove_option: Eliminar + administrators: + index: + title: Administradores + administrator: + add: Añadir como Administrador + delete: Borrar + restricted_removal: "Lo sentimos, no puedes eliminarte a ti mismo de la lista" + search: + email_placeholder: Buscar usuario por email + search: Buscar + user_not_found: Usuario no encontrado managers: index: title: Gestores @@ -341,7 +352,9 @@ es: hidden_proposals: Propuestas ocultas hidden_users: Usuarios bloqueados managers: Gestores + administrators: Administradores moderators: Moderadores + newsletter: Envío de Newsletters valuators: Evaluadores poll_officers: Presidentes de mesa polls: Votaciones @@ -374,6 +387,10 @@ es: email_placeholder: Buscar usuario por email search: Buscar user_not_found: Usuario no encontrado + newsletters: + index: + title: Envío de newsletters + button: Descargar zip con lista de usuarios valuators: index: title: Evaluadores diff --git a/config/locales/budgets.en.yml b/config/locales/budgets.en.yml index 570f9da35..6fba08534 100644 --- a/config/locales/budgets.en.yml +++ b/config/locales/budgets.en.yml @@ -17,9 +17,10 @@ en: not_verified: Only verified users can vote on investments; %{verify_account}. organization: Organizations are not permitted to vote not_selected: Unselected investment projects can not be supported - not_enough_money: "Price is higher than the available amount left." + not_enough_money_html: "You have already assigned the available budget.
Remember you can %{change_ballot} at any time" no_ballots_allowed: Selecting phase is closed different_heading_assigned: You have already voted a different heading + change_ballot: change your votes groups: show: title: Select an option @@ -64,6 +65,8 @@ en: voted_info: You can %{link} at any time until the close of this phase. No need to spend all the money available. voted_info_link: change your vote different_heading_assigned_html: "You have active votes in another heading: %{heading_link}" + change_ballot: "If your change your mind you can remove your votes in %{check_ballot} and start again." + check_ballot_link: "check my ballot" zero: You have not voted any investment project in this group. verified_only: "To create a new budget investment %{verify}." verify_account: "verify your account" @@ -106,6 +109,8 @@ en: header: check_ballot: Check my ballot different_heading_assigned_html: "You have active votes in another heading: %{heading_link}" + change_ballot: "If your change your mind you can remove your votes in %{check_ballot} and start again." + check_ballot_link: "check my ballot" progress_bar: available: "Available: " show: diff --git a/config/locales/budgets.es.yml b/config/locales/budgets.es.yml index a8e7fa5fa..1cb308472 100644 --- a/config/locales/budgets.es.yml +++ b/config/locales/budgets.es.yml @@ -17,9 +17,10 @@ es: not_verified: Las propuestas de inversión sólo pueden ser apoyadas por usuarios verificados, %{verify_account}. organization: Las organizaciones no pueden votar. not_selected: No se pueden votar propuestas inviables. - not_enough_money: No hay fondos suficientes. + not_enough_money_html: "Ya has asignado el presupuesto disponible.
Recuerda que puedes %{change_ballot} en cualquier momento" no_ballots_allowed: El periodo de votación está cerrado. different_heading_assigned: Ya votaste en una sección distinta del presupuesto. + change_ballot: cambiar tus votos groups: show: title: Selecciona una opción @@ -64,6 +65,8 @@ es: voted_info: Puedes %{link} en cualquier momento hasta el cierre de esta fase. No hace falta que gastes todo el dinero disponible. voted_info_link: cambiar tus votos different_heading_assigned_html: "Ya apoyaste propuestas de otra sección del presupuesto: %{heading_link}" + change_ballot: "Si cambias de opinión puedes borrar tus votos en %{check_ballot} y volver a empezar." + check_ballot_link: "revisar mis votos" zero: "Todavía no has votado ninguna propuesta de inversión en este ámbito del presupuesto." verified_only: "Para crear una nueva propuesta de inversión %{verify}." verify_account: "verifica tu cuenta" @@ -106,6 +109,8 @@ es: header: check_ballot: Revisar mis votos different_heading_assigned_html: "Ya apoyaste propuestas de otra sección del presupuesto: %{heading_link}" + change_ballot: "Si cambias de opinión puedes borrar tus votos en %{check_ballot} y volver a empezar." + check_ballot_link: "revisar mis votos" progress_bar: available: "Disponible: " show: diff --git a/config/routes.rb b/config/routes.rb index a7d3affdd..e5480a136 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -233,6 +233,10 @@ Rails.application.routes.draw do get :search, on: :collection end + resources :administrators, only: [:index, :create, :destroy] do + get :search, on: :collection + end + scope module: :poll do resources :polls do get :search_questions, on: :member @@ -265,6 +269,9 @@ Rails.application.routes.draw do end resource :activity, controller: :activity, only: :show + resources :newsletters, only: :index do + get :users, on: :collection + end resource :stats, only: :show do get :proposal_notifications, on: :collection get :direct_messages, on: :collection diff --git a/db/dev_seeds.rb b/db/dev_seeds.rb index 2753fe85f..a84f58219 100644 --- a/db/dev_seeds.rb +++ b/db/dev_seeds.rb @@ -408,7 +408,7 @@ tags = Faker::Lorem.words(10) title: Faker::Lorem.sentence(3).truncate(60), external_url: Faker::Internet.url, description: "

#{Faker::Lorem.paragraphs.join('

')}

", - created_at: rand((Time.now - 1.week) .. Time.now), + created_at: rand((Time.current - 1.week) .. Time.current), feasibility: %w{undecided unfeasible feasible feasible feasible feasible}.sample, unfeasibility_explanation: Faker::Lorem.paragraph, valuation_finished: [false, true].sample, @@ -437,7 +437,7 @@ budget = Budget.where(phase: "finished").last title: Faker::Lorem.sentence(3).truncate(60), external_url: Faker::Internet.url, description: "

#{Faker::Lorem.paragraphs.join('

')}

", - created_at: rand((Time.now - 1.week) .. Time.now), + created_at: rand((Time.current - 1.week) .. Time.current), feasibility: "feasible", valuation_finished: true, selected: true, diff --git a/doc/en/dev_test_setup_osx.md b/doc/en/dev_test_setup_osx.md index 67845510f..b579ce4a6 100644 --- a/doc/en/dev_test_setup_osx.md +++ b/doc/en/dev_test_setup_osx.md @@ -98,6 +98,12 @@ brew install ghostscript brew install phantomjs ``` +## Imagemagick + +``` +brew install imagemagick +``` + ## Cloning the repository Now that we have all the dependencies installed we can download the repository: diff --git a/doc/es/dev_test_setup.md b/doc/es/dev_test_setup.md index 30f5d12d3..cda2613e7 100644 --- a/doc/es/dev_test_setup.md +++ b/doc/es/dev_test_setup.md @@ -2,6 +2,8 @@ ## Linux +Consultar [aqui](dev_test_setup_linux.md) + ## Mac OS X Consultar [aquí](dev_test_setup_osx.md) diff --git a/doc/es/dev_test_setup_linux.md b/doc/es/dev_test_setup_linux.md new file mode 100644 index 000000000..170871913 --- /dev/null +++ b/doc/es/dev_test_setup_linux.md @@ -0,0 +1,146 @@ +# Configuración para los entornos de desarrollo y pruebas (GNU/Linux) + +## Git + +Git es mantenido oficialmente en Debian/Ubuntu: + +``` +sudo apt-get install git +``` + +## Ruby + +Las versiones de Ruby versions empaquetadas en repositorios oficiales no son aptas para trabajar con consul (al menos Debian 7 y 8), así que debemos instalar manualmente. + +El método recomendado es via rvm: + +(sólo la opción multiusuario instala todas las dependencias automáticamente, al usar 'sudo'.) + +###como usuario local: + +``` +curl -L https://get.rvm.io | bash -s stable +``` +###para todos los usuarios del sistema: + +``` +curl -L https://get.rvm.io | sudo bash -s stable +``` + +añadismos nuestro usuario al grupo de rvm + +``` +sudo usermod -a -G rvm +``` + +y finalmente, añadimos el script rvm a nuestro bash (~/.bashrc) (este paso sólo es necesario si no puedes ejecutar el comando rvm) + +``` +[[ -s /usr/local/rvm/scripts/rvm ]] && source /usr/local/rvm/scripts/rvm +``` + +con todo esto, deberías poder instalar la versión de ruby con rvm, por ejemplo la 2.3.2: + +``` +sudo rvm install 2.3.2 +``` + +## Bundler + +usando + +``` +gem install bundler +``` + +hay varios métodos alternativos [aquí](https://rvm.io/integration/bundler) que podrían ser mejores como: + +``` +gem install rubygems-bundler +``` + +## PostgreSQL (>=9.4) + +La versión 9.4 de PostgreSQL no es oficial en Debian 7 (wheezy), pero en Debian 8 parece ser mantenida oficialmente. + +Así que debemos añadir el respositorio oficial de postgresql a apt, por ejemplo creando el fichero */etc/apt/sources.list.d/pgdg.list* con: + +``` +deb http://apt.postgresql.org/pub/repos/apt/ wheezy-pgdg main +``` + +después deberás descargar la key e instalarla: + +``` +wget https://www.postgresql.org/media/keys/ACCC4CF8.asc +apt-key add ACCC4CF8.asc +``` + +y finalmente instalar postgresql + +``` +apt-get update +apt-get install postgresql-9.4 +``` + +## Ghostscript + +``` +apt-get install ghostscript +``` + +## Clonar el repositorio + +Ahora que ya tenemos todas las dependencias instalado podemos bajarnos el proyecto: + +``` +git clone https://github.com/consul/consul.git +cd consul +bundle install +cp config/database.yml.example config/database.yml +cp config/secrets.yml.example config/secrets.yml +``` + +Ahora copia en `database.yml` el usuario y la contraseña que pusiste para *consul*. Cuando ya lo hayas hecho: + +``` +rake db:create +rake db:setup +rake db:dev_seed +RAILS_ENV=test bin/rake db:setup +``` + +Para ejecutar los tests: + +``` +bundle exec rspec +``` + +Quizás necesites crear un rol de superusuario en postgresql, y completar en el fichero*/config/database.yml* los campos 'user:' y 'password:'. + +Además, parece que postgresql usa un socket unix por defecto para las comunicaciones en local. Si te encuentras este problema creando la base de datos, cambia en */config/database.yml* la linea: + +``` +host: localhost +``` + +por: + +``` +host: /var/run/postgresql +``` + +Tras esto en el terminal ejecutaremos: + +``` +rake db:create +rake db:setup +rake db:dev_seed +RAILS_ENV=test bin/rake db:setup +``` + +Y por último para comprobar que todo esta bien, lanza los tests: + +``` +bundle exec rspec +``` diff --git a/doc/es/dev_test_setup_osx.md b/doc/es/dev_test_setup_osx.md index 01ad29a2b..5fb251a0c 100644 --- a/doc/es/dev_test_setup_osx.md +++ b/doc/es/dev_test_setup_osx.md @@ -100,6 +100,12 @@ brew install ghostscript brew install phantomjs ``` +## Imagemagick + +``` +brew install imagemagick +``` + ## Clonar el repositorio Ahora que ya tenemos todas las dependencias instalado podemos bajarnos el proyecto: diff --git a/lib/age.rb b/lib/age.rb index c6ae6fe18..f0ca9abe0 100644 --- a/lib/age.rb +++ b/lib/age.rb @@ -1,5 +1,5 @@ module Age - def self.in_years(dob, now = Time.now.utc.to_date) + def self.in_years(dob, now = Time.current.to_date) return nil unless dob.present? # reference: http://stackoverflow.com/questions/819263/get-persons-age-in-ruby#comment21200772_819263 now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1) diff --git a/lib/newsletter_zip.rb b/lib/newsletter_zip.rb new file mode 100644 index 000000000..dead7cf82 --- /dev/null +++ b/lib/newsletter_zip.rb @@ -0,0 +1,25 @@ +require 'zip' +class NewsletterZip + attr_accessor :filename + + def initialize(filename) + @filename = filename + end + + def emails + User.newsletter.pluck(:email).join("\n") + end + + def path + Rails.root + "/tmp/#{filename}.zip" + end + + def create + Zip::File.open(path, Zip::File::CREATE) do |zipfile| + zipfile.get_output_stream("#{filename}.txt") do |file| + file.write emails + end + end + end + +end \ No newline at end of file diff --git a/spec/factories.rb b/spec/factories.rb index 8893879dd..af1120089 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -388,7 +388,7 @@ FactoryGirl.define do end factory :poll do - sequence(:name) { |n| "Poll #{n}" } + sequence(:name) { |n| "Poll #{SecureRandom.hex}" } starts_at { 1.month.ago } ends_at { 1.month.from_now } diff --git a/spec/features/admin/administrators_spec.rb b/spec/features/admin/administrators_spec.rb new file mode 100644 index 000000000..bef57b75d --- /dev/null +++ b/spec/features/admin/administrators_spec.rb @@ -0,0 +1,45 @@ +require 'rails_helper' + +feature 'Admin administrators' do + background do + @admin = create(:administrator) + @user = create(:user, username: 'Jose Luis Balbin') + @administrator = create(:administrator) + login_as(@admin.user) + visit admin_administrators_path + end + + scenario 'Index' do + expect(page).to have_content @administrator.name + expect(page).to have_content @administrator.email + expect(page).to_not have_content @user.name + end + + scenario 'Create Administrator', :js do + fill_in 'email', with: @user.email + click_button 'Search' + + expect(page).to have_content @user.name + click_link 'Add' + within("#administrators") do + expect(page).to have_content @user.name + end + end + + scenario 'Delete Administrator' do + find(:xpath, "//tr[contains(.,'#{@administrator.name}')]/td/a", text: 'Delete').click + + within("#administrators") do + expect(page).to_not have_content @administrator.name + end + end + + scenario 'Delete Administrator when its the current user' do + find(:xpath, "//tr[contains(.,'#{@admin.name}')]/td/a", text: 'Delete').click + + within("#error") do + expect(page).to have_content I18n.t("admin.administrators.administrator.restricted_removal") + end + end +end + diff --git a/spec/features/admin/newsletters_spec.rb b/spec/features/admin/newsletters_spec.rb new file mode 100644 index 000000000..fccb63dc8 --- /dev/null +++ b/spec/features/admin/newsletters_spec.rb @@ -0,0 +1,26 @@ +require 'rails_helper' + +feature 'Admin newsletters emails' do + + let(:download_button_text) { 'Download zip with users list' } + + background do + @admin = create(:administrator) + @newsletter_user = create(:user, newsletter: true) + @non_newsletter_user = create(:user, newsletter: false) + login_as(@admin.user) + visit admin_newsletters_path + end + + scenario 'Index' do + expect(page).to have_content download_button_text + end + + scenario 'Download newsletter email zip' do + click_link download_button_text + downloaded_file_content = Zip::InputStream.open(StringIO.new(page.body)).get_next_entry.get_input_stream {|is| is.read } + expect(downloaded_file_content).to include(@admin.user.email, @newsletter_user.email) + expect(downloaded_file_content).not_to include(@non_newsletter_user.email) + end +end + diff --git a/spec/features/budgets/ballots_spec.rb b/spec/features/budgets/ballots_spec.rb index b8f40acf0..35e74a9a6 100644 --- a/spec/features/budgets/ballots_spec.rb +++ b/spec/features/budgets/ballots_spec.rb @@ -536,7 +536,7 @@ feature 'Ballots' do within("#budget_investment_#{bi2.id}") do find("div.ballot").hover - expect(page).to have_content('Price is higher than the available amount left') + expect(page).to have_content('You have already assigned the available budget') expect(page).to have_selector('.in-favor a', visible: false) end end @@ -550,7 +550,7 @@ feature 'Ballots' do within("#budget_investment_#{bi1.id}") do find("div.ballot").hover - expect(page).to_not have_content('Price is higher than the available amount left') + expect(page).to_not have_content('You have already assigned the available budget') expect(page).to have_selector('.in-favor a', visible: true) end @@ -558,7 +558,7 @@ feature 'Ballots' do within("#budget_investment_#{bi2.id}") do find("div.ballot").trigger("mouseover") - expect(page).to have_content('Price is higher than the available amount left') + expect(page).to have_content('You have already assigned the available budget') expect(page).to have_selector('.in-favor a', visible: false) end @@ -576,7 +576,7 @@ feature 'Ballots' do within("#budget_investment_#{bi2.id}") do find("div.ballot").hover - expect(page).to have_content('Price is higher than the available amount left') + expect(page).to have_content('You have already assigned the available budget') expect(page).to have_selector('.in-favor a', visible: false) end @@ -587,7 +587,7 @@ feature 'Ballots' do within("#budget_investment_#{bi2.id}") do find("div.ballot").hover - expect(page).to_not have_content('Price is higher than the available amount left') + expect(page).to_not have_content('You have already assigned the available budget') expect(page).to have_selector('.in-favor a', visible: true) end end @@ -604,7 +604,7 @@ feature 'Ballots' do within("#budget_investment_#{bi2.id}") do find("div.ballot").hover - expect(page).to have_content('Price is higher than the available amount left') + expect(page).to have_content('You have already assigned the available budget') expect(page).to have_selector('.in-favor a', visible: false) end @@ -616,7 +616,7 @@ feature 'Ballots' do within("#budget_investment_#{bi2.id}") do find("div.ballot").hover - expect(page).to_not have_content('Price is higher than the available amount left') + expect(page).to_not have_content('You have already assigned the available budget') expect(page).to have_selector('.in-favor a', visible: true) end end diff --git a/spec/features/budgets/investments_spec.rb b/spec/features/budgets/investments_spec.rb index a0f65a012..ef84cc6fc 100644 --- a/spec/features/budgets/investments_spec.rb +++ b/spec/features/budgets/investments_spec.rb @@ -278,6 +278,13 @@ feature 'Budget Investments' do end end + scenario "Show back link contains heading id" do + investment = create(:budget_investment, heading: heading) + visit budget_investment_path(budget, investment) + + expect(page).to have_link "Go back", href: budget_investments_path(budget, heading_id: investment.heading) + end + context "Show (feasible budget investment)" do let(:investment) { create(:budget_investment, :feasible, diff --git a/spec/features/budgets/votes_spec.rb b/spec/features/budgets/votes_spec.rb index 497046891..95f040bb3 100644 --- a/spec/features/budgets/votes_spec.rb +++ b/spec/features/budgets/votes_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' feature 'Votes' do background do - @manuela = create(:user, verified_at: Time.now) + @manuela = create(:user, verified_at: Time.current) end feature 'Investments' do diff --git a/spec/models/budget/investment_spec.rb b/spec/models/budget/investment_spec.rb index b811e8061..2290abba9 100644 --- a/spec/models/budget/investment_spec.rb +++ b/spec/models/budget/investment_spec.rb @@ -712,7 +712,7 @@ describe Budget::Investment do ballot = create(:budget_ballot, user: user, budget: budget) ballot.investments << inv1 - expect(inv2.reason_for_not_being_ballotable_by(user, ballot)).to eq(:not_enough_money) + expect(inv2.reason_for_not_being_ballotable_by(user, ballot)).to eq(:not_enough_money_html) end end diff --git a/spec/models/direct_message_spec.rb b/spec/models/direct_message_spec.rb index e982f9e56..05c3f75cd 100644 --- a/spec/models/direct_message_spec.rb +++ b/spec/models/direct_message_spec.rb @@ -65,9 +65,9 @@ describe DirectMessage do describe "today" do it "should return direct messages created today" do - direct_message1 = create(:direct_message, created_at: Time.zone.now.beginning_of_day + 3.hours) - direct_message2 = create(:direct_message, created_at: Time.zone.now) - direct_message3 = create(:direct_message, created_at: Time.zone.now.end_of_day) + direct_message1 = create(:direct_message, created_at: Time.current.beginning_of_day + 3.hours) + direct_message2 = create(:direct_message, created_at: Time.current) + direct_message3 = create(:direct_message, created_at: Time.current.end_of_day) expect(DirectMessage.today.count).to eq 3 end @@ -82,4 +82,4 @@ describe DirectMessage do end -end \ No newline at end of file +end diff --git a/spec/models/poll/recount_spec.rb b/spec/models/poll/recount_spec.rb index fb2c80704..2469354c2 100644 --- a/spec/models/poll/recount_spec.rb +++ b/spec/models/poll/recount_spec.rb @@ -23,18 +23,21 @@ describe :recount do expect(recount.count_log).to eq("") recount.count = 33 - recount.officer_assignment = create(:poll_officer_assignment, id: 11) + poll_officer_assignment_1 = create(:poll_officer_assignment) + recount.officer_assignment = poll_officer_assignment_1 recount.save recount.count = 32 - recount.officer_assignment = create(:poll_officer_assignment, id: 12) + poll_officer_assignment_2 = create(:poll_officer_assignment) + recount.officer_assignment = poll_officer_assignment_2 recount.save recount.count = 34 - recount.officer_assignment = create(:poll_officer_assignment, id: 13) + poll_officer_assignment_3 = create(:poll_officer_assignment) + recount.officer_assignment = poll_officer_assignment_3 recount.save - expect(recount.officer_assignment_id_log).to eq(":11:12") + expect(recount.officer_assignment_id_log).to eq(":#{poll_officer_assignment_1.id}:#{poll_officer_assignment_2.id}") end -end \ No newline at end of file +end