From bf6cadd13745e08afcc94eed55773df5627f9898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 7 Dec 2018 11:39:32 +0100 Subject: [PATCH 01/55] Remove redundant comments Commenting things like "proposal_code_prefix" represents the "Prefix for the Proposal codes" is unnecessary. --- db/seeds.rb | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/db/seeds.rb b/db/seeds.rb index 20bc74e08..7ad3c513e 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -15,25 +15,18 @@ Setting["official_level_3_name"] = "Cargo oficial 3" Setting["official_level_4_name"] = "Cargo oficial 4" Setting["official_level_5_name"] = "Cargo oficial 5" -# Max percentage of allowed anonymous votes on a debate Setting["max_ratio_anon_votes_on_debates"] = 50 -# Max votes where a debate is still editable Setting["max_votes_for_debate_edit"] = 1000 -# Max votes where a proposal is still editable Setting["max_votes_for_proposal_edit"] = 1000 -# Max length for comments Setting["comments_body_max_length"] = 1000 -# Prefix for the Proposal codes Setting["proposal_code_prefix"] = "CONSUL" -# Number of votes needed for proposal success Setting["votes_for_proposal_success"] = 53726 -# Months to archive proposals Setting["months_to_archive_proposals"] = 12 # Users with this email domain will automatically be marked as level 1 officials @@ -46,7 +39,6 @@ Setting["html.per_page_code_head"] = "" # Code to be included at the top (inside ) of every page Setting["html.per_page_code_body"] = "" -# Social settings Setting["twitter_handle"] = nil Setting["twitter_hashtag"] = nil Setting["facebook_handle"] = nil @@ -54,25 +46,21 @@ Setting["youtube_handle"] = nil Setting["telegram_handle"] = nil Setting["instagram_handle"] = nil -# Public-facing URL of the app. -Setting["url"] = "http://example.com" +Setting["url"] = "http://example.com" # Public-facing URL of the app. # CONSUL installation's organization name Setting["org_name"] = "CONSUL" -# Meta tags for SEO Setting["meta_title"] = nil Setting["meta_description"] = nil Setting["meta_keywords"] = nil -# Processes Setting["process.debates"] = true Setting["process.proposals"] = true Setting["process.polls"] = true Setting["process.budgets"] = true Setting["process.legislation"] = true -# Feature flags Setting["feature.featured_proposals"] = nil Setting["feature.twitter_login"] = true Setting["feature.facebook_login"] = true @@ -89,34 +77,26 @@ Setting["feature.allow_images"] = true Setting["feature.allow_attached_documents"] = true Setting["feature.help_page"] = true -# Proposal notifications Setting["proposal_notification_minimum_interval_in_days"] = 3 Setting["direct_message_max_per_day"] = 3 -# Email settings Setting["mailer_from_name"] = "CONSUL" Setting["mailer_from_address"] = "noreply@consul.dev" -# Verification settings Setting["min_age_to_participate"] = 16 -# Featured proposals Setting["featured_proposals_number"] = 3 -# City map feature default configuration (Greenwich) Setting["map.latitude"] = 51.48 Setting["map.longitude"] = 0.0 Setting["map.zoom"] = 10 -# Related content Setting["related_content_score_threshold"] = -0.3 -# Homepage Setting["homepage.widgets.feeds.proposals"] = true Setting["homepage.widgets.feeds.debates"] = true Setting["homepage.widgets.feeds.processes"] = true -# Votes hot_score configuration Setting["hot_score_period_in_days"] = 31 WebSection.create(name: "homepage") @@ -125,7 +105,6 @@ WebSection.create(name: "proposals") WebSection.create(name: "budgets") WebSection.create(name: "help_page") -# Proposals Setting["proposals.successful_proposal_id"] = nil Setting["proposals.poll_short_title"] = nil Setting["proposals.poll_description"] = nil @@ -135,7 +114,6 @@ Setting["proposals.email_description"] = nil Setting["proposals.poster_short_title"] = nil Setting["proposals.poster_description"] = nil -# Dashboard Setting["dashboard.emails"] = nil # Default custom pages From 19245efc93535301ca534e1e324b21cfabd04f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 7 Dec 2018 11:52:53 +0100 Subject: [PATCH 02/55] Simplify code seeding settings --- db/seeds.rb | 182 +++++++++++++++++++++++----------------------------- 1 file changed, 81 insertions(+), 101 deletions(-) diff --git a/db/seeds.rb b/db/seeds.rb index 7ad3c513e..808864efc 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -7,97 +7,88 @@ if Administrator.count == 0 && !Rails.env.test? admin.create_administrator end -# Names for the moderation console, as a hint for moderators -# to know better how to assign users with official positions -Setting["official_level_1_name"] = "Cargo oficial 1" -Setting["official_level_2_name"] = "Cargo oficial 2" -Setting["official_level_3_name"] = "Cargo oficial 3" -Setting["official_level_4_name"] = "Cargo oficial 4" -Setting["official_level_5_name"] = "Cargo oficial 5" +default_settings = { + # Names for the moderation console, as a hint for moderators + # to know better how to assign users with official positions + "official_level_1_name": "Cargo oficial 1", + "official_level_2_name": "Cargo oficial 2", + "official_level_3_name": "Cargo oficial 3", + "official_level_4_name": "Cargo oficial 4", + "official_level_5_name": "Cargo oficial 5", + "max_ratio_anon_votes_on_debates": 50, + "max_votes_for_debate_edit": 1000, + "max_votes_for_proposal_edit": 1000, + "comments_body_max_length": 1000, + "proposal_code_prefix": "CONSUL", + "votes_for_proposal_success": 53726, + "months_to_archive_proposals": 12, + # Users with this email domain will automatically be marked as level 1 officials + # Emails under the domain's subdomains will also be included + "email_domain_for_officials": "", + # Code to be included at the top (inside ) of every page (useful for tracking) + "html.per_page_code_head": "", + # Code to be included at the top (inside ) of every page + "html.per_page_code_body": "", + "twitter_handle": nil, + "twitter_hashtag": nil, + "facebook_handle": nil, + "youtube_handle": nil, + "telegram_handle": nil, + "instagram_handle": nil, + "url": "http://example.com", # Public-facing URL of the app. + # CONSUL installation's organization name + "org_name": "CONSUL", + "meta_title": nil, + "meta_description": nil, + "meta_keywords": nil, + "process.debates": true, + "process.proposals": true, + "process.polls": true, + "process.budgets": true, + "process.legislation": true, + "feature.featured_proposals": nil, + "feature.twitter_login": true, + "feature.facebook_login": true, + "feature.google_login": true, + "feature.public_stats": true, + "feature.signature_sheets": true, + "feature.user.recommendations": true, + "feature.user.recommendations_on_debates": true, + "feature.user.recommendations_on_proposals": true, + "feature.user.skip_verification": "true", + "feature.community": true, + "feature.map": nil, + "feature.allow_images": true, + "feature.allow_attached_documents": true, + "feature.help_page": true, + "proposal_notification_minimum_interval_in_days": 3, + "direct_message_max_per_day": 3, + "mailer_from_name": "CONSUL", + "mailer_from_address": "noreply@consul.dev", + "min_age_to_participate": 16, + "featured_proposals_number": 3, + "map.latitude": 51.48, + "map.longitude": 0.0, + "map.zoom": 10, + "related_content_score_threshold": -0.3, + "homepage.widgets.feeds.proposals": true, + "homepage.widgets.feeds.debates": true, + "homepage.widgets.feeds.processes": true, + "hot_score_period_in_days": 31, + "proposals.successful_proposal_id": nil, + "proposals.poll_short_title": nil, + "proposals.poll_description": nil, + "proposals.poll_link": nil, + "proposals.email_short_title": nil, + "proposals.email_description": nil, + "proposals.poster_short_title": nil, + "proposals.poster_description": nil, + "dashboard.emails": nil +} -Setting["max_ratio_anon_votes_on_debates"] = 50 - -Setting["max_votes_for_debate_edit"] = 1000 - -Setting["max_votes_for_proposal_edit"] = 1000 - -Setting["comments_body_max_length"] = 1000 - -Setting["proposal_code_prefix"] = "CONSUL" - -Setting["votes_for_proposal_success"] = 53726 - -Setting["months_to_archive_proposals"] = 12 - -# Users with this email domain will automatically be marked as level 1 officials -# Emails under the domain's subdomains will also be included -Setting["email_domain_for_officials"] = "" - -# Code to be included at the top (inside ) of every page (useful for tracking) -Setting["html.per_page_code_head"] = "" - -# Code to be included at the top (inside ) of every page -Setting["html.per_page_code_body"] = "" - -Setting["twitter_handle"] = nil -Setting["twitter_hashtag"] = nil -Setting["facebook_handle"] = nil -Setting["youtube_handle"] = nil -Setting["telegram_handle"] = nil -Setting["instagram_handle"] = nil - -Setting["url"] = "http://example.com" # Public-facing URL of the app. - -# CONSUL installation's organization name -Setting["org_name"] = "CONSUL" - -Setting["meta_title"] = nil -Setting["meta_description"] = nil -Setting["meta_keywords"] = nil - -Setting["process.debates"] = true -Setting["process.proposals"] = true -Setting["process.polls"] = true -Setting["process.budgets"] = true -Setting["process.legislation"] = true - -Setting["feature.featured_proposals"] = nil -Setting["feature.twitter_login"] = true -Setting["feature.facebook_login"] = true -Setting["feature.google_login"] = true -Setting["feature.public_stats"] = true -Setting["feature.signature_sheets"] = true -Setting["feature.user.recommendations"] = true -Setting["feature.user.recommendations_on_debates"] = true -Setting["feature.user.recommendations_on_proposals"] = true -Setting["feature.user.skip_verification"] = "true" -Setting["feature.community"] = true -Setting["feature.map"] = nil -Setting["feature.allow_images"] = true -Setting["feature.allow_attached_documents"] = true -Setting["feature.help_page"] = true - -Setting["proposal_notification_minimum_interval_in_days"] = 3 -Setting["direct_message_max_per_day"] = 3 - -Setting["mailer_from_name"] = "CONSUL" -Setting["mailer_from_address"] = "noreply@consul.dev" - -Setting["min_age_to_participate"] = 16 - -Setting["featured_proposals_number"] = 3 - -Setting["map.latitude"] = 51.48 -Setting["map.longitude"] = 0.0 -Setting["map.zoom"] = 10 - -Setting["related_content_score_threshold"] = -0.3 - -Setting["homepage.widgets.feeds.proposals"] = true -Setting["homepage.widgets.feeds.debates"] = true -Setting["homepage.widgets.feeds.processes"] = true - -Setting["hot_score_period_in_days"] = 31 +default_settings.each do |name, value| + Setting[name] = value +end WebSection.create(name: "homepage") WebSection.create(name: "debates") @@ -105,16 +96,5 @@ WebSection.create(name: "proposals") WebSection.create(name: "budgets") WebSection.create(name: "help_page") -Setting["proposals.successful_proposal_id"] = nil -Setting["proposals.poll_short_title"] = nil -Setting["proposals.poll_description"] = nil -Setting["proposals.poll_link"] = nil -Setting["proposals.email_short_title"] = nil -Setting["proposals.email_description"] = nil -Setting["proposals.poster_short_title"] = nil -Setting["proposals.poster_description"] = nil - -Setting["dashboard.emails"] = nil - # Default custom pages load Rails.root.join("db", "pages.rb") From bac0004fed7de842348c105bb16ecb6c08666258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 7 Dec 2018 11:56:43 +0100 Subject: [PATCH 03/55] Move default settings to Settings model --- app/models/setting.rb | 81 +++++++++++++++++++++++++++++++++++++++++++ db/seeds.rb | 81 +------------------------------------------ 2 files changed, 82 insertions(+), 80 deletions(-) diff --git a/app/models/setting.rb b/app/models/setting.rb index 2275a3d4c..7e1ea0270 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -40,5 +40,86 @@ class Setting < ApplicationRecord setting = where(key: key).first setting.destroy if setting.present? end + + def defaults + { + # Names for the moderation console, as a hint for moderators + # to know better how to assign users with official positions + "official_level_1_name": "Cargo oficial 1", + "official_level_2_name": "Cargo oficial 2", + "official_level_3_name": "Cargo oficial 3", + "official_level_4_name": "Cargo oficial 4", + "official_level_5_name": "Cargo oficial 5", + "max_ratio_anon_votes_on_debates": 50, + "max_votes_for_debate_edit": 1000, + "max_votes_for_proposal_edit": 1000, + "comments_body_max_length": 1000, + "proposal_code_prefix": "CONSUL", + "votes_for_proposal_success": 53726, + "months_to_archive_proposals": 12, + # Users with this email domain will automatically be marked as level 1 officials + # Emails under the domain's subdomains will also be included + "email_domain_for_officials": "", + # Code to be included at the top (inside ) of every page (useful for tracking) + "html.per_page_code_head": "", + # Code to be included at the top (inside ) of every page + "html.per_page_code_body": "", + "twitter_handle": nil, + "twitter_hashtag": nil, + "facebook_handle": nil, + "youtube_handle": nil, + "telegram_handle": nil, + "instagram_handle": nil, + "url": "http://example.com", # Public-facing URL of the app. + # CONSUL installation's organization name + "org_name": "CONSUL", + "meta_title": nil, + "meta_description": nil, + "meta_keywords": nil, + "process.debates": true, + "process.proposals": true, + "process.polls": true, + "process.budgets": true, + "process.legislation": true, + "feature.featured_proposals": nil, + "feature.twitter_login": true, + "feature.facebook_login": true, + "feature.google_login": true, + "feature.public_stats": true, + "feature.signature_sheets": true, + "feature.user.recommendations": true, + "feature.user.recommendations_on_debates": true, + "feature.user.recommendations_on_proposals": true, + "feature.user.skip_verification": "true", + "feature.community": true, + "feature.map": nil, + "feature.allow_images": true, + "feature.allow_attached_documents": true, + "feature.help_page": true, + "proposal_notification_minimum_interval_in_days": 3, + "direct_message_max_per_day": 3, + "mailer_from_name": "CONSUL", + "mailer_from_address": "noreply@consul.dev", + "min_age_to_participate": 16, + "featured_proposals_number": 3, + "map.latitude": 51.48, + "map.longitude": 0.0, + "map.zoom": 10, + "related_content_score_threshold": -0.3, + "homepage.widgets.feeds.proposals": true, + "homepage.widgets.feeds.debates": true, + "homepage.widgets.feeds.processes": true, + "hot_score_period_in_days": 31, + "proposals.successful_proposal_id": nil, + "proposals.poll_short_title": nil, + "proposals.poll_description": nil, + "proposals.poll_link": nil, + "proposals.email_short_title": nil, + "proposals.email_description": nil, + "proposals.poster_short_title": nil, + "proposals.poster_description": nil, + "dashboard.emails": nil + } + end end end diff --git a/db/seeds.rb b/db/seeds.rb index 808864efc..5acdcb9b3 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -7,86 +7,7 @@ if Administrator.count == 0 && !Rails.env.test? admin.create_administrator end -default_settings = { - # Names for the moderation console, as a hint for moderators - # to know better how to assign users with official positions - "official_level_1_name": "Cargo oficial 1", - "official_level_2_name": "Cargo oficial 2", - "official_level_3_name": "Cargo oficial 3", - "official_level_4_name": "Cargo oficial 4", - "official_level_5_name": "Cargo oficial 5", - "max_ratio_anon_votes_on_debates": 50, - "max_votes_for_debate_edit": 1000, - "max_votes_for_proposal_edit": 1000, - "comments_body_max_length": 1000, - "proposal_code_prefix": "CONSUL", - "votes_for_proposal_success": 53726, - "months_to_archive_proposals": 12, - # Users with this email domain will automatically be marked as level 1 officials - # Emails under the domain's subdomains will also be included - "email_domain_for_officials": "", - # Code to be included at the top (inside ) of every page (useful for tracking) - "html.per_page_code_head": "", - # Code to be included at the top (inside ) of every page - "html.per_page_code_body": "", - "twitter_handle": nil, - "twitter_hashtag": nil, - "facebook_handle": nil, - "youtube_handle": nil, - "telegram_handle": nil, - "instagram_handle": nil, - "url": "http://example.com", # Public-facing URL of the app. - # CONSUL installation's organization name - "org_name": "CONSUL", - "meta_title": nil, - "meta_description": nil, - "meta_keywords": nil, - "process.debates": true, - "process.proposals": true, - "process.polls": true, - "process.budgets": true, - "process.legislation": true, - "feature.featured_proposals": nil, - "feature.twitter_login": true, - "feature.facebook_login": true, - "feature.google_login": true, - "feature.public_stats": true, - "feature.signature_sheets": true, - "feature.user.recommendations": true, - "feature.user.recommendations_on_debates": true, - "feature.user.recommendations_on_proposals": true, - "feature.user.skip_verification": "true", - "feature.community": true, - "feature.map": nil, - "feature.allow_images": true, - "feature.allow_attached_documents": true, - "feature.help_page": true, - "proposal_notification_minimum_interval_in_days": 3, - "direct_message_max_per_day": 3, - "mailer_from_name": "CONSUL", - "mailer_from_address": "noreply@consul.dev", - "min_age_to_participate": 16, - "featured_proposals_number": 3, - "map.latitude": 51.48, - "map.longitude": 0.0, - "map.zoom": 10, - "related_content_score_threshold": -0.3, - "homepage.widgets.feeds.proposals": true, - "homepage.widgets.feeds.debates": true, - "homepage.widgets.feeds.processes": true, - "hot_score_period_in_days": 31, - "proposals.successful_proposal_id": nil, - "proposals.poll_short_title": nil, - "proposals.poll_description": nil, - "proposals.poll_link": nil, - "proposals.email_short_title": nil, - "proposals.email_description": nil, - "proposals.poster_short_title": nil, - "proposals.poster_description": nil, - "dashboard.emails": nil -} - -default_settings.each do |name, value| +Setting.defaults.each do |name, value| Setting[name] = value end From 9e6e6d6c9ee7c59793989b5d59192a397edf7439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 7 Dec 2018 11:58:08 +0100 Subject: [PATCH 04/55] Sort settings alphabetically --- app/models/setting.rb | 122 +++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/app/models/setting.rb b/app/models/setting.rb index 7e1ea0270..a820937be 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -43,6 +43,51 @@ class Setting < ApplicationRecord def defaults { + "comments_body_max_length": 1000, + "dashboard.emails": nil, + "direct_message_max_per_day": 3, + # Users with this email domain will automatically be marked as level 1 officials + # Emails under the domain's subdomains will also be included + "email_domain_for_officials": "", + "facebook_handle": nil, + "feature.allow_attached_documents": true, + "feature.allow_images": true, + "feature.community": true, + "feature.facebook_login": true, + "feature.featured_proposals": nil, + "feature.google_login": true, + "feature.help_page": true, + "feature.map": nil, + "feature.public_stats": true, + "feature.signature_sheets": true, + "feature.twitter_login": true, + "feature.user.recommendations": true, + "feature.user.recommendations_on_debates": true, + "feature.user.recommendations_on_proposals": true, + "feature.user.skip_verification": "true", + "featured_proposals_number": 3, + "homepage.widgets.feeds.debates": true, + "homepage.widgets.feeds.processes": true, + "homepage.widgets.feeds.proposals": true, + "hot_score_period_in_days": 31, + # Code to be included at the top (inside ) of every page + "html.per_page_code_body": "", + # Code to be included at the top (inside ) of every page (useful for tracking) + "html.per_page_code_head": "", + "instagram_handle": nil, + "mailer_from_address": "noreply@consul.dev", + "mailer_from_name": "CONSUL", + "map.latitude": 51.48, + "map.longitude": 0.0, + "map.zoom": 10, + "max_ratio_anon_votes_on_debates": 50, + "max_votes_for_debate_edit": 1000, + "max_votes_for_proposal_edit": 1000, + "meta_description": nil, + "meta_keywords": nil, + "meta_title": nil, + "min_age_to_participate": 16, + "months_to_archive_proposals": 12, # Names for the moderation console, as a hint for moderators # to know better how to assign users with official positions "official_level_1_name": "Cargo oficial 1", @@ -50,75 +95,30 @@ class Setting < ApplicationRecord "official_level_3_name": "Cargo oficial 3", "official_level_4_name": "Cargo oficial 4", "official_level_5_name": "Cargo oficial 5", - "max_ratio_anon_votes_on_debates": 50, - "max_votes_for_debate_edit": 1000, - "max_votes_for_proposal_edit": 1000, - "comments_body_max_length": 1000, - "proposal_code_prefix": "CONSUL", - "votes_for_proposal_success": 53726, - "months_to_archive_proposals": 12, - # Users with this email domain will automatically be marked as level 1 officials - # Emails under the domain's subdomains will also be included - "email_domain_for_officials": "", - # Code to be included at the top (inside ) of every page (useful for tracking) - "html.per_page_code_head": "", - # Code to be included at the top (inside ) of every page - "html.per_page_code_body": "", - "twitter_handle": nil, - "twitter_hashtag": nil, - "facebook_handle": nil, - "youtube_handle": nil, - "telegram_handle": nil, - "instagram_handle": nil, - "url": "http://example.com", # Public-facing URL of the app. # CONSUL installation's organization name "org_name": "CONSUL", - "meta_title": nil, - "meta_description": nil, - "meta_keywords": nil, - "process.debates": true, - "process.proposals": true, - "process.polls": true, "process.budgets": true, + "process.debates": true, "process.legislation": true, - "feature.featured_proposals": nil, - "feature.twitter_login": true, - "feature.facebook_login": true, - "feature.google_login": true, - "feature.public_stats": true, - "feature.signature_sheets": true, - "feature.user.recommendations": true, - "feature.user.recommendations_on_debates": true, - "feature.user.recommendations_on_proposals": true, - "feature.user.skip_verification": "true", - "feature.community": true, - "feature.map": nil, - "feature.allow_images": true, - "feature.allow_attached_documents": true, - "feature.help_page": true, + "process.polls": true, + "process.proposals": true, + "proposal_code_prefix": "CONSUL", "proposal_notification_minimum_interval_in_days": 3, - "direct_message_max_per_day": 3, - "mailer_from_name": "CONSUL", - "mailer_from_address": "noreply@consul.dev", - "min_age_to_participate": 16, - "featured_proposals_number": 3, - "map.latitude": 51.48, - "map.longitude": 0.0, - "map.zoom": 10, - "related_content_score_threshold": -0.3, - "homepage.widgets.feeds.proposals": true, - "homepage.widgets.feeds.debates": true, - "homepage.widgets.feeds.processes": true, - "hot_score_period_in_days": 31, - "proposals.successful_proposal_id": nil, - "proposals.poll_short_title": nil, + "proposals.email_description": nil, + "proposals.email_short_title": nil, "proposals.poll_description": nil, "proposals.poll_link": nil, - "proposals.email_short_title": nil, - "proposals.email_description": nil, - "proposals.poster_short_title": nil, + "proposals.poll_short_title": nil, "proposals.poster_description": nil, - "dashboard.emails": nil + "proposals.poster_short_title": nil, + "proposals.successful_proposal_id": nil, + "related_content_score_threshold": -0.3, + "telegram_handle": nil, + "twitter_handle": nil, + "twitter_hashtag": nil, + "url": "http://example.com", # Public-facing URL of the app. + "votes_for_proposal_success": 53726, + "youtube_handle": nil } end end From 7830d90d4ae85444fe123458df0e08cd99ed50ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 7 Dec 2018 12:00:34 +0100 Subject: [PATCH 05/55] Extract method to reset default settings --- app/models/setting.rb | 4 ++++ db/seeds.rb | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/models/setting.rb b/app/models/setting.rb index a820937be..df9a34614 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -121,5 +121,9 @@ class Setting < ApplicationRecord "youtube_handle": nil } end + + def reset_defaults + defaults.each { |name, value| self[name] = value } + end end end diff --git a/db/seeds.rb b/db/seeds.rb index 5acdcb9b3..37599fd0c 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -7,9 +7,7 @@ if Administrator.count == 0 && !Rails.env.test? admin.create_administrator end -Setting.defaults.each do |name, value| - Setting[name] = value -end +Setting.reset_defaults WebSection.create(name: "homepage") WebSection.create(name: "debates") From ced0409270669e2dfc7410ee4aa794c250be3650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 7 Dec 2018 12:08:35 +0100 Subject: [PATCH 06/55] Reuse default settings in dev seeds --- db/dev_seeds/settings.rb | 53 ++-------------------------------------- 1 file changed, 2 insertions(+), 51 deletions(-) diff --git a/db/dev_seeds/settings.rb b/db/dev_seeds/settings.rb index 70b6574e2..1c0a9172b 100644 --- a/db/dev_seeds/settings.rb +++ b/db/dev_seeds/settings.rb @@ -1,4 +1,6 @@ section "Creating Settings" do + Setting.reset_defaults + Setting.create(key: "official_level_1_name", value: I18n.t("seeds.settings.official_level_1_name")) Setting.create(key: "official_level_2_name", @@ -9,13 +11,8 @@ section "Creating Settings" do value: I18n.t("seeds.settings.official_level_4_name")) Setting.create(key: "official_level_5_name", value: I18n.t("seeds.settings.official_level_5_name")) - Setting.create(key: "max_ratio_anon_votes_on_debates", value: "50") - Setting.create(key: "max_votes_for_debate_edit", value: "1000") - Setting.create(key: "max_votes_for_proposal_edit", value: "1000") Setting.create(key: "proposal_code_prefix", value: "MAD") Setting.create(key: "votes_for_proposal_success", value: "100") - Setting.create(key: "months_to_archive_proposals", value: "12") - Setting.create(key: "comments_body_max_length", value: "1000") Setting.create(key: "twitter_handle", value: "@consul_dev") Setting.create(key: "twitter_hashtag", value: "#consul_dev") @@ -26,61 +23,15 @@ section "Creating Settings" do Setting.create(key: "url", value: "http://localhost:3000") Setting.create(key: "org_name", value: "CONSUL") - Setting.create(key: "process.debates", value: "true") - Setting.create(key: "process.proposals", value: "true") - Setting.create(key: "process.polls", value: "true") - Setting.create(key: "process.budgets", value: "true") - Setting.create(key: "process.legislation", value: "true") - Setting.create(key: "feature.featured_proposals", value: "true") - Setting.create(key: "feature.twitter_login", value: "true") - Setting.create(key: "feature.facebook_login", value: "true") - Setting.create(key: "feature.google_login", value: "true") - Setting.create(key: "feature.signature_sheets", value: "true") - Setting.create(key: "feature.user.recommendations", value: "true") - Setting.create(key: "feature.user.recommendations_on_debates", value: "true") - Setting.create(key: "feature.user.recommendations_on_proposals", value: "true") - Setting.create(key: "feature.user.skip_verification", value: "true") - Setting.create(key: "feature.community", value: "true") Setting.create(key: "feature.map", value: "true") - Setting.create(key: "feature.allow_images", value: "true") - Setting.create(key: "feature.allow_attached_documents", value: "true") - Setting.create(key: "feature.public_stats", value: "true") - Setting.create(key: "feature.help_page", value: "true") - Setting.create(key: "html.per_page_code_head", value: "") - Setting.create(key: "html.per_page_code_body", value: "") - - Setting.create(key: "comments_body_max_length", value: "1000") Setting.create(key: "mailer_from_name", value: "CONSUL") Setting.create(key: "mailer_from_address", value: "noreply@consul.dev") Setting.create(key: "meta_title", value: "CONSUL") Setting.create(key: "meta_description", value: "Citizen participation tool for an open, "\ "transparent and democratic government") Setting.create(key: "meta_keywords", value: "citizen participation, open government") - Setting.create(key: "min_age_to_participate", value: "16") - Setting.create(key: "map.latitude", value: 40.4332002) - Setting.create(key: "map.longitude", value: -3.7009591) - Setting.create(key: "map.zoom", value: 10) - - Setting.create(key: "featured_proposals_number", value: 3) Setting.create(key: "proposal_notification_minimum_interval_in_days", value: 0) - Setting.create(key: "direct_message_max_per_day", value: 3) - - Setting.create(key: "related_content_score_threshold", value: -0.3) - Setting.create(key: "hot_score_period_in_days", value: 31) - - Setting.create(key: "homepage.widgets.feeds.proposals", value: "true") - Setting.create(key: "homepage.widgets.feeds.debates", value: "true") - Setting.create(key: "homepage.widgets.feeds.processes", value: "true") - - Setting.create(key: "proposals.successful_proposal_id", value: nil) - Setting.create(key: "proposals.poll_short_title", value: nil) - Setting.create(key: "proposals.poll_description", value: nil) - Setting.create(key: "proposals.poll_link", value: nil) - Setting.create(key: "proposals.email_short_title", value: nil) - Setting.create(key: "proposals.email_description", value: nil) - - Setting.create(key: "dashboard.emails", value: nil) end From 3db1acf73d6b9cc7b79d2f0535f1e28e637169f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 7 Dec 2018 12:18:04 +0100 Subject: [PATCH 07/55] Simplify dev seed settings code --- db/dev_seeds/settings.rb | 61 ++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/db/dev_seeds/settings.rb b/db/dev_seeds/settings.rb index 1c0a9172b..a8ba73be8 100644 --- a/db/dev_seeds/settings.rb +++ b/db/dev_seeds/settings.rb @@ -1,37 +1,32 @@ section "Creating Settings" do Setting.reset_defaults - Setting.create(key: "official_level_1_name", - value: I18n.t("seeds.settings.official_level_1_name")) - Setting.create(key: "official_level_2_name", - value: I18n.t("seeds.settings.official_level_2_name")) - Setting.create(key: "official_level_3_name", - value: I18n.t("seeds.settings.official_level_3_name")) - Setting.create(key: "official_level_4_name", - value: I18n.t("seeds.settings.official_level_4_name")) - Setting.create(key: "official_level_5_name", - value: I18n.t("seeds.settings.official_level_5_name")) - Setting.create(key: "proposal_code_prefix", value: "MAD") - Setting.create(key: "votes_for_proposal_success", value: "100") - - Setting.create(key: "twitter_handle", value: "@consul_dev") - Setting.create(key: "twitter_hashtag", value: "#consul_dev") - Setting.create(key: "facebook_handle", value: "CONSUL") - Setting.create(key: "youtube_handle", value: "CONSUL") - Setting.create(key: "telegram_handle", value: "CONSUL") - Setting.create(key: "instagram_handle", value: "CONSUL") - Setting.create(key: "url", value: "http://localhost:3000") - Setting.create(key: "org_name", value: "CONSUL") - - Setting.create(key: "feature.featured_proposals", value: "true") - - Setting.create(key: "feature.map", value: "true") - - Setting.create(key: "mailer_from_name", value: "CONSUL") - Setting.create(key: "mailer_from_address", value: "noreply@consul.dev") - Setting.create(key: "meta_title", value: "CONSUL") - Setting.create(key: "meta_description", value: "Citizen participation tool for an open, "\ - "transparent and democratic government") - Setting.create(key: "meta_keywords", value: "citizen participation, open government") - Setting.create(key: "proposal_notification_minimum_interval_in_days", value: 0) + { + "facebook_handle": "CONSUL", + "feature.featured_proposals": "true", + "feature.map": "true", + "instagram_handle": "CONSUL", + "mailer_from_address": "noreply@consul.dev", + "mailer_from_name": "CONSUL", + "meta_description": "Citizen participation tool for an open, "\ + "transparent and democratic government", + "meta_keywords": "citizen participation, open government", + "meta_title": "CONSUL", + "official_level_1_name": I18n.t("seeds.settings.official_level_1_name"), + "official_level_2_name": I18n.t("seeds.settings.official_level_2_name"), + "official_level_3_name": I18n.t("seeds.settings.official_level_3_name"), + "official_level_4_name": I18n.t("seeds.settings.official_level_4_name"), + "official_level_5_name": I18n.t("seeds.settings.official_level_5_name"), + "org_name": "CONSUL", + "proposal_code_prefix": "MAD", + "proposal_notification_minimum_interval_in_days": 0, + "telegram_handle": "CONSUL", + "twitter_handle": "@consul_dev", + "twitter_hashtag": "#consul_dev", + "url": "http://localhost:3000", + "votes_for_proposal_success": "100", + "youtube_handle": "CONSUL" + }.each do |name, value| + Setting[name] = value + end end From ff21ce7665fa5df33b342162254fa31134b7dd5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 30 May 2019 17:35:53 +0200 Subject: [PATCH 08/55] Use translations for official level names seeds --- app/models/setting.rb | 10 +++++----- config/i18n-tasks.yml | 3 ++- db/dev_seeds/settings.rb | 5 ----- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/app/models/setting.rb b/app/models/setting.rb index df9a34614..fcac095ce 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -90,11 +90,11 @@ class Setting < ApplicationRecord "months_to_archive_proposals": 12, # Names for the moderation console, as a hint for moderators # to know better how to assign users with official positions - "official_level_1_name": "Cargo oficial 1", - "official_level_2_name": "Cargo oficial 2", - "official_level_3_name": "Cargo oficial 3", - "official_level_4_name": "Cargo oficial 4", - "official_level_5_name": "Cargo oficial 5", + "official_level_1_name": I18n.t("seeds.settings.official_level_1_name"), + "official_level_2_name": I18n.t("seeds.settings.official_level_2_name"), + "official_level_3_name": I18n.t("seeds.settings.official_level_3_name"), + "official_level_4_name": I18n.t("seeds.settings.official_level_4_name"), + "official_level_5_name": I18n.t("seeds.settings.official_level_5_name"), # CONSUL installation's organization name "org_name": "CONSUL", "process.budgets": true, diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index b0beb1e42..240313c32 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -118,6 +118,7 @@ ignore_missing: - "devise.failure.invalid" - "devise.registrations.destroyed" - "devise.password_expired.*" + - "seeds.settings.*" ## Consider these keys used: ignore_unused: @@ -206,7 +207,7 @@ ignore_unused: - admin.stats.polls.expired - "stats.polls.*_percentage" - landings.cambia_tu_ciudad.* - - 'seeds.settings.*' + - "seeds.settings.*" - "dashboard.polls.*.submit" #### ## Exclude these keys from the `i18n-tasks eq-base" report: diff --git a/db/dev_seeds/settings.rb b/db/dev_seeds/settings.rb index a8ba73be8..364773ab6 100644 --- a/db/dev_seeds/settings.rb +++ b/db/dev_seeds/settings.rb @@ -12,11 +12,6 @@ section "Creating Settings" do "transparent and democratic government", "meta_keywords": "citizen participation, open government", "meta_title": "CONSUL", - "official_level_1_name": I18n.t("seeds.settings.official_level_1_name"), - "official_level_2_name": I18n.t("seeds.settings.official_level_2_name"), - "official_level_3_name": I18n.t("seeds.settings.official_level_3_name"), - "official_level_4_name": I18n.t("seeds.settings.official_level_4_name"), - "official_level_5_name": I18n.t("seeds.settings.official_level_5_name"), "org_name": "CONSUL", "proposal_code_prefix": "MAD", "proposal_notification_minimum_interval_in_days": 0, From cf26b230c97b884af2db03011505f323afbb78c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 30 May 2019 17:37:23 +0200 Subject: [PATCH 09/55] Use a rounded value for default supports needed We were using the value which is used in Madrid. --- app/models/setting.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/setting.rb b/app/models/setting.rb index fcac095ce..7295136bc 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -117,7 +117,7 @@ class Setting < ApplicationRecord "twitter_handle": nil, "twitter_hashtag": nil, "url": "http://example.com", # Public-facing URL of the app. - "votes_for_proposal_success": 53726, + "votes_for_proposal_success": 10000, "youtube_handle": nil } end From 555fae0811af60671dd69581e0a41150f1c2f583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 31 May 2019 15:20:10 +0200 Subject: [PATCH 10/55] Order settings to follow our traditional order Settings are displayed in the administration in the order they are created, and reordering them alphabetically in the code caused them to be displayed in a different order. Since we don't want to confuse our users by randomly changing the order, I've grouped the settings by namespaces and ordered each namespace the way it used to be. Note dev seeds don't need to be changed, since they are executed after default settings have been created. --- app/models/setting.rb | 92 +++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/app/models/setting.rb b/app/models/setting.rb index 7295136bc..1cd5152f8 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -43,51 +43,44 @@ class Setting < ApplicationRecord def defaults { - "comments_body_max_length": 1000, - "dashboard.emails": nil, - "direct_message_max_per_day": 3, - # Users with this email domain will automatically be marked as level 1 officials - # Emails under the domain's subdomains will also be included - "email_domain_for_officials": "", - "facebook_handle": nil, - "feature.allow_attached_documents": true, - "feature.allow_images": true, - "feature.community": true, - "feature.facebook_login": true, "feature.featured_proposals": nil, + "feature.facebook_login": true, "feature.google_login": true, - "feature.help_page": true, - "feature.map": nil, + "feature.twitter_login": true, "feature.public_stats": true, "feature.signature_sheets": true, - "feature.twitter_login": true, "feature.user.recommendations": true, "feature.user.recommendations_on_debates": true, "feature.user.recommendations_on_proposals": true, "feature.user.skip_verification": "true", - "featured_proposals_number": 3, + "feature.community": true, + "feature.map": nil, + "feature.allow_attached_documents": true, + "feature.allow_images": true, + "feature.help_page": true, "homepage.widgets.feeds.debates": true, "homepage.widgets.feeds.processes": true, "homepage.widgets.feeds.proposals": true, - "hot_score_period_in_days": 31, # Code to be included at the top (inside ) of every page "html.per_page_code_body": "", # Code to be included at the top (inside ) of every page (useful for tracking) "html.per_page_code_head": "", - "instagram_handle": nil, - "mailer_from_address": "noreply@consul.dev", - "mailer_from_name": "CONSUL", "map.latitude": 51.48, "map.longitude": 0.0, "map.zoom": 10, - "max_ratio_anon_votes_on_debates": 50, - "max_votes_for_debate_edit": 1000, - "max_votes_for_proposal_edit": 1000, - "meta_description": nil, - "meta_keywords": nil, - "meta_title": nil, - "min_age_to_participate": 16, - "months_to_archive_proposals": 12, + "process.debates": true, + "process.proposals": true, + "process.polls": true, + "process.budgets": true, + "process.legislation": true, + "proposals.successful_proposal_id": nil, + "proposals.poll_short_title": nil, + "proposals.poll_description": nil, + "proposals.poll_link": nil, + "proposals.email_short_title": nil, + "proposals.email_description": nil, + "proposals.poster_short_title": nil, + "proposals.poster_description": nil, # Names for the moderation console, as a hint for moderators # to know better how to assign users with official positions "official_level_1_name": I18n.t("seeds.settings.official_level_1_name"), @@ -95,30 +88,37 @@ class Setting < ApplicationRecord "official_level_3_name": I18n.t("seeds.settings.official_level_3_name"), "official_level_4_name": I18n.t("seeds.settings.official_level_4_name"), "official_level_5_name": I18n.t("seeds.settings.official_level_5_name"), - # CONSUL installation's organization name - "org_name": "CONSUL", - "process.budgets": true, - "process.debates": true, - "process.legislation": true, - "process.polls": true, - "process.proposals": true, + "max_ratio_anon_votes_on_debates": 50, + "max_votes_for_debate_edit": 1000, + "max_votes_for_proposal_edit": 1000, + "comments_body_max_length": 1000, "proposal_code_prefix": "CONSUL", - "proposal_notification_minimum_interval_in_days": 3, - "proposals.email_description": nil, - "proposals.email_short_title": nil, - "proposals.poll_description": nil, - "proposals.poll_link": nil, - "proposals.poll_short_title": nil, - "proposals.poster_description": nil, - "proposals.poster_short_title": nil, - "proposals.successful_proposal_id": nil, - "related_content_score_threshold": -0.3, + "votes_for_proposal_success": 10000, + "months_to_archive_proposals": 12, + # Users with this email domain will automatically be marked as level 1 officials + # Emails under the domain's subdomains will also be included + "email_domain_for_officials": "", + "facebook_handle": nil, + "instagram_handle": nil, "telegram_handle": nil, "twitter_handle": nil, "twitter_hashtag": nil, + "youtube_handle": nil, "url": "http://example.com", # Public-facing URL of the app. - "votes_for_proposal_success": 10000, - "youtube_handle": nil + # CONSUL installation's organization name + "org_name": "CONSUL", + "meta_title": nil, + "meta_description": nil, + "meta_keywords": nil, + "proposal_notification_minimum_interval_in_days": 3, + "direct_message_max_per_day": 3, + "mailer_from_name": "CONSUL", + "mailer_from_address": "noreply@consul.dev", + "min_age_to_participate": 16, + "hot_score_period_in_days": 31, + "related_content_score_threshold": -0.3, + "featured_proposals_number": 3, + "dashboard.emails": nil } end From bb504d90fbee5edb8e9745a887a30c8766d82a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 7 Dec 2018 12:35:26 +0100 Subject: [PATCH 11/55] Add method to add new settings --- app/models/setting.rb | 6 ++++ spec/models/setting_spec.rb | 56 +++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/app/models/setting.rb b/app/models/setting.rb index 1cd5152f8..6210b6ba5 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -125,5 +125,11 @@ class Setting < ApplicationRecord def reset_defaults defaults.each { |name, value| self[name] = value } end + + def add_new_settings + defaults.each do |name, value| + self[name] = value unless find_by(key: name) + end + end end end diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb index c0e060a3c..6fa86b686 100644 --- a/spec/models/setting_spec.rb +++ b/spec/models/setting_spec.rb @@ -117,4 +117,60 @@ describe Setting do expect(Setting.all).to eq all_settings end end + + describe ".add_new_settings" do + context "default settings with strings" do + before do + allow(Setting).to receive(:defaults).and_return({ stub: "stub" }) + end + + it "creates the setting if it doesn't exist" do + expect(Setting.where(key: :stub)).to be_empty + + Setting.add_new_settings + + expect(Setting.where(key: :stub)).not_to be_empty + expect(Setting.find_by(key: :stub).value).to eq "stub" + end + + it "doesn't modify custom values" do + Setting["stub"] = "custom" + + Setting.add_new_settings + + expect(Setting.find_by(key: :stub).value).to eq "custom" + end + + it "doesn't modify custom nil values" do + Setting["stub"] = nil + + Setting.add_new_settings + + expect(Setting.find_by(key: :stub).value).to be_nil + end + end + + context "nil default settings" do + before do + allow(Setting).to receive(:defaults).and_return({ stub: nil }) + end + + it "creates the setting if it doesn't exist" do + expect(Setting.where(key: :stub)).to be_empty + + Setting.add_new_settings + + expect(Setting.where(key: :stub)).not_to be_empty + expect(Setting.find_by(key: :stub).value).to be_nil + end + + it "doesn't modify custom values" do + Setting["stub"] = "custom" + + Setting.add_new_settings + + expect(Setting.find_by(key: :stub).value).to eq "custom" + end + end + end end From e339ed94d1ba04de7c97dfc52e366167f1ddefd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 7 Dec 2018 12:47:52 +0100 Subject: [PATCH 12/55] Add new settings every time we deploy This way we don't need to add and execute rake tasks every time we add a new setting. --- config/deploy.rb | 11 +++++++++++ lib/tasks/settings.rake | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/config/deploy.rb b/config/deploy.rb index dc6639ac1..bb7627f3a 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -45,6 +45,7 @@ namespace :deploy do #before :starting, "rvm1:install:ruby" # install Ruby and create gemset #before :starting, "install_bundler_gem" # install bundler gem + after "deploy:migrate", "add_new_settings" after :publishing, "deploy:restart" after :published, "delayed_job:restart" after :published, "refresh_sitemap" @@ -67,3 +68,13 @@ task :refresh_sitemap do end end end + +task :add_new_settings do + on roles(:app) do + within release_path do + with rails_env: fetch(:rails_env) do + execute :rake, "settings:add_new_settings" + end + end + end +end diff --git a/lib/tasks/settings.rake b/lib/tasks/settings.rake index 286c3e85c..c206af98d 100644 --- a/lib/tasks/settings.rake +++ b/lib/tasks/settings.rake @@ -46,4 +46,8 @@ namespace :settings do Setting.rename_key from: "feature.homepage.widgets.feeds.processes", to: "homepage.widgets.feeds.processes" end + desc "Add new settings" + task add_new_settings: :environment do + Setting.add_new_settings + end end From 85722eb7c40ef4c859900be99370aba0573baea8 Mon Sep 17 00:00:00 2001 From: German Galia Date: Sun, 2 Jun 2019 19:12:26 +0200 Subject: [PATCH 13/55] Rename admin/debates and admin/comments to hidden --- .gitignore | 1 - ...oller.rb => hidden_comments_controller.rb} | 2 +- ...roller.rb => hidden_debates_controller.rb} | 4 +-- app/controllers/officing/base_controller.rb | 1 - app/helpers/admin_helper.rb | 2 +- app/views/admin/_menu.html.erb | 8 ++--- .../budget_investments/_investments.html.erb | 1 + .../index.html.erb | 13 ++++---- .../index.html.erb | 11 +++---- .../legislation/proposals/_geozones.html.erb | 2 +- app/views/stats/index.json.erb | 4 +-- config/environments/production.rb | 1 + config/i18n-tasks.yml | 6 ++-- config/locales/en/admin.yml | 4 +-- config/locales/es/admin.yml | 4 +-- config/routes/admin.rb | 4 +-- spec/factories/polls.rb | 1 - spec/features/admin/activity_spec.rb | 4 +-- spec/features/admin/comments_spec.rb | 30 +++++++++---------- spec/features/admin/debates_spec.rb | 22 +++++++------- 20 files changed, 63 insertions(+), 62 deletions(-) rename app/controllers/admin/{comments_controller.rb => hidden_comments_controller.rb} (92%) rename app/controllers/admin/{debates_controller.rb => hidden_debates_controller.rb} (91%) rename app/views/admin/{comments => hidden_comments}/index.html.erb (71%) rename app/views/admin/{debates => hidden_debates}/index.html.erb (78%) diff --git a/.gitignore b/.gitignore index 31491de9d..57284ee60 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,3 @@ public/sitemap.xml public/system/ /public/ckeditor_assets/ - diff --git a/app/controllers/admin/comments_controller.rb b/app/controllers/admin/hidden_comments_controller.rb similarity index 92% rename from app/controllers/admin/comments_controller.rb rename to app/controllers/admin/hidden_comments_controller.rb index ef815470d..c3e3bdea9 100644 --- a/app/controllers/admin/comments_controller.rb +++ b/app/controllers/admin/hidden_comments_controller.rb @@ -1,4 +1,4 @@ -class Admin::CommentsController < Admin::BaseController +class Admin::HiddenCommentsController < Admin::BaseController has_filters %w{without_confirmed_hide all with_confirmed_hide} before_action :load_comment, only: [:confirm_hide, :restore] diff --git a/app/controllers/admin/debates_controller.rb b/app/controllers/admin/hidden_debates_controller.rb similarity index 91% rename from app/controllers/admin/debates_controller.rb rename to app/controllers/admin/hidden_debates_controller.rb index bbe4a06d7..77d30b488 100644 --- a/app/controllers/admin/debates_controller.rb +++ b/app/controllers/admin/hidden_debates_controller.rb @@ -1,4 +1,4 @@ -class Admin::DebatesController < Admin::BaseController +class Admin::HiddenDebatesController < Admin::BaseController include FeatureFlags feature_flag :debates @@ -29,4 +29,4 @@ class Admin::DebatesController < Admin::BaseController @debate = Debate.with_hidden.find(params[:id]) end -end \ No newline at end of file +end diff --git a/app/controllers/officing/base_controller.rb b/app/controllers/officing/base_controller.rb index 96f185d24..42b85860b 100644 --- a/app/controllers/officing/base_controller.rb +++ b/app/controllers/officing/base_controller.rb @@ -45,5 +45,4 @@ class Officing::BaseController < ApplicationController def current_booth Poll::Booth.where(id: session[:booth_id]).first end - end diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb index 0446aaf73..8f2c9c9cc 100644 --- a/app/helpers/admin_helper.rb +++ b/app/helpers/admin_helper.rb @@ -25,7 +25,7 @@ module AdminHelper end def moderated_sections - ["hidden_proposals", "debates", "comments", "hidden_users", "activity", + ["hidden_proposals", "hidden_debates", "hidden_comments", "hidden_users", "activity", "hidden_budget_investments"] end diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index cb509cf15..9745fcfbc 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -155,8 +155,8 @@ <% end %> <% if feature?(:debates) %> -
  • > - <%= link_to t("admin.menu.hidden_debates"), admin_debates_path %> +
  • > + <%= link_to t("admin.menu.hidden_debates"), admin_hidden_debates_path %>
  • <% end %> @@ -166,8 +166,8 @@ <% end %> -
  • > - <%= link_to t("admin.menu.hidden_comments"), admin_comments_path %> +
  • > + <%= link_to t("admin.menu.hidden_comments"), admin_hidden_comments_path %>
  • > diff --git a/app/views/admin/budget_investments/_investments.html.erb b/app/views/admin/budget_investments/_investments.html.erb index 55603bbcd..9f7f97735 100644 --- a/app/views/admin/budget_investments/_investments.html.erb +++ b/app/views/admin/budget_investments/_investments.html.erb @@ -2,6 +2,7 @@ admin_budget_budget_investments_path(csv_params), class: "float-right small clear" %> + <% if params[:advanced_filters].include?("winners") %> <% if display_calculate_winners_button?(@budget) %> <%= link_to calculate_winner_button_text(@budget), diff --git a/app/views/admin/comments/index.html.erb b/app/views/admin/hidden_comments/index.html.erb similarity index 71% rename from app/views/admin/comments/index.html.erb rename to app/views/admin/hidden_comments/index.html.erb index adf27701e..28cabbcb5 100644 --- a/app/views/admin/comments/index.html.erb +++ b/app/views/admin/hidden_comments/index.html.erb @@ -1,7 +1,8 @@ -

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

    +

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

    <%= t("admin.shared.moderated_content") %>

    -<%= render "shared/filter_subnav", i18n_namespace: "admin.comments.index" %> + +<%= render "shared/filter_subnav", i18n_namespace: "admin.hidden_comments.index" %> <% if @comments.any? %>

    <%= page_entries_info @comments %>

    @@ -17,20 +18,20 @@ <%= text_with_links comment.body %>
    <% if comment.commentable.hidden? %> - (<%= t("admin.comments.index.hidden_#{comment.commentable_type.downcase}") %>: <%= comment.commentable.title %>) + (<%= t("admin.hidden_comments.index.hidden_#{comment.commentable_type.downcase}") %>: <%= comment.commentable.title %>) <% else %> <%= link_to comment.commentable.title, commentable_path(comment) %> <% end %> <%= link_to t("admin.actions.restore"), - restore_admin_comment_path(comment, request.query_parameters), + restore_admin_hidden_comment_path(comment, request.query_parameters), method: :put, data: { confirm: t("admin.actions.confirm") }, class: "button hollow warning" %> <% unless comment.confirmed_hide? %> <%= link_to t("admin.actions.confirm_hide"), - confirm_hide_admin_comment_path(comment, request.query_parameters), + confirm_hide_admin_hidden_comment_path(comment, request.query_parameters), method: :put, class: "button" %> <% end %> @@ -43,6 +44,6 @@ <%= paginate @comments %> <% else %>
    - <%= t("admin.comments.index.no_hidden_comments") %> + <%= t("admin.hidden_comments.index.no_hidden_comments") %>
    <% end %> diff --git a/app/views/admin/debates/index.html.erb b/app/views/admin/hidden_debates/index.html.erb similarity index 78% rename from app/views/admin/debates/index.html.erb rename to app/views/admin/hidden_debates/index.html.erb index 45a992f3e..a11e821d9 100644 --- a/app/views/admin/debates/index.html.erb +++ b/app/views/admin/hidden_debates/index.html.erb @@ -1,7 +1,8 @@ -

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

    +

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

    <%= t("admin.shared.moderated_content") %>

    -<%= render "shared/filter_subnav", i18n_namespace: "admin.debates.index" %> + +<%= render "shared/filter_subnav", i18n_namespace: "admin.hidden_debates.index" %> <% if @debates.any? %>

    <%= page_entries_info @debates %>

    @@ -25,13 +26,13 @@ <%= link_to t("admin.actions.restore"), - restore_admin_debate_path(debate, request.query_parameters), + restore_admin_hidden_debate_path(debate, request.query_parameters), method: :put, data: { confirm: t("admin.actions.confirm") }, class: "button hollow warning" %> <% unless debate.confirmed_hide? %> <%= link_to t("admin.actions.confirm_hide"), - confirm_hide_admin_debate_path(debate, request.query_parameters), + confirm_hide_admin_hidden_debate_path(debate, request.query_parameters), method: :put, class: "button" %> <% end %> @@ -44,6 +45,6 @@ <%= paginate @debates %> <% else %>
    - <%= t("admin.debates.index.no_hidden_debates") %> + <%= t("admin.hidden_debates.index.no_hidden_debates") %>
    <% end %> diff --git a/app/views/legislation/proposals/_geozones.html.erb b/app/views/legislation/proposals/_geozones.html.erb index 064f819dd..4a8d8ee95 100644 --- a/app/views/legislation/proposals/_geozones.html.erb +++ b/app/views/legislation/proposals/_geozones.html.erb @@ -2,5 +2,5 @@
    <%= link_to map_proposals_path, id: "map", title: t("shared.tags_cloud.districts_list") do %> - <%= image_tag(image_path_for("map.jpg", alt: t("shared.tags_cloud.districts_list")) %> + <%= image_tag(image_path_for("map.jpg", alt: t("shared.tags_cloud.districts_list"))) %> <% end %> diff --git a/app/views/stats/index.json.erb b/app/views/stats/index.json.erb index f997b4f31..c2c27702f 100644 --- a/app/views/stats/index.json.erb +++ b/app/views/stats/index.json.erb @@ -1,6 +1,6 @@ { "<%= t("stats.index.visits") %>" : "<%= number_with_delimiter(@visits) %>", - "<%= t("stats.index.debates") %>" : <%= number_with_delimiter(@debates) %>", + "<%= t("stats.index.debates") %>" : "<%= number_with_delimiter(@debates) %>", "<%= t("stats.index.proposals") %>" : "<%= number_with_delimiter(@proposals) %>", "<%= t("stats.index.comments") %>" : "<%= number_with_delimiter(@comments) %>", "<%= t("stats.index.proposal_votes") %>" : "<%= number_with_delimiter(@proposal_votes) %>", @@ -9,4 +9,4 @@ "<%= t("stats.index.votes") %>" : "<%= number_with_delimiter(@votes) %>", "<%= t("stats.index.verified_users") %>" : "<%= number_with_delimiter(@verified_users) %>", "<%= t("stats.index.unverified_users") %>" : "<%= number_with_delimiter(@unverified_users) %>" -} \ No newline at end of file +} diff --git a/config/environments/production.rb b/config/environments/production.rb index 2972eb78f..49ffa3202 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -48,6 +48,7 @@ Rails.application.configure do # config.action_cable.url = 'wss://example.com/cable' # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. config.force_ssl = true diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index c4c4b55e6..62617119d 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -131,9 +131,9 @@ ignore_unused: - "date.order" - "unauthorized.*" - "admin.officials.level_*" - - "admin.comments.index.filter*" + - "admin.hidden_comments.index.filter*" - "admin.banners.index.filters.*" - - "admin.debates.index.filter*" + - "admin.hidden_debates.index.filter*" - "admin.hidden_proposals.index.filter*" - "admin.proposal_notifications.index.filter*" - "admin.budgets.index.filter*" @@ -149,7 +149,7 @@ ignore_unused: - "admin.legislation.processes.proposals.select_order" - "admin.legislation.draft_versions.*.submit_button" - "admin.legislation.questions.*.submit_button" - - "admin.comments.index.hidden_*" + - "admin.hidden_comments.index.hidden_*" - "admin.settings.index.features.*" - "admin.polls.*.submit_button" - "admin.booths.*.submit_button" diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index c03001722..2263fe734 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -355,7 +355,7 @@ en: notice: "Progress bar updated successfully" delete: notice: "Progress bar deleted successfully" - comments: + hidden_comments: index: filter: Filter filters: @@ -430,7 +430,7 @@ en: request: Requested resource update: success: The task has been marked as solved. - debates: + hidden_debates: index: filter: Filter filters: diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 0e6a91fc0..492c0b15b 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -355,7 +355,7 @@ es: notice: "Barra de progreso actualizada" delete: notice: "Barra de progreso eliminada correctamente" - comments: + hidden_comments: index: filter: Filtro filters: @@ -430,7 +430,7 @@ es: request: Recurso solicitado update: success: La tarea ha sido marcada como resuelta - debates: + hidden_debates: index: filter: Filtro filters: diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 917f1447c..40a5765a0 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -22,7 +22,7 @@ namespace :admin do end end - resources :debates, only: :index do + resources :hidden_debates, only: :index do member do put :restore put :confirm_hide @@ -83,7 +83,7 @@ namespace :admin do collection { get :search } end - resources :comments, only: :index do + resources :hidden_comments, only: :index do member do put :restore put :confirm_hide diff --git a/spec/factories/polls.rb b/spec/factories/polls.rb index b7a532d82..7b49d3f5a 100644 --- a/spec/factories/polls.rb +++ b/spec/factories/polls.rb @@ -94,7 +94,6 @@ FactoryBot.define do transient { budget nil } poll { budget&.poll || association(:poll, budget: budget) } - trait :from_web do origin "web" token SecureRandom.hex(32) diff --git a/spec/features/admin/activity_spec.rb b/spec/features/admin/activity_spec.rb index 113062427..891e59947 100644 --- a/spec/features/admin/activity_spec.rb +++ b/spec/features/admin/activity_spec.rb @@ -117,7 +117,7 @@ describe "Admin activity" do scenario "Shows admin restores" do debate = create(:debate, :hidden) - visit admin_debates_path + visit admin_hidden_debates_path within("#debate_#{debate.id}") do click_on "Restore" @@ -181,7 +181,7 @@ describe "Admin activity" do scenario "Shows admin restores" do comment = create(:comment, :hidden) - visit admin_comments_path + visit admin_hidden_comments_path within("#comment_#{comment.id}") do click_on "Restore" diff --git a/spec/features/admin/comments_spec.rb b/spec/features/admin/comments_spec.rb index 5953e5252..7806df96e 100644 --- a/spec/features/admin/comments_spec.rb +++ b/spec/features/admin/comments_spec.rb @@ -12,7 +12,7 @@ describe "Admin comments" do proposal = create(:proposal, author: comment.author) create(:comment, commentable: proposal, user: comment.author, body: "Good Proposal!") - visit admin_comments_path + visit admin_hidden_comments_path expect(page).to have_content("SPAM from SPAMMER") expect(page).not_to have_content("Good Proposal!") @@ -21,7 +21,7 @@ describe "Admin comments" do click_link "Hide author" end - visit admin_comments_path + visit admin_hidden_comments_path expect(page).not_to have_content("SPAM from SPAMMER") expect(page).not_to have_content("Good Proposal!") end @@ -32,7 +32,7 @@ describe "Admin comments" do create(:comment, :hidden, commentable: debate, body: "This is SPAM comment on debate") create(:comment, :hidden, commentable: proposal, body: "This is SPAM comment on proposal") - visit admin_comments_path + visit admin_hidden_comments_path expect(page).to have_content("Debate with spam comment") expect(page).to have_content("Proposal with spam comment") @@ -43,7 +43,7 @@ describe "Admin comments" do expect(page).to have_content("Debate with spam comment") expect(page).not_to have_content("This is SPAM comment on debate") - visit admin_comments_path + visit admin_hidden_comments_path click_link "Proposal with spam comment" expect(page).to have_content("Proposal with spam comment") @@ -56,7 +56,7 @@ describe "Admin comments" do create(:comment, :hidden, commentable: debate, body: "This is SPAM comment on debate") create(:comment, :hidden, commentable: proposal, body: "This is SPAM comment on proposal") - visit admin_comments_path + visit admin_hidden_comments_path expect(page).to have_content("(Hidden proposal: Hidden proposal title)") expect(page).to have_content("(Hidden debate: Hidden debate title)") @@ -67,7 +67,7 @@ describe "Admin comments" do scenario "Restore" do comment = create(:comment, :hidden, body: "Not really SPAM") - visit admin_comments_path + visit admin_hidden_comments_path click_link "Restore" @@ -79,7 +79,7 @@ describe "Admin comments" do scenario "Confirm hide" do comment = create(:comment, :hidden, body: "SPAM") - visit admin_comments_path + visit admin_hidden_comments_path click_link "Confirm moderation" @@ -91,22 +91,22 @@ describe "Admin comments" do end scenario "Current filter is properly highlighted" do - visit admin_comments_path + visit admin_hidden_comments_path expect(page).not_to have_link("Pending") expect(page).to have_link("All") expect(page).to have_link("Confirmed") - visit admin_comments_path(filter: "Pending") + visit admin_hidden_comments_path(filter: "Pending") expect(page).not_to have_link("Pending") expect(page).to have_link("All") expect(page).to have_link("Confirmed") - visit admin_comments_path(filter: "all") + visit admin_hidden_comments_path(filter: "all") expect(page).to have_link("Pending") expect(page).not_to have_link("All") expect(page).to have_link("Confirmed") - visit admin_comments_path(filter: "with_confirmed_hide") + visit admin_hidden_comments_path(filter: "with_confirmed_hide") expect(page).to have_link("Pending") expect(page).to have_link("All") expect(page).not_to have_link("Confirmed") @@ -116,11 +116,11 @@ describe "Admin comments" do create(:comment, :hidden, body: "Unconfirmed comment") create(:comment, :hidden, :with_confirmed_hide, body: "Confirmed comment") - visit admin_comments_path(filter: "all") + visit admin_hidden_comments_path(filter: "all") expect(page).to have_content("Unconfirmed comment") expect(page).to have_content("Confirmed comment") - visit admin_comments_path(filter: "with_confirmed_hide") + visit admin_hidden_comments_path(filter: "with_confirmed_hide") expect(page).not_to have_content("Unconfirmed comment") expect(page).to have_content("Confirmed comment") end @@ -129,7 +129,7 @@ describe "Admin comments" do per_page = Kaminari.config.default_per_page (per_page + 2).times { create(:comment, :hidden, :with_confirmed_hide) } - visit admin_comments_path(filter: "with_confirmed_hide", page: 2) + visit admin_hidden_comments_path(filter: "with_confirmed_hide", page: 2) click_on("Restore", match: :first, exact: true) @@ -137,4 +137,4 @@ describe "Admin comments" do expect(current_url).to include("page=2") end -end \ No newline at end of file +end diff --git a/spec/features/admin/debates_spec.rb b/spec/features/admin/debates_spec.rb index 009e1900e..2b3e683c0 100644 --- a/spec/features/admin/debates_spec.rb +++ b/spec/features/admin/debates_spec.rb @@ -7,7 +7,7 @@ describe "Admin debates" do admin = create(:administrator) login_as(admin.user) - expect{ visit admin_debates_path }.to raise_exception(FeatureFlags::FeatureDisabled) + expect{ visit admin_hidden_debates_path }.to raise_exception(FeatureFlags::FeatureDisabled) Setting["process.debates"] = true end @@ -19,7 +19,7 @@ describe "Admin debates" do scenario "Restore" do debate = create(:debate, :hidden) - visit admin_debates_path + visit admin_hidden_debates_path click_link "Restore" @@ -31,7 +31,7 @@ describe "Admin debates" do scenario "Confirm hide" do debate = create(:debate, :hidden) - visit admin_debates_path + visit admin_hidden_debates_path click_link "Confirm moderation" @@ -43,22 +43,22 @@ describe "Admin debates" do end scenario "Current filter is properly highlighted" do - visit admin_debates_path + visit admin_hidden_debates_path expect(page).not_to have_link("Pending") expect(page).to have_link("All") expect(page).to have_link("Confirmed") - visit admin_debates_path(filter: "Pending") + visit admin_hidden_debates_path(filter: "Pending") expect(page).not_to have_link("Pending") expect(page).to have_link("All") expect(page).to have_link("Confirmed") - visit admin_debates_path(filter: "all") + visit admin_hidden_debates_path(filter: "all") expect(page).to have_link("Pending") expect(page).not_to have_link("All") expect(page).to have_link("Confirmed") - visit admin_debates_path(filter: "with_confirmed_hide") + visit admin_hidden_debates_path(filter: "with_confirmed_hide") expect(page).to have_link("All") expect(page).to have_link("Pending") expect(page).not_to have_link("Confirmed") @@ -68,15 +68,15 @@ describe "Admin debates" do create(:debate, :hidden, title: "Unconfirmed debate") create(:debate, :hidden, :with_confirmed_hide, title: "Confirmed debate") - visit admin_debates_path(filter: "pending") + visit admin_hidden_debates_path(filter: "pending") expect(page).to have_content("Unconfirmed debate") expect(page).not_to have_content("Confirmed debate") - visit admin_debates_path(filter: "all") + visit admin_hidden_debates_path(filter: "all") expect(page).to have_content("Unconfirmed debate") expect(page).to have_content("Confirmed debate") - visit admin_debates_path(filter: "with_confirmed_hide") + visit admin_hidden_debates_path(filter: "with_confirmed_hide") expect(page).not_to have_content("Unconfirmed debate") expect(page).to have_content("Confirmed debate") end @@ -85,7 +85,7 @@ describe "Admin debates" do per_page = Kaminari.config.default_per_page (per_page + 2).times { create(:debate, :hidden, :with_confirmed_hide) } - visit admin_debates_path(filter: "with_confirmed_hide", page: 2) + visit admin_hidden_debates_path(filter: "with_confirmed_hide", page: 2) click_on("Restore", match: :first, exact: true) From 0d761ddfb1f579b531aa4579f9c061f3cd93c7d0 Mon Sep 17 00:00:00 2001 From: Bertocq Date: Thu, 13 Jul 2017 19:29:30 +0200 Subject: [PATCH 14/55] Hotfixing find the only budget for emails --- app/controllers/budgets/results_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/budgets/results_controller.rb b/app/controllers/budgets/results_controller.rb index e8e80a234..e44a38cd9 100644 --- a/app/controllers/budgets/results_controller.rb +++ b/app/controllers/budgets/results_controller.rb @@ -14,7 +14,7 @@ module Budgets private def load_budget - @budget = Budget.find_by(id: params[:budget_id]) + @budget = Budget.find_by(id: params[:budget_id]) || Budget.first end def load_heading From 998b4d9e39726ecb491a426aa0a5fd71a76b5281 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Sun, 15 Jan 2017 21:36:03 +0100 Subject: [PATCH 15/55] Load budgets using slugs --- app/controllers/admin/budgets_controller.rb | 5 ++ .../budgets/ballot/lines_controller.rb | 2 +- app/controllers/budgets/ballots_controller.rb | 5 ++ app/controllers/budgets/groups_controller.rb | 11 ++++ .../budgets/investments_controller.rb | 7 ++- app/controllers/budgets_controller.rb | 7 +++ .../budgets/investments_controller.rb | 5 ++ .../budget_investments_controller.rb | 2 +- spec/features/budgets/investments_spec.rb | 56 +++++++++---------- 9 files changed, 69 insertions(+), 31 deletions(-) diff --git a/app/controllers/admin/budgets_controller.rb b/app/controllers/admin/budgets_controller.rb index 771a8c91d..0aa868449 100644 --- a/app/controllers/admin/budgets_controller.rb +++ b/app/controllers/admin/budgets_controller.rb @@ -6,6 +6,7 @@ class Admin::BudgetsController < Admin::BaseController has_filters %w{open finished}, only: :index + before_action :load_budget load_and_authorize_resource def index @@ -66,4 +67,8 @@ class Admin::BudgetsController < Admin::BaseController params.require(:budget).permit(*valid_attributes, *report_attributes, translation_params(Budget)) end + def load_budget + @budget = Budget.find_by(slug: params[:id]) || Budget.find_by(id: params[:id]) + end + end diff --git a/app/controllers/budgets/ballot/lines_controller.rb b/app/controllers/budgets/ballot/lines_controller.rb index c2e617e08..67b53535e 100644 --- a/app/controllers/budgets/ballot/lines_controller.rb +++ b/app/controllers/budgets/ballot/lines_controller.rb @@ -37,7 +37,7 @@ module Budgets end def load_budget - @budget = Budget.find(params[:budget_id]) + @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) end def load_ballot diff --git a/app/controllers/budgets/ballots_controller.rb b/app/controllers/budgets/ballots_controller.rb index b5b63b4aa..501ad7b24 100644 --- a/app/controllers/budgets/ballots_controller.rb +++ b/app/controllers/budgets/ballots_controller.rb @@ -1,6 +1,7 @@ module Budgets class BallotsController < ApplicationController before_action :authenticate_user! + before_action :load_budget load_and_authorize_resource :budget before_action :load_ballot after_action :store_referer, only: [:show] @@ -13,6 +14,10 @@ module Budgets private + def load_budget + @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + end + def load_ballot query = Budget::Ballot.where(user: current_user, budget: @budget) @ballot = @budget.balloting? ? query.first_or_create : query.first_or_initialize diff --git a/app/controllers/budgets/groups_controller.rb b/app/controllers/budgets/groups_controller.rb index f298b5a93..9c15eef28 100644 --- a/app/controllers/budgets/groups_controller.rb +++ b/app/controllers/budgets/groups_controller.rb @@ -1,5 +1,7 @@ module Budgets class GroupsController < ApplicationController + before_action :load_budget + before_action :load_group load_and_authorize_resource :budget load_and_authorize_resource :group, class: "Budget::Group" @@ -9,5 +11,14 @@ module Budgets def show end + private + + def load_budget + @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + end + + def load_group + @group = Budget::Group.find_by(slug: params[:id]) || Budget.find_by(id: params[:id]) + end end end \ No newline at end of file diff --git a/app/controllers/budgets/investments_controller.rb b/app/controllers/budgets/investments_controller.rb index cc762cff5..0280129af 100644 --- a/app/controllers/budgets/investments_controller.rb +++ b/app/controllers/budgets/investments_controller.rb @@ -10,6 +10,7 @@ module Budgets PER_PAGE = 10 before_action :authenticate_user!, except: [:index, :show, :json_data] + before_action :load_budget, except: :json_data load_and_authorize_resource :budget, except: :json_data load_and_authorize_resource :investment, through: :budget, class: "Budget::Investment", @@ -136,7 +137,7 @@ module Budgets def load_heading if params[:heading_id].present? - @heading = @budget.headings.find(params[:heading_id]) + @heading = @budget.headings.find_by(slug: params[:heading_id]) || @budget.headings.find_by(id: params[:heading_id]) @assigned_heading = @ballot.try(:heading_for_group, @heading.try(:group)) load_map end @@ -154,6 +155,10 @@ module Budgets TagCloud.new(Budget::Investment, params[:search]) end + def load_budget + @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + end + def set_view @view = (params[:view] == "minimal") ? "minimal" : "default" end diff --git a/app/controllers/budgets_controller.rb b/app/controllers/budgets_controller.rb index c1f9e03ef..355184897 100644 --- a/app/controllers/budgets_controller.rb +++ b/app/controllers/budgets_controller.rb @@ -3,6 +3,7 @@ class BudgetsController < ApplicationController include BudgetsHelper feature_flag :budgets + before_action :load_budget, only: :show load_and_authorize_resource before_action :set_default_budget_filter, only: :show has_filters %w[not_unfeasible feasible unfeasible unselected selected winners], only: :show @@ -19,4 +20,10 @@ class BudgetsController < ApplicationController @banners = Banner.in_section("budgets").with_active end + private + + def load_budget + @budget = Budget.find_by(slug: params[:id]) || Budget.find_by(id: params[:id]) + end + end diff --git a/app/controllers/management/budgets/investments_controller.rb b/app/controllers/management/budgets/investments_controller.rb index 0b30820a2..aba7f0724 100644 --- a/app/controllers/management/budgets/investments_controller.rb +++ b/app/controllers/management/budgets/investments_controller.rb @@ -1,4 +1,5 @@ class Management::Budgets::InvestmentsController < Management::BaseController + before_action :load_budget load_resource :budget load_resource :investment, through: :budget, class: "Budget::Investment" @@ -60,6 +61,10 @@ class Management::Budgets::InvestmentsController < Management::BaseController check_verified_user t("management.budget_investments.alert.unverified_user") end + def load_budget + @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + end + def load_categories @categories = ActsAsTaggableOn::Tag.category.order(:name) end diff --git a/app/controllers/valuation/budget_investments_controller.rb b/app/controllers/valuation/budget_investments_controller.rb index aa29120f1..a024c1760 100644 --- a/app/controllers/valuation/budget_investments_controller.rb +++ b/app/controllers/valuation/budget_investments_controller.rb @@ -65,7 +65,7 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController end def load_budget - @budget = Budget.find(params[:budget_id]) + @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) end def load_investment diff --git a/spec/features/budgets/investments_spec.rb b/spec/features/budgets/investments_spec.rb index eece48251..6d84ed03a 100644 --- a/spec/features/budgets/investments_spec.rb +++ b/spec/features/budgets/investments_spec.rb @@ -40,7 +40,7 @@ describe "Budget Investments" do investments.each do |investment| within("#budget-investments") do expect(page).to have_content investment.title - expect(page).to have_css("a[href='#{budget_investment_path(budget_id: budget.id, id: investment.id)}']", text: investment.title) + expect(page).to have_css("a[href='#{budget_investment_path(budget, id: investment.id)}']", text: investment.title) expect(page).not_to have_content(unfeasible_investment.title) end end @@ -476,7 +476,7 @@ describe "Budget Investments" do investment3 = create(:budget_investment, heading: heading) investment4 = create(:budget_investment, :feasible, heading: heading) - visit budget_investments_path(budget_id: budget.id, heading_id: heading.id, filter: "unfeasible") + visit budget_investments_path(budget, heading_id: heading.id, filter: "unfeasible") within("#budget-investments") do expect(page).to have_css(".budget-investment", count: 1) @@ -810,7 +810,7 @@ describe "Budget Investments" do scenario "Create with invisible_captcha honeypot field" do login_as(author) - visit new_budget_investment_path(budget_id: budget.id) + visit new_budget_investment_path(budget) select heading.name, from: "budget_investment_heading_id" fill_in "budget_investment_title", with: "I am a bot" @@ -822,14 +822,14 @@ describe "Budget Investments" do expect(page.status_code).to eq(200) expect(page.html).to be_empty - expect(page).to have_current_path(budget_investments_path(budget_id: budget.id)) + expect(page).to have_current_path(budget_investments_path(budget)) end scenario "Create budget investment too fast" do allow(InvisibleCaptcha).to receive(:timestamp_threshold).and_return(Float::INFINITY) login_as(author) - visit new_budget_investment_path(budget_id: budget.id) + visit new_budget_investment_path(budget) select heading.name, from: "budget_investment_heading_id" fill_in "budget_investment_title", with: "I am a bot" @@ -839,13 +839,13 @@ describe "Budget Investments" do click_button "Create Investment" expect(page).to have_content "Sorry, that was too quick! Please resubmit" - expect(page).to have_current_path(new_budget_investment_path(budget_id: budget.id)) + expect(page).to have_current_path(new_budget_investment_path(budget)) end scenario "Create" do login_as(author) - visit new_budget_investment_path(budget_id: budget.id) + visit new_budget_investment_path(budget) select heading.name, from: "budget_investment_heading_id" fill_in "budget_investment_title", with: "Build a skyscraper" @@ -872,7 +872,7 @@ describe "Budget Investments" do scenario "Errors on create" do login_as(author) - visit new_budget_investment_path(budget_id: budget.id) + visit new_budget_investment_path(budget) click_button "Create Investment" expect(page).to have_content error_message end @@ -948,7 +948,7 @@ describe "Budget Investments" do login_as(author) - visit new_budget_investment_path(budget_id: budget.id) + visit new_budget_investment_path(budget) select_options = find("#budget_investment_heading_id").all("option").collect(&:text) expect(select_options.first).to eq("") @@ -964,7 +964,7 @@ describe "Budget Investments" do investment = create(:budget_investment, heading: heading) - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).to have_content(investment.title) expect(page).to have_content(investment.description) @@ -984,7 +984,7 @@ describe "Budget Investments" do scenario "Price & explanation is shown when Budget is on published prices phase" do Budget::Phase::PUBLISHED_PRICES_PHASES.each do |phase| budget.update(phase: phase) - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).to have_content(investment.formatted_price) expect(page).to have_content(investment.price_explanation) @@ -1003,7 +1003,7 @@ describe "Budget Investments" do scenario "Price & explanation isn't shown when Budget is not on published prices phase" do (Budget::Phase::PHASE_KINDS - Budget::Phase::PUBLISHED_PRICES_PHASES).each do |phase| budget.update(phase: phase) - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).not_to have_content(investment.formatted_price) expect(page).not_to have_content(investment.price_explanation) @@ -1025,7 +1025,7 @@ describe "Budget Investments" do scenario "Price & explanation isn't shown for any Budget's phase" do Budget::Phase::PHASE_KINDS.each do |phase| budget.update(phase: phase) - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).not_to have_content(investment.formatted_price) expect(page).not_to have_content(investment.price_explanation) @@ -1044,7 +1044,7 @@ describe "Budget Investments" do Setting["feature.community"] = true investment = create(:budget_investment, heading: heading) - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).to have_content "Access the community" Setting["feature.community"] = false @@ -1054,14 +1054,14 @@ describe "Budget Investments" do Setting["feature.community"] = false investment = create(:budget_investment, heading: heading) - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).not_to have_content "Access the community" end scenario "Don't display flaggable buttons" do investment = create(:budget_investment, heading: heading) - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).not_to have_selector ".js-follow" end @@ -1092,7 +1092,7 @@ describe "Budget Investments" do scenario "Budget in selecting phase" do budget.update(phase: "selecting") - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).not_to have_content("Unfeasibility explanation") expect(page).not_to have_content("Price explanation") @@ -1120,14 +1120,14 @@ describe "Budget Investments" do heading: heading, unfeasibility_explanation: "The unfeasible explanation") - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).not_to have_content("Unfeasibility explanation") expect(page).not_to have_content("Local government is not competent in this") expect(page).not_to have_content("This investment project has been marked as not feasible "\ "and will not go to balloting phase") - visit budget_investment_path(budget_id: budget.id, id: investment_2.id) + visit budget_investment_path(budget, id: investment_2.id) expect(page).to have_content("Unfeasibility explanation") expect(page).to have_content("The unfeasible explanation") @@ -1147,7 +1147,7 @@ describe "Budget Investments" do group: group, heading: heading) - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).to have_content("This investment project has been selected for balloting phase") end @@ -1166,13 +1166,13 @@ describe "Budget Investments" do group: group, heading: heading) - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).not_to have_content("Winning investment project") budget.update(phase: "finished") - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).to have_content("Winning investment project") end @@ -1189,7 +1189,7 @@ describe "Budget Investments" do group: group, heading: heading) - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).to have_content("This investment project has not been selected for balloting phase") end @@ -1205,7 +1205,7 @@ describe "Budget Investments" do group: group, heading: heading) - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) within("aside") do expect(page).to have_content("Investment project") @@ -1225,7 +1225,7 @@ describe "Budget Investments" do heading: heading, unfeasibility_explanation: "Local government is not competent in this matter") - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).not_to have_content("Unfeasibility explanation") expect(page).not_to have_content("Local government is not competent in this matter") @@ -1243,7 +1243,7 @@ describe "Budget Investments" do heading: heading, unfeasibility_explanation: "Local government is not competent in this matter") - visit budget_investment_path(budget_id: budget.id, id: investment.id) + visit budget_investment_path(budget, id: investment.id) expect(page).not_to have_content("Unfeasibility explanation") expect(page).not_to have_content("Local government is not competent in this matter") @@ -1647,7 +1647,7 @@ describe "Budget Investments" do investment3 = create(:budget_investment, :selected, :feasible, heading: heading, valuation_finished: true) investment4 = create(:budget_investment, :selected, :feasible, heading: heading, valuation_finished: true) - visit budget_investments_path(budget_id: budget.id, heading_id: heading.id, filter: "unselected") + visit budget_investments_path(budget, heading_id: heading.id, filter: "unselected") within("#budget-investments") do expect(page).to have_css(".budget-investment", count: 1) @@ -1691,7 +1691,7 @@ describe "Budget Investments" do scenario "Do not display vote button for unselected investments in index" do investment = create(:budget_investment, :unselected, heading: heading) - visit budget_investments_path(budget_id: budget.id, heading_id: heading.id, filter: "unselected") + visit budget_investments_path(budget, heading_id: heading.id, filter: "unselected") expect(page).to have_content investment.title expect(page).not_to have_link("Vote") From 4bd20eebf4f9e232ef1e55fc85ce1ffb9bcbb90b Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Thu, 17 Jan 2019 10:28:22 +0100 Subject: [PATCH 16/55] Use correct param in controller There were some custom routes created using the param[:id] but the Rails routes use the param[:budget_id] by default, so the same controller could be asked for different param keys. --- app/controllers/budgets/results_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/budgets/results_controller.rb b/app/controllers/budgets/results_controller.rb index e44a38cd9..36868b07c 100644 --- a/app/controllers/budgets/results_controller.rb +++ b/app/controllers/budgets/results_controller.rb @@ -14,7 +14,9 @@ module Budgets private def load_budget - @budget = Budget.find_by(id: params[:budget_id]) || Budget.first + @budget = Budget.find_by(slug: params[:budget_id]) + @budget ||= Budget.find_by(id: params[:budget_id]) + @budget ||= Budget.first end def load_heading From 22076dd95c1dfab462119bb028f11e116e9a6b1b Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Thu, 17 Jan 2019 10:42:15 +0100 Subject: [PATCH 17/55] Add method find_by_slug_or_id! to Sluggable module Make it easier to find by slug or id for sluggable models. It will raise a 404 HTML Not found error if the resource is not found. --- app/models/concerns/sluggable.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/concerns/sluggable.rb b/app/models/concerns/sluggable.rb index 8fb308d22..bdcdff856 100644 --- a/app/models/concerns/sluggable.rb +++ b/app/models/concerns/sluggable.rb @@ -7,6 +7,10 @@ module Sluggable def self.find_by_slug_or_id(slug_or_id) find_by_slug(slug_or_id) || find_by_id(slug_or_id) end + + def self.find_by_slug_or_id!(slug_or_id) + find_by_slug(slug_or_id) || find(slug_or_id) + end end def generate_slug From b122302c5884bb0b3a73fb624530e06428111ddd Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Thu, 17 Jan 2019 10:47:54 +0100 Subject: [PATCH 18/55] Use find instead of find_by_id Better raise a 404 HTML NotFound exception than any other unexpected error. --- .../admin/budget_groups_controller.rb | 4 +- .../admin/budget_headings_controller.rb | 6 +-- .../admin/budget_investments_controller.rb | 2 +- app/controllers/admin/budgets_controller.rb | 4 +- .../budgets/ballot/lines_controller.rb | 2 +- app/controllers/budgets/ballots_controller.rb | 2 +- app/controllers/budgets/groups_controller.rb | 4 +- .../budgets/investments_controller.rb | 4 +- app/controllers/budgets/results_controller.rb | 13 ++--- app/controllers/budgets/stats_controller.rb | 2 +- app/controllers/budgets_controller.rb | 2 +- .../budgets/investments_controller.rb | 2 +- .../budget_investments_controller.rb | 2 +- app/models/budget/investment.rb | 2 +- .../budgets/ballots/lines_controller_spec.rb | 25 ++++++++++ spec/features/admin/budget_groups_spec.rb | 37 ++++++++++++++ spec/features/admin/budget_headings_spec.rb | 50 +++++++++++++++++++ .../features/admin/budget_investments_spec.rb | 28 +++++++++++ spec/features/admin/budgets_spec.rb | 23 +++++++++ spec/features/budgets/ballots_spec.rb | 49 ++++++++++++++++++ spec/features/budgets/budgets_spec.rb | 24 +++++++++ spec/features/budgets/executions_spec.rb | 16 ++++++ spec/features/budgets/groups_spec.rb | 41 ++++++++++++++- spec/features/budgets/investments_spec.rb | 47 +++++++++++++++++ spec/features/budgets/results_spec.rb | 24 +++++++++ spec/features/budgets/stats_spec.rb | 28 +++++++++-- .../management/budget_investments_spec.rb | 29 ++++++++++- .../valuation/budget_investments_spec.rb | 24 +++++++++ spec/models/budget/investment_spec.rb | 24 +++++++++ 29 files changed, 487 insertions(+), 33 deletions(-) create mode 100644 spec/controllers/budgets/ballots/lines_controller_spec.rb diff --git a/app/controllers/admin/budget_groups_controller.rb b/app/controllers/admin/budget_groups_controller.rb index aa87e2373..8e44b2101 100644 --- a/app/controllers/admin/budget_groups_controller.rb +++ b/app/controllers/admin/budget_groups_controller.rb @@ -46,11 +46,11 @@ class Admin::BudgetGroupsController < Admin::BaseController private def load_budget - @budget = Budget.includes(:groups).find(params[:budget_id]) + @budget = Budget.find_by_slug_or_id! params[:budget_id] end def load_group - @group = @budget.groups.find(params[:id]) + @group = @budget.groups.find_by_slug_or_id! params[:id] end def groups_index diff --git a/app/controllers/admin/budget_headings_controller.rb b/app/controllers/admin/budget_headings_controller.rb index 46e46bc62..659bf6760 100644 --- a/app/controllers/admin/budget_headings_controller.rb +++ b/app/controllers/admin/budget_headings_controller.rb @@ -47,15 +47,15 @@ class Admin::BudgetHeadingsController < Admin::BaseController private def load_budget - @budget = Budget.includes(:groups).find(params[:budget_id]) + @budget = Budget.find_by_slug_or_id! params[:budget_id] end def load_group - @group = @budget.groups.find(params[:group_id]) + @group = @budget.groups.find_by_slug_or_id! params[:group_id] end def load_heading - @heading = @group.headings.find(params[:id]) + @heading = @group.headings.find_by_slug_or_id! params[:id] end def headings_index diff --git a/app/controllers/admin/budget_investments_controller.rb b/app/controllers/admin/budget_investments_controller.rb index 0ab4a4765..fa4608bbc 100644 --- a/app/controllers/admin/budget_investments_controller.rb +++ b/app/controllers/admin/budget_investments_controller.rb @@ -87,7 +87,7 @@ class Admin::BudgetInvestmentsController < Admin::BaseController end def load_budget - @budget = Budget.includes(:groups).find(params[:budget_id]) + @budget = Budget.find_by_slug_or_id! params[:budget_id] end def load_investment diff --git a/app/controllers/admin/budgets_controller.rb b/app/controllers/admin/budgets_controller.rb index 0aa868449..fa9ad6752 100644 --- a/app/controllers/admin/budgets_controller.rb +++ b/app/controllers/admin/budgets_controller.rb @@ -6,7 +6,7 @@ class Admin::BudgetsController < Admin::BaseController has_filters %w{open finished}, only: :index - before_action :load_budget + before_action :load_budget, except: [:index, :new, :create] load_and_authorize_resource def index @@ -68,7 +68,7 @@ class Admin::BudgetsController < Admin::BaseController end def load_budget - @budget = Budget.find_by(slug: params[:id]) || Budget.find_by(id: params[:id]) + @budget = Budget.find_by_slug_or_id! params[:id] end end diff --git a/app/controllers/budgets/ballot/lines_controller.rb b/app/controllers/budgets/ballot/lines_controller.rb index 67b53535e..5d882c51b 100644 --- a/app/controllers/budgets/ballot/lines_controller.rb +++ b/app/controllers/budgets/ballot/lines_controller.rb @@ -37,7 +37,7 @@ module Budgets end def load_budget - @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + @budget = Budget.find_by_slug_or_id! params[:budget_id] end def load_ballot diff --git a/app/controllers/budgets/ballots_controller.rb b/app/controllers/budgets/ballots_controller.rb index 501ad7b24..91286e2ec 100644 --- a/app/controllers/budgets/ballots_controller.rb +++ b/app/controllers/budgets/ballots_controller.rb @@ -15,7 +15,7 @@ module Budgets private def load_budget - @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + @budget = Budget.find_by_slug_or_id! params[:budget_id] end def load_ballot diff --git a/app/controllers/budgets/groups_controller.rb b/app/controllers/budgets/groups_controller.rb index 9c15eef28..e309a3c8c 100644 --- a/app/controllers/budgets/groups_controller.rb +++ b/app/controllers/budgets/groups_controller.rb @@ -14,11 +14,11 @@ module Budgets private def load_budget - @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + @budget = Budget.find_by_slug_or_id! params[:budget_id] end def load_group - @group = Budget::Group.find_by(slug: params[:id]) || Budget.find_by(id: params[:id]) + @group = @budget.groups.find_by_slug_or_id! params[:id] end end end \ No newline at end of file diff --git a/app/controllers/budgets/investments_controller.rb b/app/controllers/budgets/investments_controller.rb index 0280129af..1aea05085 100644 --- a/app/controllers/budgets/investments_controller.rb +++ b/app/controllers/budgets/investments_controller.rb @@ -137,7 +137,7 @@ module Budgets def load_heading if params[:heading_id].present? - @heading = @budget.headings.find_by(slug: params[:heading_id]) || @budget.headings.find_by(id: params[:heading_id]) + @heading = @budget.headings.find_by_slug_or_id! params[:heading_id] @assigned_heading = @ballot.try(:heading_for_group, @heading.try(:group)) load_map end @@ -156,7 +156,7 @@ module Budgets end def load_budget - @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + @budget = Budget.find_by_slug_or_id! params[:budget_id] end def set_view diff --git a/app/controllers/budgets/results_controller.rb b/app/controllers/budgets/results_controller.rb index 36868b07c..f8d54c2a2 100644 --- a/app/controllers/budgets/results_controller.rb +++ b/app/controllers/budgets/results_controller.rb @@ -14,17 +14,14 @@ module Budgets private def load_budget - @budget = Budget.find_by(slug: params[:budget_id]) - @budget ||= Budget.find_by(id: params[:budget_id]) - @budget ||= Budget.first + @budget = Budget.find_by_slug_or_id(params[:budget_id]) || Budget.first end def load_heading - @heading = if params[:heading_id].present? - @budget.headings.find(params[:heading_id]) - else - @budget.headings.first - end + if @budget.present? + headings = @budget.headings + @heading = headings.find_by_slug_or_id(params[:heading_id]) || headings.first + end end end diff --git a/app/controllers/budgets/stats_controller.rb b/app/controllers/budgets/stats_controller.rb index d8ac1e267..964974125 100644 --- a/app/controllers/budgets/stats_controller.rb +++ b/app/controllers/budgets/stats_controller.rb @@ -13,7 +13,7 @@ module Budgets private def load_budget - @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + @budget = Budget.find_by_slug_or_id! params[:budget_id] end end diff --git a/app/controllers/budgets_controller.rb b/app/controllers/budgets_controller.rb index 355184897..19f1e69be 100644 --- a/app/controllers/budgets_controller.rb +++ b/app/controllers/budgets_controller.rb @@ -23,7 +23,7 @@ class BudgetsController < ApplicationController private def load_budget - @budget = Budget.find_by(slug: params[:id]) || Budget.find_by(id: params[:id]) + @budget = Budget.find_by_slug_or_id! params[:id] end end diff --git a/app/controllers/management/budgets/investments_controller.rb b/app/controllers/management/budgets/investments_controller.rb index aba7f0724..79a65cf15 100644 --- a/app/controllers/management/budgets/investments_controller.rb +++ b/app/controllers/management/budgets/investments_controller.rb @@ -62,7 +62,7 @@ class Management::Budgets::InvestmentsController < Management::BaseController end def load_budget - @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + @budget = Budget.find_by_slug_or_id! params[:budget_id] end def load_categories diff --git a/app/controllers/valuation/budget_investments_controller.rb b/app/controllers/valuation/budget_investments_controller.rb index a024c1760..afe4ea6b2 100644 --- a/app/controllers/valuation/budget_investments_controller.rb +++ b/app/controllers/valuation/budget_investments_controller.rb @@ -65,7 +65,7 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController end def load_budget - @budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + @budget = Budget.find_by_slug_or_id! params[:budget_id] end def load_investment diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index 2eaf801c1..59b88f8d5 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -113,7 +113,7 @@ class Budget end def self.scoped_filter(params, current_filter) - budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) + budget = Budget.find_by_slug_or_id params[:budget_id] results = Investment.by_budget(budget) results = results.where("cached_votes_up + physical_votes >= ?", diff --git a/spec/controllers/budgets/ballots/lines_controller_spec.rb b/spec/controllers/budgets/ballots/lines_controller_spec.rb new file mode 100644 index 000000000..641a90a95 --- /dev/null +++ b/spec/controllers/budgets/ballots/lines_controller_spec.rb @@ -0,0 +1,25 @@ +require "rails_helper" + +describe Budgets::Ballot::LinesController do + + describe "#load_budget" do + + it "raises an error if budget slug is not found" do + controller.params[:budget_id] = "wrong_budget" + + expect do + controller.send(:load_budget) + end.to raise_error ActiveRecord::RecordNotFound + end + + it "raises an error if budget id is not found" do + controller.params[:budget_id] = 0 + + expect do + controller.send(:load_budget) + end.to raise_error ActiveRecord::RecordNotFound + end + + end + +end diff --git a/spec/features/admin/budget_groups_spec.rb b/spec/features/admin/budget_groups_spec.rb index e0b7e3132..cc0c75d62 100644 --- a/spec/features/admin/budget_groups_spec.rb +++ b/spec/features/admin/budget_groups_spec.rb @@ -32,6 +32,43 @@ describe "Admin budget groups" do end + context "Load" do + + let!(:budget) { create(:budget, slug: "budget_slug") } + let!(:group) { create(:budget_group, slug: "group_slug", budget: budget) } + + scenario "finds budget and group by slug" do + visit edit_admin_budget_group_path("budget_slug", "group_slug") + expect(page).to have_content(budget.name) + expect(page).to have_field "Group name", with: group.name + end + + scenario "raises an error if budget slug is not found" do + expect do + visit edit_admin_budget_group_path("wrong_budget", group) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if budget id is not found" do + expect do + visit edit_admin_budget_group_path(0, group) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if group slug is not found" do + expect do + visit edit_admin_budget_group_path(budget, "wrong_group") + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if group id is not found" do + expect do + visit edit_admin_budget_group_path(budget, 0) + end.to raise_error ActiveRecord::RecordNotFound + end + + end + context "Index" do scenario "Displaying no groups for budget" do diff --git a/spec/features/admin/budget_headings_spec.rb b/spec/features/admin/budget_headings_spec.rb index b4fd06db7..6b633058d 100644 --- a/spec/features/admin/budget_headings_spec.rb +++ b/spec/features/admin/budget_headings_spec.rb @@ -33,6 +33,56 @@ describe "Admin budget headings" do end + context "Load" do + + let!(:budget) { create(:budget, slug: "budget_slug") } + let!(:group) { create(:budget_group, slug: "group_slug", budget: budget) } + let!(:heading) { create(:budget_heading, slug: "heading_slug", group: group) } + + scenario "finds budget, group and heading by slug" do + visit edit_admin_budget_group_heading_path("budget_slug", "group_slug", "heading_slug") + expect(page).to have_content(budget.name) + expect(page).to have_content(group.name) + expect(page).to have_field "Heading name", with: heading.name + end + + scenario "raises an error if budget slug is not found" do + expect do + visit edit_admin_budget_group_heading_path("wrong_budget", group, heading) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if budget id is not found" do + expect do + visit edit_admin_budget_group_heading_path(0, group, heading) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if group slug is not found" do + expect do + visit edit_admin_budget_group_heading_path(budget, "wrong_group", heading) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if group id is not found" do + expect do + visit edit_admin_budget_group_heading_path(budget, 0, heading) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if heading slug is not found" do + expect do + visit edit_admin_budget_group_heading_path(budget, group, "wrong_heading") + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if heading id is not found" do + expect do + visit edit_admin_budget_group_heading_path(budget, group, 0) + end.to raise_error ActiveRecord::RecordNotFound + end + end + context "Index" do scenario "Displaying no headings for group" do diff --git a/spec/features/admin/budget_investments_spec.rb b/spec/features/admin/budget_investments_spec.rb index 8dd9743d3..dc5b5d6ed 100644 --- a/spec/features/admin/budget_investments_spec.rb +++ b/spec/features/admin/budget_investments_spec.rb @@ -32,6 +32,34 @@ describe "Admin budget investments" do end + context "Load" do + + let(:group) { create(:budget_group, budget: budget) } + let(:heading) { create(:budget_heading, group: group) } + let!(:investment) { create(:budget_investment, heading: heading) } + + before { budget.update(slug: "budget_slug") } + + scenario "finds investments using budget slug" do + visit admin_budget_budget_investments_path("budget_slug") + + expect(page).to have_link investment.title + end + + scenario "raises an error if budget slug is not found" do + expect do + visit admin_budget_budget_investments_path("wrong_budget", investment) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if budget id is not found" do + expect do + visit admin_budget_budget_investments_path(0, investment) + end.to raise_error ActiveRecord::RecordNotFound + end + + end + context "Index" do scenario "Displaying investments" do diff --git a/spec/features/admin/budgets_spec.rb b/spec/features/admin/budgets_spec.rb index e7c99cfb4..912262afc 100644 --- a/spec/features/admin/budgets_spec.rb +++ b/spec/features/admin/budgets_spec.rb @@ -24,6 +24,29 @@ describe "Admin budgets" do end + context "Load" do + + let!(:budget) { create(:budget, slug: "budget_slug") } + + scenario "finds budget by slug" do + visit admin_budget_path("budget_slug") + expect(page).to have_content(budget.name) + end + + scenario "raises an error if budget slug is not found" do + expect do + visit admin_budget_path("wrong_budget") + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if budget id is not found" do + expect do + visit admin_budget_path(0) + end.to raise_error ActiveRecord::RecordNotFound + end + + end + context "Index" do scenario "Displaying no open budgets text" do diff --git a/spec/features/budgets/ballots_spec.rb b/spec/features/budgets/ballots_spec.rb index 0f9bb1c05..707dbf0da 100644 --- a/spec/features/budgets/ballots_spec.rb +++ b/spec/features/budgets/ballots_spec.rb @@ -8,6 +8,55 @@ describe "Ballots" do let!(:california) { create(:budget_heading, group: states, name: "California", price: 1000) } let!(:new_york) { create(:budget_heading, group: states, name: "New York", price: 1000000) } + context "Load" do + + let(:ballot) { create(:budget_ballot, user: user, budget: budget) } + + before do + budget.update(slug: "budget_slug") + ballot.investments << create(:budget_investment, :selected, heading: california) + login_as(user) + end + + scenario "finds ballot using budget slug" do + visit budget_ballot_path("budget_slug") + + expect(page).to have_content("You have voted one investment") + end + + scenario "raises an error if budget slug is not found" do + expect do + visit budget_ballot_path("wrong_budget") + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if budget id is not found" do + expect do + visit budget_ballot_path(0) + end.to raise_error ActiveRecord::RecordNotFound + end + + end + + context "Lines Load" do + + let!(:investment) { create(:budget_investment, :selected, heading: california) } + + before do + create(:budget_ballot, user: user, budget: budget) + budget.update(slug: "budget_slug") + login_as(user) + end + + scenario "finds ballot lines using budget slug", :js do + visit budget_investments_path("budget_slug", states, california) + add_to_ballot(investment) + + within("#sidebar") { expect(page).to have_content investment.title } + end + + end + context "Voting" do before do diff --git a/spec/features/budgets/budgets_spec.rb b/spec/features/budgets/budgets_spec.rb index d6fdf13e0..0f2f0a8bd 100644 --- a/spec/features/budgets/budgets_spec.rb +++ b/spec/features/budgets/budgets_spec.rb @@ -6,6 +6,30 @@ describe "Budgets" do let(:level_two_user) { create(:user, :level_two) } let(:allowed_phase_list) { ["balloting", "reviewing_ballots", "finished"] } + context "Load" do + + before { budget.update(slug: "budget_slug") } + + scenario "finds budget by slug" do + visit budget_path("budget_slug") + + expect(page).to have_content budget.name + end + + scenario "raises an error if budget slug is not found" do + expect do + visit budget_path("wrong_budget") + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if budget id is not found" do + expect do + visit budget_path(0) + end.to raise_error ActiveRecord::RecordNotFound + end + + end + context "Index" do scenario "Show normal index with links" do diff --git a/spec/features/budgets/executions_spec.rb b/spec/features/budgets/executions_spec.rb index dd8966ed9..3083d1eca 100644 --- a/spec/features/budgets/executions_spec.rb +++ b/spec/features/budgets/executions_spec.rb @@ -11,6 +11,22 @@ describe "Executions" do let!(:investment4) { create(:budget_investment, :winner, heading: heading) } let!(:investment3) { create(:budget_investment, :incompatible, heading: heading) } + scenario "finds budget by id or slug" do + budget.update(slug: "budget_slug") + + visit budget_executions_path("budget_slug") + within(".budgets-stats") { expect(page).to have_content budget.name } + + visit budget_executions_path(budget) + within(".budgets-stats") { expect(page).to have_content budget.name } + + visit budget_executions_path("budget_slug") + within(".budgets-stats") { expect(page).to have_content budget.name } + + visit budget_executions_path(budget) + within(".budgets-stats") { expect(page).to have_content budget.name } + end + scenario "only displays investments with milestones" do create(:milestone, milestoneable: investment1) diff --git a/spec/features/budgets/groups_spec.rb b/spec/features/budgets/groups_spec.rb index 808646733..a5fad8eee 100644 --- a/spec/features/budgets/groups_spec.rb +++ b/spec/features/budgets/groups_spec.rb @@ -2,8 +2,45 @@ require "rails_helper" describe "Budget Groups" do - let(:budget) { create(:budget) } - let(:group) { create(:budget_group, budget: budget) } + let(:budget) { create(:budget, slug: "budget_slug") } + let!(:group) { create(:budget_group, slug: "group_slug", budget: budget) } + + context "Load" do + + scenario "finds group using budget slug and group slug" do + visit budget_group_path("budget_slug", "group_slug") + expect(page).to have_content "Select an option" + end + + scenario "finds group using budget id and group id" do + visit budget_group_path(budget, group) + expect(page).to have_content "Select an option" + end + + scenario "raises an error if budget slug is not found" do + expect do + visit budget_group_path("wrong_budget", group) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if budget id is not found" do + expect do + visit budget_group_path(0, group) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if group slug is not found" do + expect do + visit budget_group_path(budget, "wrong_group") + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if group id is not found" do + expect do + visit budget_group_path(budget, 0) + end.to raise_error ActiveRecord::RecordNotFound + end + end context "Show" do scenario "Headings are sorted by name" do diff --git a/spec/features/budgets/investments_spec.rb b/spec/features/budgets/investments_spec.rb index 6d84ed03a..50c0e4d07 100644 --- a/spec/features/budgets/investments_spec.rb +++ b/spec/features/budgets/investments_spec.rb @@ -26,6 +26,53 @@ describe "Budget Investments" do it_behaves_like "relationable", Budget::Investment end + context "Load" do + + let(:investment) { create(:budget_investment, heading: heading) } + + before do + budget.update(slug: "budget_slug") + heading.update(slug: "heading_slug") + end + + scenario "finds investment using budget slug" do + visit budget_investment_path("budget_slug", investment) + + expect(page).to have_content investment.title + end + + scenario "raises an error if budget slug is not found" do + expect do + visit budget_investment_path("wrong_budget", investment) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if budget id is not found" do + expect do + visit budget_investment_path(0, investment) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "finds investment using heading slug" do + visit budget_investment_path(budget, investment, heading_id: "heading_slug") + + expect(page).to have_content investment.title + end + + scenario "raises an error if heading slug is not found" do + expect do + visit budget_investment_path(budget, investment, heading_id: "wrong_heading") + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if heading id is not found" do + expect do + visit budget_investment_path(budget, investment, heading_id: 0) + end.to raise_error ActiveRecord::RecordNotFound + end + + end + scenario "Index" do investments = [create(:budget_investment, heading: heading), create(:budget_investment, heading: heading), diff --git a/spec/features/budgets/results_spec.rb b/spec/features/budgets/results_spec.rb index f8e00bcb3..e10e9e7b2 100644 --- a/spec/features/budgets/results_spec.rb +++ b/spec/features/budgets/results_spec.rb @@ -52,6 +52,30 @@ describe "Results" do end end + scenario "Does not raise error if budget (slug or id) is not found" do + visit budget_results_path("wrong budget") + + within(".budgets-stats") do + expect(page).to have_content "Participatory budget results" + end + + visit budget_results_path(0) + + within(".budgets-stats") do + expect(page).to have_content "Participatory budget results" + end + end + + scenario "Loads budget and heading by slug" do + visit budget_results_path(budget.slug, heading.slug) + + expect(page).to have_selector("a.is-active", text: heading.name) + + within("#budget-investments-compatible") do + expect(page).to have_content investment1.title + end + end + scenario "Load first budget heading if not specified" do other_heading = create(:budget_heading, group: group) other_investment = create(:budget_investment, :winner, heading: other_heading) diff --git a/spec/features/budgets/stats_spec.rb b/spec/features/budgets/stats_spec.rb index 5b9305b33..d6113c7c0 100644 --- a/spec/features/budgets/stats_spec.rb +++ b/spec/features/budgets/stats_spec.rb @@ -2,14 +2,36 @@ require "rails_helper" describe "Stats" do - let(:budget) { create(:budget) } + let(:budget) { create(:budget, :finished) } let(:group) { create(:budget_group, budget: budget) } let(:heading) { create(:budget_heading, group: group, price: 1000) } + context "Load" do + + before { budget.update(slug: "budget_slug") } + + scenario "finds budget by slug" do + visit budget_stats_path("budget_slug") + + expect(page).to have_content budget.name + end + + scenario "raises an error if budget slug is not found" do + expect do + visit budget_stats_path("wrong_budget") + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if budget id is not found" do + expect do + visit budget_stats_path(0) + end.to raise_error ActiveRecord::RecordNotFound + end + + end + describe "Show" do describe "advanced stats" do - let(:budget) { create(:budget, :finished) } - scenario "advanced stats enabled" do budget.update(advanced_stats_enabled: true) diff --git a/spec/features/management/budget_investments_spec.rb b/spec/features/management/budget_investments_spec.rb index efb00d12f..14297ab41 100644 --- a/spec/features/management/budget_investments_spec.rb +++ b/spec/features/management/budget_investments_spec.rb @@ -3,7 +3,7 @@ require "rails_helper" describe "Budget Investments" do let(:manager) { create(:manager) } - let(:budget) { create(:budget, phase: "selecting", name: "2033") } + let(:budget) { create(:budget, phase: "selecting", name: "2033", slug: "budget_slug") } let(:group) { create(:budget_group, budget: budget, name: "Whole city") } let(:heading) { create(:budget_heading, group: group, name: "Health") } @@ -18,6 +18,33 @@ describe "Budget Investments" do { "budget_id": "budget_id" }, management = true + context "Load" do + + let(:investment) { create(:budget_investment, budget: budget) } + let(:user) { create(:user, :level_two) } + + before { login_managed_user(user) } + + scenario "finds investment using budget slug" do + visit management_budget_investment_path("budget_slug", investment) + + expect(page).to have_content investment.title + end + + scenario "raises an error if budget slug is not found" do + expect do + visit management_budget_investment_path("wrong_budget", investment) + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if budget id is not found" do + expect do + visit management_budget_investment_path(0, investment) + end.to raise_error ActiveRecord::RecordNotFound + end + + end + context "Create" do before { heading.budget.update(phase: "accepting") } diff --git a/spec/features/valuation/budget_investments_spec.rb b/spec/features/valuation/budget_investments_spec.rb index 03b843e03..372389f64 100644 --- a/spec/features/valuation/budget_investments_spec.rb +++ b/spec/features/valuation/budget_investments_spec.rb @@ -11,6 +11,30 @@ describe "Valuation budget investments" do login_as(valuator.user) end + context "Load" do + + before { budget.update(slug: "budget_slug") } + + scenario "finds investment using budget slug" do + visit valuation_budget_budget_investments_path("budget_slug") + + expect(page).to have_content budget.name + end + + scenario "raises an error if budget slug is not found" do + expect do + visit valuation_budget_budget_investments_path("wrong_budget") + end.to raise_error ActiveRecord::RecordNotFound + end + + scenario "raises an error if budget id is not found" do + expect do + visit valuation_budget_budget_investments_path(0) + end.to raise_error ActiveRecord::RecordNotFound + end + + end + scenario "Disabled with a feature flag" do Setting["process.budgets"] = nil expect{ diff --git a/spec/models/budget/investment_spec.rb b/spec/models/budget/investment_spec.rb index 044dead99..21283d1fe 100644 --- a/spec/models/budget/investment_spec.rb +++ b/spec/models/budget/investment_spec.rb @@ -382,6 +382,30 @@ describe Budget::Investment do end end + describe "scoped_filter" do + + let!(:budget) { create(:budget, slug: "budget_slug") } + let!(:group) { create(:budget_group, budget: budget) } + let!(:heading) { create(:budget_heading, group: group) } + let!(:investment) { create(:budget_investment, :feasible, heading: heading) } + + it "finds budget by id or slug" do + result = described_class.scoped_filter({budget_id: budget.id}, nil) + expect(result.count).to be 1 + expect(result.first.id).to be investment.id + + result = described_class.scoped_filter({budget_id: "budget_slug"}, nil) + expect(result.count).to be 1 + expect(result.first.id).to be investment.id + end + + it "does not raise error if budget is not found" do + result = described_class.scoped_filter({budget_id: "wrong_budget"}, nil) + expect(result).to be_empty + end + + end + describe "scopes" do describe "valuation_open" do it "returns all investments with false valuation_finished" do From dadbf873ba96c93194b236706058fe6dd4f1345e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 31 May 2019 13:41:01 +0200 Subject: [PATCH 19/55] Order translations using ruby MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Joining the translations table caused duplicate records to appear. Ordering with SQL is simply too hard because we need to consider fallback locales. Thanks Senén for providing most of the tests in the poll spec. --- app/controllers/polls_controller.rb | 5 +- .../budget_investments_controller.rb | 4 +- app/models/budget/group.rb | 4 +- app/models/budget/heading.rb | 3 +- app/models/poll.rb | 14 ++++- app/models/user.rb | 4 +- app/views/budgets/investments/_votes.html.erb | 2 +- spec/models/budget/group_spec.rb | 38 +++++++++++++ spec/models/budget/heading_spec.rb | 6 +++ spec/models/poll/poll_spec.rb | 54 +++++++++++++++++++ spec/models/user_spec.rb | 11 +++- 11 files changed, 131 insertions(+), 14 deletions(-) diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb index 60e1e4eff..f322379ce 100644 --- a/app/controllers/polls_controller.rb +++ b/app/controllers/polls_controller.rb @@ -12,8 +12,9 @@ class PollsController < ApplicationController ::Poll::Answer # trigger autoload def index - @polls = @polls.not_budget.public_polls.send(@current_filter).includes(:geozones) - .sort_for_list.page(params[:page]) + @polls = Kaminari.paginate_array( + @polls.public_polls.not_budget.send(@current_filter).includes(:geozones).sort_for_list + ).page(params[:page]) end def show diff --git a/app/controllers/valuation/budget_investments_controller.rb b/app/controllers/valuation/budget_investments_controller.rb index aa29120f1..a97616f41 100644 --- a/app/controllers/valuation/budget_investments_controller.rb +++ b/app/controllers/valuation/budget_investments_controller.rb @@ -75,9 +75,7 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController def heading_filters investments = @budget.investments.by_valuator(current_user.valuator.try(:id)) .visible_to_valuators.distinct - investment_headings = Budget::Heading.joins(:translations) - .where(id: investments.pluck(:heading_id).uniq) - .order(name: :asc) + investment_headings = Budget::Heading.where(id: investments.pluck(:heading_id)).sort_by(&:name) all_headings_filter = [ { diff --git a/app/models/budget/group.rb b/app/models/budget/group.rb index d77c840c0..43d28a0ae 100644 --- a/app/models/budget/group.rb +++ b/app/models/budget/group.rb @@ -28,7 +28,9 @@ class Budget validates :budget_id, presence: true validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/ - scope :sort_by_name, -> { joins(:translations).order(:name) } + def self.sort_by_name + all.sort_by(&:name) + end def single_heading_group? headings.count == 1 diff --git a/app/models/budget/heading.rb b/app/models/budget/heading.rb index 0b94aac67..7b33c4e54 100644 --- a/app/models/budget/heading.rb +++ b/app/models/budget/heading.rb @@ -40,8 +40,7 @@ class Budget delegate :budget, :budget_id, to: :group, allow_nil: true - scope :i18n, -> { joins(:translations) } - scope :allow_custom_content, -> { i18n.where(allow_custom_content: true).order("budget_heading_translations.name") } + scope :allow_custom_content, -> { where(allow_custom_content: true).sort_by(&:name) } def self.sort_by_name all.sort do |heading, other_heading| diff --git a/app/models/poll.rb b/app/models/poll.rb index 3e9ef6322..a6526f8cf 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -49,7 +49,19 @@ class Poll < ApplicationRecord scope :not_budget, -> { where(budget_id: nil) } scope :created_by_admin, -> { where(related_type: nil) } - scope :sort_for_list, -> { joins(:translations).order(:geozone_restricted, :starts_at, "poll_translations.name") } + def self.sort_for_list + all.sort do |poll, another_poll| + if poll.geozone_restricted? == another_poll.geozone_restricted? + [poll.starts_at, poll.name] <=> [another_poll.starts_at, another_poll.name] + else + if poll.geozone_restricted? + 1 + else + -1 + end + end + end + end def self.overlaping_with(poll) where("? < ends_at and ? >= starts_at", poll.starts_at.beginning_of_day, diff --git a/app/models/user.rb b/app/models/user.rb index ba9aec3d5..4e6b5f00c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -131,9 +131,7 @@ class User < ApplicationRecord end def headings_voted_within_group(group) - Budget::Heading.joins(:translations) - .order("name") - .where(id: voted_investments.by_group(group).pluck(:heading_id)) + Budget::Heading.where(id: voted_investments.by_group(group).pluck(:heading_id)) end def voted_investments diff --git a/app/views/budgets/investments/_votes.html.erb b/app/views/budgets/investments/_votes.html.erb index 8d17f696e..2923fccee 100644 --- a/app/views/budgets/investments/_votes.html.erb +++ b/app/views/budgets/investments/_votes.html.erb @@ -36,7 +36,7 @@ verify_account: link_to(t("votes.verify_account"), verification_path), signin: link_to(t("votes.signin"), new_user_session_path), signup: link_to(t("votes.signup"), new_user_registration_path), - supported_headings: (current_user && current_user.headings_voted_within_group(investment.group).map(&:name).to_sentence) + supported_headings: (current_user && current_user.headings_voted_within_group(investment.group).map(&:name).sort.to_sentence) ).html_safe %>

    diff --git a/spec/models/budget/group_spec.rb b/spec/models/budget/group_spec.rb index 2ab137ba5..ce1f47563 100644 --- a/spec/models/budget/group_spec.rb +++ b/spec/models/budget/group_spec.rb @@ -32,4 +32,42 @@ describe Budget::Group do end end + + describe "#sort_by_name" do + it "returns groups sorted by name ASC" do + thinkers = create(:budget_group, name: "Mmmm...") + sleepers = create(:budget_group, name: "Zzz...") + startled = create(:budget_group, name: "Aaaaah!") + + expect(Budget::Group.sort_by_name).to eq [startled, thinkers, sleepers] + end + + it "returns groups with multiple translations only once" do + create(:budget_group, name_en: "English", name_es: "Spanish") + + expect(Budget::Group.sort_by_name.count).to eq 1 + end + + context "fallback locales" do + before do + allow(I18n.fallbacks).to receive(:[]).and_return([:es]) + Globalize.set_fallbacks_to_all_available_locales + end + + it "orders by name considering fallback locale," do + budget = create(:budget, name: "Teams") + charlie = create(:budget_group, budget: budget, name: "Charlie") + delta = create(:budget_group, budget: budget, name: "Delta") + zulu = Globalize.with_locale(:es) do + create(:budget_group, budget: budget, name: "Zulu", name_fr: "Alpha") + end + bravo = Globalize.with_locale(:es) do + create(:budget_group, budget: budget, name: "Bravo") + end + + expect(Budget::Group.sort_by_name.count).to eq 4 + expect(Budget::Group.sort_by_name).to eq [bravo, charlie, delta, zulu] + end + end + end end diff --git a/spec/models/budget/heading_spec.rb b/spec/models/budget/heading_spec.rb index 2d69839e6..b32f6aeb1 100644 --- a/spec/models/budget/heading_spec.rb +++ b/spec/models/budget/heading_spec.rb @@ -319,6 +319,12 @@ describe Budget::Heading do expect(Budget::Heading.allow_custom_content.first).to eq first_heading expect(Budget::Heading.allow_custom_content.last).to eq last_heading end + + it "returns headings with multiple translations only once" do + create(:budget_heading, allow_custom_content: true, name_en: "English", name_es: "Spanish") + + expect(Budget::Heading.allow_custom_content.count).to eq 1 + end end end diff --git a/spec/models/poll/poll_spec.rb b/spec/models/poll/poll_spec.rb index 581123997..e03d941b1 100644 --- a/spec/models/poll/poll_spec.rb +++ b/spec/models/poll/poll_spec.rb @@ -359,4 +359,58 @@ describe Poll do end + describe "#sort_for_list" do + it "returns polls sorted by name ASC" do + starts_at = Time.current + 1.day + poll1 = create(:poll, geozone_restricted: true, starts_at: starts_at, name: "Zzz...") + poll2 = create(:poll, geozone_restricted: true, starts_at: starts_at, name: "Mmmm...") + poll3 = create(:poll, geozone_restricted: true, starts_at: starts_at, name: "Aaaaah!") + + expect(Poll.sort_for_list).to eq [poll3, poll2, poll1] + end + + it "returns not geozone restricted polls first" do + starts_at = Time.current + 1.day + poll1 = create(:poll, geozone_restricted: false, starts_at: starts_at, name: "Zzz...") + poll2 = create(:poll, geozone_restricted: true, starts_at: starts_at, name: "Aaaaaah!") + + expect(Poll.sort_for_list).to eq [poll1, poll2] + end + + it "returns polls earlier to start first" do + starts_at = Time.current + 1.day + poll1 = create(:poll, geozone_restricted: false, starts_at: starts_at - 1.hour, name: "Zzz...") + poll2 = create(:poll, geozone_restricted: false, starts_at: starts_at, name: "Aaaaah!") + + expect(Poll.sort_for_list).to eq [poll1, poll2] + end + + it "returns polls with multiple translations only once" do + create(:poll, name_en: "English", name_es: "Spanish") + + expect(Poll.sort_for_list.count).to eq 1 + end + + context "fallback locales" do + before do + allow(I18n.fallbacks).to receive(:[]).and_return([:es]) + Globalize.set_fallbacks_to_all_available_locales + end + + it "orders by name considering fallback locales" do + starts_at = Time.current + 1.day + poll1 = create(:poll, starts_at: starts_at, name: "Charlie") + poll2 = create(:poll, starts_at: starts_at, name: "Delta") + poll3 = Globalize.with_locale(:es) do + create(:poll, starts_at: starts_at, name: "Zzz...", name_fr: "Aaaah!") + end + poll4 = Globalize.with_locale(:es) do + create(:poll, starts_at: starts_at, name: "Bravo") + end + + expect(Poll.sort_for_list.count).to eq 4 + expect(Poll.sort_for_list).to eq [poll4, poll1, poll2, poll3] + end + end + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 599459324..d11df7760 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -3,7 +3,7 @@ require "rails_helper" describe User do describe "#headings_voted_within_group" do - it "returns the headings voted by a user ordered by name" do + it "returns the headings voted by a user" do user1 = create(:user) user2 = create(:user) @@ -35,6 +35,15 @@ describe User do expect(user2.headings_voted_within_group(group)).not_to include(san_franciso) expect(user2.headings_voted_within_group(group)).not_to include(another_heading) end + + it "returns headings with multiple translations only once" do + user = create(:user) + group = create(:budget_group) + heading = create(:budget_heading, group: group, name_en: "English", name_es: "Spanish") + create(:vote, votable: create(:budget_investment, heading: heading), voter: user) + + expect(user.headings_voted_within_group(group).count).to eq 1 + end end describe "#debate_votes" do From 481184e7f3e296da7165c7077a0556fd220b6bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 31 May 2019 13:48:27 +0200 Subject: [PATCH 20/55] Fix accidental translations join We added the code thinking we were ordering by the name of the poll, but here we're actually ordering by the name of the booth. --- app/models/poll/booth.rb | 2 +- spec/models/poll/booth_spec.rb | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/poll/booth.rb b/app/models/poll/booth.rb index d10270064..4c45b9d1a 100644 --- a/app/models/poll/booth.rb +++ b/app/models/poll/booth.rb @@ -12,7 +12,7 @@ class Poll end def self.available - where(polls: { id: Poll.current_or_recounting }).joins(polls: :translations) + where(polls: { id: Poll.current_or_recounting }).joins(:polls) end def assignment_on_poll(poll) diff --git a/spec/models/poll/booth_spec.rb b/spec/models/poll/booth_spec.rb index 8cde2263b..2a90d23c6 100644 --- a/spec/models/poll/booth_spec.rb +++ b/spec/models/poll/booth_spec.rb @@ -24,7 +24,7 @@ describe Poll::Booth do end end - describe "#available" do + describe ".available" do it "returns booths associated to current polls" do booth_for_current_poll = create(:poll_booth) @@ -40,5 +40,10 @@ describe Poll::Booth do expect(described_class.available).not_to include(booth_for_expired_poll) end + it "returns polls with multiple translations only once" do + create(:poll_booth, polls: [create(:poll, :current, name: "English", name_es: "Spanish")]) + + expect(Poll::Booth.available.count).to eq 1 + end end end From b4e8395bd6bae5e62d3595bd19a5d7d92c36b5b6 Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Tue, 14 May 2019 18:32:13 +0200 Subject: [PATCH 21/55] Extract settings forms to partials --- .../admin/settings/_featured_settings_form.html.erb | 6 ++++++ .../admin/settings/_featured_settings_table.html.erb | 8 +------- app/views/admin/settings/_settings_form.html.erb | 8 ++++++++ app/views/admin/settings/_settings_table.html.erb | 9 +-------- 4 files changed, 16 insertions(+), 15 deletions(-) create mode 100644 app/views/admin/settings/_featured_settings_form.html.erb create mode 100644 app/views/admin/settings/_settings_form.html.erb diff --git a/app/views/admin/settings/_featured_settings_form.html.erb b/app/views/admin/settings/_featured_settings_form.html.erb new file mode 100644 index 000000000..b1ec7ca7a --- /dev/null +++ b/app/views/admin/settings/_featured_settings_form.html.erb @@ -0,0 +1,6 @@ +<%= form_for(feature, url: admin_setting_path(feature), html: { id: "edit_#{dom_id(feature)}"}) do |f| %> + <%= f.hidden_field :value, id: dom_id(feature), value: (feature.enabled? ? "" : "active") %> + <%= f.submit(t("admin.settings.index.features.#{feature.enabled? ? "disable" : "enable"}"), + class: "button expanded #{feature.enabled? ? "hollow alert" : "success"}", + data: {confirm: t("admin.actions.confirm")}) %> +<% end %> diff --git a/app/views/admin/settings/_featured_settings_table.html.erb b/app/views/admin/settings/_featured_settings_table.html.erb index 5de1dc9b1..f4ba75823 100644 --- a/app/views/admin/settings/_featured_settings_table.html.erb +++ b/app/views/admin/settings/_featured_settings_table.html.erb @@ -32,13 +32,7 @@ - <%= form_for(feature, url: admin_setting_path(feature), html: { id: "edit_#{dom_id(feature)}"}) do |f| %> - - <%= f.hidden_field :value, id: dom_id(feature), value: (feature.enabled? ? "" : "active") %> - <%= f.submit(t("admin.settings.index.features.#{feature.enabled? ? "disable" : "enable"}"), - class: "button expanded #{feature.enabled? ? "hollow alert" : "success"}", - data: {confirm: t("admin.actions.confirm")}) %> - <% end %> + <%= render "admin/settings/featured_settings_form", feature: feature %> <% end %> diff --git a/app/views/admin/settings/_settings_form.html.erb b/app/views/admin/settings/_settings_form.html.erb new file mode 100644 index 000000000..b6851669b --- /dev/null +++ b/app/views/admin/settings/_settings_form.html.erb @@ -0,0 +1,8 @@ +<%= form_for(setting, url: admin_setting_path(setting), html: { id: "edit_#{dom_id(setting)}"}) do |f| %> +
    + <%= f.text_area :value, label: false, id: dom_id(setting), lines: 1 %> +
    +
    + <%= f.submit(t("admin.settings.index.update_setting"), class: "button hollow expanded") %> +
    +<% end %> diff --git a/app/views/admin/settings/_settings_table.html.erb b/app/views/admin/settings/_settings_table.html.erb index b6c75b440..76f567afc 100644 --- a/app/views/admin/settings/_settings_table.html.erb +++ b/app/views/admin/settings/_settings_table.html.erb @@ -16,14 +16,7 @@ - <%= form_for(setting, url: admin_setting_path(setting), html: { id: "edit_#{dom_id(setting)}"}) do |f| %> -
    - <%= f.text_area :value, label: false, id: dom_id(setting), lines: 1 %> -
    -
    - <%= f.submit(t("admin.settings.index.update_setting"), class: "button hollow expanded") %> -
    - <% end %> + <%= render "admin/settings/settings_form", setting: setting %> <% end %> From e32faf3a3ce21983639a65e73a652ea4db15412b Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Tue, 14 May 2019 18:44:29 +0200 Subject: [PATCH 22/55] Extract setting prefix to a method We may need to access the setting key prefix in the future, so it's better to have it in a method to avoid duplication --- app/models/setting.rb | 5 ++++- spec/models/setting_spec.rb | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/models/setting.rb b/app/models/setting.rb index 6210b6ba5..11623624a 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -3,8 +3,11 @@ class Setting < ApplicationRecord default_scope { order(id: :asc) } + def prefix + key.split(".").first + end + def type - prefix = key.split(".").first if %w[feature process proposals map html homepage].include? prefix prefix else diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb index 6fa86b686..971fbe3df 100644 --- a/spec/models/setting_spec.rb +++ b/spec/models/setting_spec.rb @@ -17,6 +17,16 @@ describe Setting do expect(described_class.where(key: "official_level_1_name", value: "Stormtrooper")).to exist end + describe "#prefix" do + it "returns the prefix of its key" do + expect(Setting.create(key: "prefix.key_name").prefix).to eq "prefix" + end + + it "returns the whole key for a non prefixed key" do + expect(Setting.create(key: "key_name").prefix).to eq "key_name" + end + end + describe "#type" do it "returns the key prefix for 'process' settings" do process_setting = Setting.create(key: "process.whatever") From 35cd26c11714bc782e2e2c2a41193b5a22ea95c2 Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Thu, 16 May 2019 16:11:43 +0200 Subject: [PATCH 23/55] Add images and document settings to the DB --- app/models/setting.rb | 10 ++++++++++ config/locales/en/settings.yml | 22 ++++++++++++++++++++++ config/locales/es/settings.yml | 22 ++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/app/models/setting.rb b/app/models/setting.rb index 11623624a..c3bb8bca0 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -84,6 +84,16 @@ class Setting < ApplicationRecord "proposals.email_description": nil, "proposals.poster_short_title": nil, "proposals.poster_description": nil, + # Images and Documents + "uploads.images.title.min_length": 4, + "uploads.images.title.max_length": 80, + "uploads.images.min_width": 0, + "uploads.images.min_height": 475, + "uploads.images.max_size": 1, + "uploads.images.content_types": "image/jpeg", + "uploads.documents.max_amount": 3, + "uploads.documents.max_size": 3, + "uploads.documents.content_types": "application/pdf", # Names for the moderation console, as a hint for moderators # to know better how to assign users with official positions "official_level_1_name": I18n.t("seeds.settings.official_level_1_name"), diff --git a/config/locales/en/settings.yml b/config/locales/en/settings.yml index cb0363f48..dc74ae6fb 100644 --- a/config/locales/en/settings.yml +++ b/config/locales/en/settings.yml @@ -139,3 +139,25 @@ en: per_page_code_head_description: "This code will appear inside the label. Useful for entering custom scripts, analytics..." per_page_code_body: "Code to be included on every page ()" per_page_code_body_description: "This code will appear inside the label. Useful for entering custom scripts, analytics..." + uploads: + images: + min_width: "Image minimum width" + min_width_description: "Minimum width allowed for an uploaded image (in pixels)" + min_height: "Image minimum height" + min_height_description: "Minimum height allowed for an uploaded image (in pixels)" + max_size: "Image maximum size" + max_size_description: "Maximum size allowed for an uploaded image (in Megabytes/MB)" + content_types: "Accepted content types for images" + content_types_description: "Select all the content types allowed for uploaded images" + title: + min_length: "Image title minimum length" + min_length_description: "Title provided by the user when uploading an image (used as alt HTML attribute)" + max_length: "Image title maximum length" + max_length_description: "Title provided by the user when uploading an image (used as alt HTML attribute)" + documents: + max_amount: "Maximum number of documents" + max_amount_description: "Maximum number of documents that can be attached to a proposal, investment..." + max_size: "Document maximum size" + max_size_description: "Maximum size allowed for an uploaded document (in Megabytes/MB)" + content_types: "Accepted content types for documents" + content_types_description: "Select all the content types allowed for uploaded documents" diff --git a/config/locales/es/settings.yml b/config/locales/es/settings.yml index 42b0726d1..cdf7f2452 100644 --- a/config/locales/es/settings.yml +++ b/config/locales/es/settings.yml @@ -139,3 +139,25 @@ es: per_page_code_head_description: "Esté código aparecerá dentro de la etiqueta . Útil para introducir scripts personalizados, analitycs..." per_page_code_body: "Código a incluir en cada página ()" per_page_code_body_description: "Esté código aparecerá dentro de la etiqueta . Útil para introducir scripts personalizados, analitycs..." + uploads: + images: + min_width: "Ancho mínimo de imagen" + min_width_description: "Ancho mínimo permitido al subir una imagen (en pixeles)" + min_height: "Alto mínimo de imagen" + min_height_description: "Alto mínimo permitido al subir una imagen (en pixeles)" + max_size: "Tamaño máximo de imagen" + max_size_description: "Tamaño máximo permitido al subir una imagen (en Megabytes/MB)" + content_types: "Tipos de imagenes permitidos" + content_types_description: "Selecciona todos los tipos permitidos para las imágenes subidas" + title: + min_length: "Longitud mínima del título de la imagen" + min_length_description: "El título es proporcionado por el usuario cuando se sube una imagen (usado como atributo HTML alt)" + max_length: "Longitud máxima del título de la imagen" + max_length_description: "El título es proporcionado por el usuario cuando se sube una imagen (usado como atributo HTML alt)" + documents: + max_amount: "Número máximo de documentos" + max_amount_description: "Número máximo de documentos que se pueden añadir a una propuesta, proyecto de gasto..." + max_size: "Tamaño máximo de documento" + max_size_description: "Tamaño máximo permitido al subir un documento (en Megabytes/MB)" + content_types: "Tipos de documentos permitidos" + content_types_description: "Selecciona todos los tipos permitidos para los documentos subidos" From 220bfb065afddc38b11706625632237c252167c2 Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Thu, 16 May 2019 16:14:40 +0200 Subject: [PATCH 24/55] Add images and documents settings to admin panel --- app/assets/stylesheets/admin.scss | 9 +++++ app/controllers/admin/settings_controller.rb | 15 ++++++++ app/models/setting.rb | 29 +++++++++++++++- .../_content_types_settings_form.html.erb | 19 +++++++++++ .../admin/settings/_filter_subnav.html.erb | 6 ++++ .../_images_and_documents_tab.html.erb | 3 ++ .../admin/settings/_settings_table.html.erb | 6 +++- app/views/admin/settings/index.html.erb | 4 +++ config/locales/en/admin.yml | 1 + config/locales/es/admin.yml | 1 + config/routes/admin.rb | 1 + spec/features/admin/settings_spec.rb | 34 +++++++++++++++++++ spec/models/setting_spec.rb | 20 +++++++++++ 13 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 app/views/admin/settings/_content_types_settings_form.html.erb create mode 100644 app/views/admin/settings/_images_and_documents_tab.html.erb diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 9c95f400f..e42bb0362 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -414,6 +414,15 @@ code { word-break: break-all; } +.content-type { + white-space: nowrap; + padding-right: $line-height; + + label { + margin-left: 0 !important; + } +} + // 02. Sidebar // ----------- diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb index 34fa22704..f24b6c63e 100644 --- a/app/controllers/admin/settings_controller.rb +++ b/app/controllers/admin/settings_controller.rb @@ -14,6 +14,7 @@ class Admin::SettingsController < Admin::BaseController @participation_processes_settings = all_settings["process"] @map_configuration_settings = all_settings["map"] @proposals_settings = all_settings["proposals"] + @uploads_settings = all_settings["uploads"] end def update @@ -29,10 +30,24 @@ class Admin::SettingsController < Admin::BaseController redirect_to admin_settings_path, notice: t("admin.settings.index.map.flash.update") end + def update_content_types + setting = Setting.find(params[:id]) + group = setting.content_type_group + mime_type_values = content_type_params.keys.map do |content_type| + Setting.mime_types[group][content_type] + end + setting.update value: mime_type_values.join(" ") + redirect_to admin_settings_path, notice: t("admin.settings.flash.updated") + end + private def settings_params params.require(:setting).permit(:value) end + def content_type_params + params.permit(:jpg, :png, :gif, :pdf, :doc, :docx, :xls, :xlsx, :csv, :zip) + end + end diff --git a/app/models/setting.rb b/app/models/setting.rb index c3bb8bca0..12162db4e 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -8,7 +8,7 @@ class Setting < ApplicationRecord end def type - if %w[feature process proposals map html homepage].include? prefix + if %w[feature process proposals map html homepage uploads].include? prefix prefix else "configuration" @@ -19,6 +19,14 @@ class Setting < ApplicationRecord value.present? end + def content_type? + key.split(".").last == "content_types" + end + + def content_type_group + key.split(".").second + end + class << self def [](key) where(key: key).pluck(:value).first.presence @@ -44,6 +52,25 @@ class Setting < ApplicationRecord setting.destroy if setting.present? end + def mime_types + { + "images" => { + "jpg" => "image/jpeg", + "png" => "image/png", + "gif" => "image/gif" + }, + "documents" => { + "pdf" => "application/pdf", + "doc" => "application/msword", + "docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "xls" => "application/x-ole-storage", + "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "csv" => "text/plain", + "zip" => "application/zip" + } + } + end + def defaults { "feature.featured_proposals": nil, diff --git a/app/views/admin/settings/_content_types_settings_form.html.erb b/app/views/admin/settings/_content_types_settings_form.html.erb new file mode 100644 index 000000000..67262c155 --- /dev/null +++ b/app/views/admin/settings/_content_types_settings_form.html.erb @@ -0,0 +1,19 @@ +<%= form_tag admin_update_content_types_path, method: :put, id: "edit_#{dom_id(setting)}" do %> + <%= hidden_field_tag "id", setting.id %> + +
    + <% group = setting.content_type_group %> + <% Setting.mime_types[group].each do |content_type, mime_type_value| %> + + <%= check_box_tag content_type, + setting.value.split(" ").include?(mime_type_value), + setting.value.split(" ").include?(mime_type_value) %> + <%= label_tag content_type, content_type.upcase %> + + <% end %> +
    + +
    + <%= submit_tag t("admin.settings.index.update_setting"), class: "button hollow expanded" %> +
    +<% end %> diff --git a/app/views/admin/settings/_filter_subnav.html.erb b/app/views/admin/settings/_filter_subnav.html.erb index d4e4a00d8..355e7a1c5 100644 --- a/app/views/admin/settings/_filter_subnav.html.erb +++ b/app/views/admin/settings/_filter_subnav.html.erb @@ -29,6 +29,12 @@ <% end %>
  • +
  • + <%= link_to "#tab-images-and-documents" do %> + <%= t("admin.settings.index.images_and_documents") %> + <% end %> +
  • +
  • <%= link_to "#tab-proposals" do %> <%= t("admin.settings.index.dashboard.title") %> diff --git a/app/views/admin/settings/_images_and_documents_tab.html.erb b/app/views/admin/settings/_images_and_documents_tab.html.erb new file mode 100644 index 000000000..740f55f23 --- /dev/null +++ b/app/views/admin/settings/_images_and_documents_tab.html.erb @@ -0,0 +1,3 @@ +

    <%= t("admin.settings.index.images_and_documents") %>

    + +<%= render "settings_table", settings: @uploads_settings %> diff --git a/app/views/admin/settings/_settings_table.html.erb b/app/views/admin/settings/_settings_table.html.erb index 76f567afc..ce99ffe4e 100644 --- a/app/views/admin/settings/_settings_table.html.erb +++ b/app/views/admin/settings/_settings_table.html.erb @@ -16,7 +16,11 @@ - <%= render "admin/settings/settings_form", setting: setting %> + <% if setting.content_type? %> + <%= render "admin/settings/content_types_settings_form", setting: setting %> + <% else %> + <%= render "admin/settings/settings_form", setting: setting %> + <% end %> <% end %> diff --git a/app/views/admin/settings/index.html.erb b/app/views/admin/settings/index.html.erb index 1de5b66be..a000cffc5 100644 --- a/app/views/admin/settings/index.html.erb +++ b/app/views/admin/settings/index.html.erb @@ -18,6 +18,10 @@ <%= render "map_configuration_tab" %> +
    + <%= render "images_and_documents_tab" %> +
    +
    <%= render "proposals_dashboard" %>
    diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index e3c5c597b..c860315ab 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -1261,6 +1261,7 @@ en: title: Configuration settings update_setting: Update participation_processes: "Participation processes" + images_and_documents: "Images and documents" feature_flags: Features features: enabled: "Feature enabled" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 34fc7510b..f783e0777 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -1260,6 +1260,7 @@ es: title: Configuración global update_setting: Actualizar participation_processes: "Procesos de participación" + images_and_documents: "Imágenes y documentos" feature_flags: Funcionalidades features: enabled: "Funcionalidad activada" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 39ed9b44b..ca162b9ca 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -90,6 +90,7 @@ namespace :admin do resources :settings, only: [:index, :update] put :update_map, to: "settings#update_map" + put :update_content_types, to: "settings#update_content_types" resources :moderators, only: [:index, :create, :destroy] do get :search, on: :collection diff --git a/spec/features/admin/settings_spec.rb b/spec/features/admin/settings_spec.rb index 4800b8c3e..ef72b5d0c 100644 --- a/spec/features/admin/settings_spec.rb +++ b/spec/features/admin/settings_spec.rb @@ -98,6 +98,40 @@ describe "Admin settings" do end + describe "Update content types" do + + scenario "stores the correct mime types" do + setting = Setting.create(key: "upload.images.content_types", value: "image/png") + admin = create(:administrator).user + login_as(admin) + visit admin_settings_path + find("#images-and-documents-tab").click + + within "#edit_setting_#{setting.id}" do + expect(find("#png")).to be_checked + expect(find("#jpg")).not_to be_checked + expect(find("#gif")).not_to be_checked + + check "gif" + + click_button "Update" + end + + expect(page).to have_content "Value updated" + expect(Setting["upload.images.content_types"]).to include "image/png" + expect(Setting["upload.images.content_types"]).to include "image/gif" + + visit admin_settings_path(anchor: "tab-images-and-documents") + + within "#edit_setting_#{setting.id}" do + expect(find("#png")).to be_checked + expect(find("#gif")).to be_checked + expect(find("#jpg")).not_to be_checked + end + end + + end + describe "Skip verification" do scenario "deactivate skip verification", :js do diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb index 971fbe3df..df764b7ca 100644 --- a/spec/models/setting_spec.rb +++ b/spec/models/setting_spec.rb @@ -80,6 +80,26 @@ describe Setting do end end + describe "#content_type?" do + it "returns true if the last part of the key is content_types" do + expect(Setting.create(key: "key_name.content_types").content_type?).to be true + end + + it "returns false if the last part of the key is not content_types" do + expect(Setting.create(key: "key_name.whatever").content_type?).to be false + end + end + + describe "#content_type_group" do + it "returns the group for content_types settings" do + images = Setting.create(key: "update.images.content_types") + documents = Setting.create(key: "update.documents.content_types") + + expect(images.content_type_group).to eq "images" + expect(documents.content_type_group).to eq "documents" + end + end + describe ".rename_key" do it "renames the setting keeping the original value and deletes the old setting" do Setting["old_key"] = "old_value" From 8e0bbf54f6dfc38b8a0f50ce3b06b0a0be97bce7 Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Thu, 16 May 2019 17:42:17 +0200 Subject: [PATCH 25/55] Replace harcoded images and documents settings --- app/helpers/documentables_helper.rb | 8 ++---- app/helpers/imageables_helper.rb | 18 ++++++------ app/models/budget/investment.rb | 3 -- app/models/concerns/documentable.rb | 17 +++++------ app/models/dashboard/action.rb | 8 ------ app/models/image.rb | 38 ++++++++++++++++++------- app/models/legislation/process.rb | 3 -- app/models/legislation/proposal.rb | 3 -- app/models/milestone.rb | 3 -- app/models/poll/question/answer.rb | 3 -- app/models/proposal.rb | 3 -- app/models/setting.rb | 5 ++++ config/i18n-tasks.yml | 2 ++ spec/models/setting_spec.rb | 18 ++++++++++++ spec/shared/models/image_validations.rb | 5 ++-- 15 files changed, 77 insertions(+), 60 deletions(-) diff --git a/app/helpers/documentables_helper.rb b/app/helpers/documentables_helper.rb index ef1a2d6b9..98ce55b59 100644 --- a/app/helpers/documentables_helper.rb +++ b/app/helpers/documentables_helper.rb @@ -17,15 +17,11 @@ module DocumentablesHelper end def accepted_content_types_extensions(documentable_class) - documentable_class.accepted_content_types - .collect{ |content_type| ".#{content_type.split("/").last}" } - .join(",") + Setting.accepted_content_types_for("documents").map { |content_type| ".#{content_type}" }.join(",") end def documentable_humanized_accepted_content_types(documentable_class) - documentable_class.accepted_content_types - .collect{ |content_type| content_type.split("/").last } - .join(", ") + Setting.accepted_content_types_for("documents").join(", ") end def documentables_note(documentable) diff --git a/app/helpers/imageables_helper.rb b/app/helpers/imageables_helper.rb index 1a85362d8..7c8d87298 100644 --- a/app/helpers/imageables_helper.rb +++ b/app/helpers/imageables_helper.rb @@ -9,7 +9,7 @@ module ImageablesHelper end def imageable_max_file_size - bytes_to_megabytes(Image::MAX_IMAGE_SIZE) + bytes_to_megabytes(Setting["uploads.images.max_size"].to_i.megabytes) end def bytes_to_megabytes(bytes) @@ -17,19 +17,21 @@ module ImageablesHelper end def imageable_accepted_content_types - Image::ACCEPTED_CONTENT_TYPE + Setting["uploads.images.content_types"]&.split(" ") || [ "image/jpeg" ] end def imageable_accepted_content_types_extensions - Image::ACCEPTED_CONTENT_TYPE - .collect{ |content_type| ".#{content_type.split("/").last}" } - .join(",") + Setting.accepted_content_types_for("images").map do |content_type| + if content_type == "jpg" + ".jpg,.jpeg" + else + ".#{content_type}" + end + end.join(",") end def imageable_humanized_accepted_content_types - Image::ACCEPTED_CONTENT_TYPE - .collect{ |content_type| content_type.split("/").last } - .join(", ") + Setting.accepted_content_types_for("images").join(", ") end def imageables_note(_imageable) diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index 2eaf801c1..55634cf4b 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -13,9 +13,6 @@ class Budget include Imageable include Mappable include Documentable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] acts_as_votable acts_as_paranoid column: :hidden_at diff --git a/app/models/concerns/documentable.rb b/app/models/concerns/documentable.rb index 36e91ece7..9f49d9af9 100644 --- a/app/models/concerns/documentable.rb +++ b/app/models/concerns/documentable.rb @@ -7,16 +7,17 @@ module Documentable end module ClassMethods - attr_reader :max_documents_allowed, :max_file_size, :accepted_content_types - - private - - def documentable(options = {}) - @max_documents_allowed = options[:max_documents_allowed] - @max_file_size = options[:max_file_size] - @accepted_content_types = options[:accepted_content_types] + def max_documents_allowed + Setting["uploads.documents.max_amount"].to_i end + def max_file_size + Setting["uploads.documents.max_size"].to_i.megabytes + end + + def accepted_content_types + Setting["uploads.documents.content_types"]&.split(" ") || [ "application/pdf" ] + end end end diff --git a/app/models/dashboard/action.rb b/app/models/dashboard/action.rb index afe61c075..4dea153a7 100644 --- a/app/models/dashboard/action.rb +++ b/app/models/dashboard/action.rb @@ -1,13 +1,5 @@ class Dashboard::Action < ApplicationRecord include Documentable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf", - "image/jpeg", - "image/jpg", - "image/png", - "application/zip" ] - include Linkable acts_as_paranoid column: :hidden_at diff --git a/app/models/image.rb b/app/models/image.rb index eefcd3926..6929f5f96 100644 --- a/app/models/image.rb +++ b/app/models/image.rb @@ -2,12 +2,11 @@ class Image < ApplicationRecord include ImagesHelper include ImageablesHelper - TITLE_LENGTH_RANGE = 4..80 - MIN_SIZE = 475 - MAX_IMAGE_SIZE = 1.megabyte - ACCEPTED_CONTENT_TYPE = %w(image/jpeg image/jpg).freeze - - has_attached_file :attachment, styles: { large: "x#{MIN_SIZE}", medium: "300x300#", thumb: "140x245#" }, + has_attached_file :attachment, styles: { + large: "x#{Setting["uploads.images.min_height"]}", + medium: "300x300#", + thumb: "140x245#" + }, url: "/system/:class/:prefix/:style/:hash.:extension", hash_data: ":class/:style", use_timestamp: false, @@ -23,7 +22,8 @@ class Image < ApplicationRecord validate :attachment_presence validate :validate_attachment_content_type, if: -> { attachment.present? } validate :validate_attachment_size, if: -> { attachment.present? } - validates :title, presence: true, length: { in: TITLE_LENGTH_RANGE } + validates :title, presence: true + validate :validate_title_length validates :user_id, presence: true validates :imageable_id, presence: true, if: -> { persisted? } validates :imageable_type, presence: true, if: -> { persisted? } @@ -71,20 +71,38 @@ class Image < ApplicationRecord return true if imageable_class == Widget::Card dimensions = Paperclip::Geometry.from_file(attachment.queued_for_write[:original].path) - errors.add(:attachment, :min_image_width, required_min_width: MIN_SIZE) if dimensions.width < MIN_SIZE - errors.add(:attachment, :min_image_height, required_min_height: MIN_SIZE) if dimensions.height < MIN_SIZE + min_width = Setting["uploads.images.min_width"].to_i + min_height = Setting["uploads.images.min_height"].to_i + errors.add(:attachment, :min_image_width, required_min_width: min_width) if dimensions.width < min_width + errors.add(:attachment, :min_image_height, required_min_height: min_height) if dimensions.height < min_height end end def validate_attachment_size if imageable_class && - attachment_file_size > 1.megabytes + attachment_file_size > Setting["uploads.images.max_size"].to_i.megabytes errors.add(:attachment, I18n.t("images.errors.messages.in_between", min: "0 Bytes", max: "#{imageable_max_file_size} MB")) end end + def validate_title_length + if title.present? + + title_min_length = Setting["uploads.images.title.min_length"].to_i + title_max_length = Setting["uploads.images.title.max_length"].to_i + + if title.size < title_min_length + errors.add(:title, I18n.t("errors.messages.too_short", count: title_min_length)) + end + + if title.size > title_max_length + errors.add(:title, I18n.t("errors.messages.too_long", count: title_max_length)) + end + end + end + def validate_attachment_content_type if imageable_class && !attachment_of_valid_content_type? message = I18n.t("images.errors.messages.wrong_content_type", diff --git a/app/models/legislation/process.rb b/app/models/legislation/process.rb index 3d2c72c27..3f91ea27a 100644 --- a/app/models/legislation/process.rb +++ b/app/models/legislation/process.rb @@ -4,9 +4,6 @@ class Legislation::Process < ApplicationRecord include Milestoneable include Imageable include Documentable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] acts_as_paranoid column: :hidden_at acts_as_taggable_on :customs diff --git a/app/models/legislation/proposal.rb b/app/models/legislation/proposal.rb index d6fdf5eb3..879eeb27d 100644 --- a/app/models/legislation/proposal.rb +++ b/app/models/legislation/proposal.rb @@ -14,9 +14,6 @@ class Legislation::Proposal < ApplicationRecord include Imageable include Randomizable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] accepts_nested_attributes_for :documents, allow_destroy: true acts_as_votable diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 69d46149a..52f8ccb29 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -1,9 +1,6 @@ class Milestone < ApplicationRecord include Imageable include Documentable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] translates :title, :description, touch: true include Globalizable diff --git a/app/models/poll/question/answer.rb b/app/models/poll/question/answer.rb index cde5e00d1..aab7acb59 100644 --- a/app/models/poll/question/answer.rb +++ b/app/models/poll/question/answer.rb @@ -6,9 +6,6 @@ class Poll::Question::Answer < ApplicationRecord translates :description, touch: true include Globalizable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] accepts_nested_attributes_for :documents, allow_destroy: true belongs_to :question, class_name: "Poll::Question", foreign_key: "question_id" diff --git a/app/models/proposal.rb b/app/models/proposal.rb index deb829058..3b4fbd0e2 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -15,9 +15,6 @@ class Proposal < ApplicationRecord include Mappable include Notifiable include Documentable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] include EmbedVideosHelper include Relationable include Milestoneable diff --git a/app/models/setting.rb b/app/models/setting.rb index 12162db4e..afc363502 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -52,6 +52,11 @@ class Setting < ApplicationRecord setting.destroy if setting.present? end + def accepted_content_types_for(group) + mime_content_types = Setting["uploads.#{group}.content_types"]&.split(" ") || [] + Setting.mime_types[group].select { |_, content_type| mime_content_types.include?(content_type) }.keys + end + def mime_types { "images" => { diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 240313c32..87670d676 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -115,6 +115,8 @@ ignore_missing: - "activerecord.errors.models.direct_message.*" - "errors.messages.blank" - "errors.messages.taken" + - "errors.messages.too_short" + - "errors.messages.too_long" - "devise.failure.invalid" - "devise.registrations.destroyed" - "devise.password_expired.*" diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb index df764b7ca..fde1615cd 100644 --- a/spec/models/setting_spec.rb +++ b/spec/models/setting_spec.rb @@ -148,6 +148,24 @@ describe Setting do end end + describe ".accepted_content_types_for" do + it "returns the formats accepted according to the setting value" do + Setting["uploads.images.content_types"] = "image/jpeg image/gif" + Setting["uploads.documents.content_types"] = "application/pdf application/msword" + + expect(Setting.accepted_content_types_for("images")).to eq ["jpg", "gif"] + expect(Setting.accepted_content_types_for("documents")).to eq ["pdf", "doc"] + end + + it "returns empty array if setting does't exist" do + Setting.remove("uploads.images.content_types") + Setting.remove("uploads.documents.content_types") + + expect(Setting.accepted_content_types_for("images")).to be_empty + expect(Setting.accepted_content_types_for("documents")).to be_empty + end + end + describe ".add_new_settings" do context "default settings with strings" do before do diff --git a/spec/shared/models/image_validations.rb b/spec/shared/models/image_validations.rb index 691d73bae..c2b02a636 100644 --- a/spec/shared/models/image_validations.rb +++ b/spec/shared/models/image_validations.rb @@ -41,7 +41,8 @@ shared_examples "image validations" do |imageable_factory| end it "is not valid for attachments larger than imageable max_file_size definition" do - allow(image).to receive(:attachment_file_size).and_return(Image::MAX_IMAGE_SIZE + 1.byte) + larger_size = Setting["uploads.images.max_size"].to_i.megabytes + 1.byte + allow(image).to receive(:attachment_file_size).and_return(larger_size) expect(image).not_to be_valid expect(image.errors[:attachment]).to include "must be in between 0 Bytes and 1 MB" @@ -67,4 +68,4 @@ shared_examples "image validations" do |imageable_factory| expect(image).not_to be_valid end -end \ No newline at end of file +end From 19867836f88886c69923a6a624c6600fe9a8cb5f Mon Sep 17 00:00:00 2001 From: decabeza Date: Tue, 4 Jun 2019 17:29:31 +0200 Subject: [PATCH 26/55] Extract partial with mobile sticky content --- app/views/proposals/_support_status.html.erb | 43 +++++++++++++++ app/views/proposals/show.html.erb | 57 +++----------------- 2 files changed, 50 insertions(+), 50 deletions(-) create mode 100644 app/views/proposals/_support_status.html.erb diff --git a/app/views/proposals/_support_status.html.erb b/app/views/proposals/_support_status.html.erb new file mode 100644 index 000000000..21fd09dc9 --- /dev/null +++ b/app/views/proposals/_support_status.html.erb @@ -0,0 +1,43 @@ +<% if proposal.selected? %> +
    + <%= t("proposals.proposal.selected") %> +
    +<% else %> +
    +
    +
    + +

    <%= t("votes.supports") %>

    + +
    + <% if proposal.draft? %> +
    +

    <%= t("proposals.show.draft") %>

    +
    + <% elsif proposal.successful? %> +
    + <%= render "supports", proposal: proposal %> +
    + <% elsif proposal.archived? %> +
    +

    + <%= t("proposals.proposal.supports", count: proposal.total_votes) %> +

    +

    <%= t("proposals.proposal.archived") %>

    +
    + <% else %> + <%= render "votes", { proposal: proposal, vote_url: vote_proposal_path(proposal, value: "yes") } %> + <% end %> +
    +
    +
    +
    + +
    +<% end %> diff --git a/app/views/proposals/show.html.erb b/app/views/proposals/show.html.erb index f405f15af..596c9624e 100644 --- a/app/views/proposals/show.html.erb +++ b/app/views/proposals/show.html.erb @@ -2,12 +2,12 @@ <% provide :title do %><%= @proposal.title %><% end %> <% content_for :meta_description do %><%= @proposal.summary %><% end %> <% provide :social_media_meta_tags do %> -<%= render "shared/social_media_meta_tags", - social_url: proposal_url(@proposal), - social_title: @proposal.title, - social_description: @proposal.summary, - twitter_image_url: (@proposal.image.present? ? @proposal.image_url(:thumb) : nil), - og_image_url: (@proposal.image.present? ? @proposal.image_url(:thumb) : nil) %> + <%= render "shared/social_media_meta_tags", + social_url: proposal_url(@proposal), + social_title: @proposal.title, + social_description: @proposal.summary, + twitter_image_url: (@proposal.image.present? ? @proposal.image_url(:thumb) : nil), + og_image_url: (@proposal.image.present? ? @proposal.image_url(:thumb) : nil) %> <% end %> <% content_for :canonical do %> <%= render "shared/canonical", href: proposal_url(@proposal) %> @@ -62,50 +62,7 @@ <% end %> - <% if @proposal.selected? %> -
    - <%= t("proposals.proposal.selected") %> -
    - <% else %> -
    -
    -
    - -

    <%= t("votes.supports") %>

    - -
    - <% if @proposal.draft? %> -
    -

    <%= t(".draft") %>

    -
    - <% elsif @proposal.successful? %> -
    - <%= render "supports", proposal: @proposal %> -
    - <% elsif @proposal.archived? %> -
    -

    - <%= t("proposals.proposal.supports", count: @proposal.total_votes) %> -

    -

    <%= t("proposals.proposal.archived") %>

    -
    - <% else %> - <%= render "votes", - { proposal: @proposal, vote_url: vote_proposal_path(@proposal, value: "yes") } %> - <% end %> -
    -
    -
    -
    - -
    - <% end %> + <%= render "proposals/support_status", proposal: @proposal %> <%= render "proposals/social_share", proposal: @proposal, share_title: t("proposals.show.share") %> From 1f76b25e2a3f2e5280c452397526f9ca6ec3fd59 Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Fri, 31 May 2019 18:29:11 +0200 Subject: [PATCH 27/55] Show name and email for deleted poll officer's user account Avoid to raise an exception `Module::DelegationError' when trying to show the name and/or email of a poll officer whose user account has been deleted. We'll show a message "User deleted" and "Email deleted" instead. --- app/models/poll/officer.rb | 8 +++++++- config/locales/en/general.yml | 1 + config/locales/es/general.yml | 1 + spec/models/poll/officer_spec.rb | 28 ++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/app/models/poll/officer.rb b/app/models/poll/officer.rb index c44256a5a..429add05a 100644 --- a/app/models/poll/officer.rb +++ b/app/models/poll/officer.rb @@ -7,7 +7,13 @@ class Poll validates :user_id, presence: true, uniqueness: true - delegate :name, :email, to: :user + def name + user&.name || I18n.t("shared.author_info.author_deleted") + end + + def email + user&.email || I18n.t("shared.author_info.email_deleted") + end def voting_days_assigned_polls officer_assignments.voting_days.includes(booth_assignment: :poll). diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index 1cc83c602..5bb51982d 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -724,6 +724,7 @@ en: to: "To" author_info: author_deleted: User deleted + email_deleted: Email deleted back: Go back check: Select check_all: All diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index b7a527c6a..52994ae87 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -722,6 +722,7 @@ es: to: "Hasta" author_info: author_deleted: Usuario eliminado + email_deleted: Email eliminado back: Volver check: Seleccionar check_all: Todos diff --git a/spec/models/poll/officer_spec.rb b/spec/models/poll/officer_spec.rb index 4cdc581b1..d86efce14 100644 --- a/spec/models/poll/officer_spec.rb +++ b/spec/models/poll/officer_spec.rb @@ -2,6 +2,34 @@ require "rails_helper" describe Poll::Officer do + describe "#name" do + let(:officer) { create(:poll_officer) } + + it "returns user name if user is not deleted" do + expect(officer.name).to eq officer.user.name + end + + it "returns 'User deleted' if user is deleted" do + officer.user.destroy + + expect(officer.reload.name).to eq "User deleted" + end + end + + describe "#email" do + let(:officer) { create(:poll_officer) } + + it "returns user email if user is not deleted" do + expect(officer.email).to eq officer.user.email + end + + it "returns 'Email deleted' if user is deleted" do + officer.user.destroy + + expect(officer.reload.email).to eq "Email deleted" + end + end + describe "#voting_days_assigned_polls" do it "returns all polls with this officer assigned during voting days" do officer = create(:poll_officer) From d0e1eff53290930271a2999e7896eeb31a898222 Mon Sep 17 00:00:00 2001 From: lalo Date: Tue, 30 Apr 2019 17:39:27 +0200 Subject: [PATCH 28/55] Add description field to administrator users like evaluators description --- .../admin/administrators_controller.rb | 18 ++++++++ app/helpers/admin_helper.rb | 4 +- app/models/abilities/administrator.rb | 2 +- app/models/administrator.rb | 10 +++++ app/views/admin/administrators/edit.html.erb | 15 +++++++ app/views/admin/administrators/index.html.erb | 10 ++++- .../_select_investment.html.erb | 6 +-- .../admin/budget_investments/edit.html.erb | 2 +- config/locales/en/admin.yml | 4 ++ config/locales/es/activerecord.yml | 2 + config/locales/es/admin.yml | 4 ++ config/routes/admin.rb | 2 +- ...150724_add_description_to_administrator.rb | 5 +++ db/schema.rb | 1 + spec/features/admin/administrators_spec.rb | 18 +++++++- .../features/admin/budget_investments_spec.rb | 14 ++++-- spec/models/administrator_spec.rb | 45 +++++++++++++++++++ 17 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 app/views/admin/administrators/edit.html.erb create mode 100644 db/migrate/20190314150724_add_description_to_administrator.rb create mode 100644 spec/models/administrator_spec.rb diff --git a/app/controllers/admin/administrators_controller.rb b/app/controllers/admin/administrators_controller.rb index 3467ee0c0..5e423be81 100644 --- a/app/controllers/admin/administrators_controller.rb +++ b/app/controllers/admin/administrators_controller.rb @@ -28,4 +28,22 @@ class Admin::AdministratorsController < Admin::BaseController redirect_to admin_administrators_path end + + def edit + end + + def update + if @administrator.update(update_administrator_params) + notice = t("admin.administrators.form.updated") + redirect_to admin_administrators_path, notice: notice + else + render :edit + end + end + + private + + def update_administrator_params + params.require(:administrator).permit(:description) + end end diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb index e0dc3834f..bad9af389 100644 --- a/app/helpers/admin_helper.rb +++ b/app/helpers/admin_helper.rb @@ -77,7 +77,9 @@ module AdminHelper end def admin_select_options - Administrator.all.order("users.username asc").includes(:user).collect { |v| [ v.name, v.id ] } + Administrator.with_user + .collect { |v| [ v.description_or_name, v.id ] } + .sort_by { |a| a[0] } end def admin_submit_action(resource) diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index c37e06f65..c0e723fff 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -51,7 +51,7 @@ module Abilities can :comment_as_administrator, [Debate, Comment, Proposal, Poll::Question, Budget::Investment, Legislation::Question, Legislation::Proposal, Legislation::Annotation, Topic] - can [:search, :create, :index, :destroy], ::Administrator + can [:search, :create, :index, :destroy, :edit, :update], ::Administrator can [:search, :create, :index, :destroy], ::Moderator can [:search, :show, :edit, :update, :create, :index, :destroy, :summary], ::Valuator can [:search, :create, :index, :destroy], ::Manager diff --git a/app/models/administrator.rb b/app/models/administrator.rb index b8b6cc1c0..1c5cb027a 100644 --- a/app/models/administrator.rb +++ b/app/models/administrator.rb @@ -3,4 +3,14 @@ class Administrator < ApplicationRecord delegate :name, :email, :name_and_email, to: :user validates :user_id, presence: true, uniqueness: true + + scope :with_user, -> { includes(:user) } + + def description_or_name + description.presence || name + end + + def description_or_name_and_email + "#{description_or_name} (#{email})" + end end diff --git a/app/views/admin/administrators/edit.html.erb b/app/views/admin/administrators/edit.html.erb new file mode 100644 index 000000000..e9374ee78 --- /dev/null +++ b/app/views/admin/administrators/edit.html.erb @@ -0,0 +1,15 @@ +<%= back_link_to admin_administrators_path %> + +

    <%= t("admin.administrators.form.edit_title") %>

    + +
    + <%= @administrator.name %>
    + <%= @administrator.email %> +
    + +
    + <%= form_for [:admin, @administrator] do |f| %> + <%= f.text_field :description %> + <%= f.submit class: "button success" %> + <% end %> +
    diff --git a/app/views/admin/administrators/index.html.erb b/app/views/admin/administrators/index.html.erb index c727114bf..469905279 100644 --- a/app/views/admin/administrators/index.html.erb +++ b/app/views/admin/administrators/index.html.erb @@ -11,6 +11,7 @@ <%= t("admin.administrators.index.id") %> <%= t("admin.administrators.index.name") %> <%= t("admin.administrators.index.email") %> + <%= t("admin.administrators.index.description") %> <%= t("admin.shared.actions") %> <% @administrators.each do |administrator| %> @@ -24,13 +25,18 @@ <%= administrator.email %> + + <%= administrator.description %> + <% if administrator.persisted? %> + <%= link_to t("admin.actions.edit"), + edit_admin_administrator_path(administrator), + class: "button hollow" %> <%= link_to t("admin.administrators.administrator.delete"), admin_administrator_path(administrator), method: :delete, - class: "button hollow alert expanded" - %> + class: "button hollow alert" %> <% else %> <%= link_to t("admin.administrators.administrator.add"), { controller: "admin/administrators", action: :create, diff --git a/app/views/admin/budget_investments/_select_investment.html.erb b/app/views/admin/budget_investments/_select_investment.html.erb index 788d9824b..2da7a3817 100644 --- a/app/views/admin/budget_investments/_select_investment.html.erb +++ b/app/views/admin/budget_investments/_select_investment.html.erb @@ -13,9 +13,9 @@ <% if investment.administrator.present? %> - "> - <%= investment.administrator.name %> - + "> + <%= investment.administrator.description_or_name %> + <% else %> <%= t("admin.budget_investments.index.no_admin_assigned") %> <% end %> diff --git a/app/views/admin/budget_investments/edit.html.erb b/app/views/admin/budget_investments/edit.html.erb index 20e3c5e56..8d2ff842f 100644 --- a/app/views/admin/budget_investments/edit.html.erb +++ b/app/views/admin/budget_investments/edit.html.erb @@ -46,7 +46,7 @@
    <%= f.select(:administrator_id, - @admins.collect{ |a| [a.name_and_email, a.id ] }, + @admins.collect{ |a| [a.description_or_name_and_email, a.id ] }, { include_blank: t("admin.budget_investments.edit.undefined") }) %>
    diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index e3c5c597b..0ad7312e9 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -728,6 +728,7 @@ en: title: Administrators name: Name email: Email + description: Description id: Administrator ID no_administrators: There are no administrators. administrator: @@ -736,6 +737,9 @@ en: restricted_removal: "Sorry, you can't remove yourself from the administrators" search: title: "Administrators: User search" + form: + edit_title: "Edit administrator" + updated: "Administrator updated successfully" moderators: index: title: Moderators diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml index e3bd118f7..4e5b8acd8 100644 --- a/config/locales/es/activerecord.yml +++ b/config/locales/es/activerecord.yml @@ -126,6 +126,8 @@ es: one: Enlace other: Enlaces attributes: + administrator: + description: Descripción budget: name: "Nombre" description_accepting: "Descripción durante la fase de presentación de proyectos" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 34fc7510b..291051a9d 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -727,6 +727,7 @@ es: title: Administradores name: Nombre email: Email + description: Descripción id: ID de Administrador no_administrators: No hay administradores. administrator: @@ -735,6 +736,9 @@ es: restricted_removal: "Lo sentimos, no puedes eliminarte a ti mismo de la lista" search: title: "Administradores: Búsqueda de usuarios" + form: + edit_title: "Editar administrador" + updated: "Administrador actualizado correctamente" moderators: index: title: Moderadores diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 39ed9b44b..148138d61 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -106,7 +106,7 @@ namespace :admin do get :search, on: :collection end - resources :administrators, only: [:index, :create, :destroy] do + resources :administrators, only: [:index, :create, :destroy, :edit, :update] do get :search, on: :collection end diff --git a/db/migrate/20190314150724_add_description_to_administrator.rb b/db/migrate/20190314150724_add_description_to_administrator.rb new file mode 100644 index 000000000..d2058bb7f --- /dev/null +++ b/db/migrate/20190314150724_add_description_to_administrator.rb @@ -0,0 +1,5 @@ +class AddDescriptionToAdministrator < ActiveRecord::Migration + def change + add_column :administrators, :description, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 5178cec3c..8f83dfffb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -67,6 +67,7 @@ ActiveRecord::Schema.define(version: 20190429125842) do create_table "administrators", force: :cascade do |t| t.integer "user_id" + t.string "description" t.index ["user_id"], name: "index_administrators_on_user_id", using: :btree end diff --git a/spec/features/admin/administrators_spec.rb b/spec/features/admin/administrators_spec.rb index a782e39d3..3ba76eaac 100644 --- a/spec/features/admin/administrators_spec.rb +++ b/spec/features/admin/administrators_spec.rb @@ -3,7 +3,7 @@ require "rails_helper" describe "Admin administrators" do let!(:admin) { create(:administrator) } let!(:user) { create(:user, username: "Jose Luis Balbin") } - let!(:user_administrator) { create(:administrator) } + let!(:user_administrator) { create(:administrator, description: "admin_alias") } before do login_as(admin.user) @@ -14,6 +14,7 @@ describe "Admin administrators" do expect(page).to have_content user_administrator.id expect(page).to have_content user_administrator.name expect(page).to have_content user_administrator.email + expect(page).to have_content user_administrator.description expect(page).not_to have_content user.name end @@ -100,4 +101,19 @@ describe "Admin administrators" do end end + context "Edit" do + let!(:administrator1) { create(:administrator, user: create(:user, + username: "Bernard Sumner", + email: "bernard@sumner.com")) } + + scenario "admin can edit administrator1" do + visit(edit_admin_administrator_path(administrator1)) + fill_in "administrator_description", with: "Admin Alias" + click_button "Update Administrator" + + expect(page).to have_content("Administrator updated successfully") + expect(page).to have_content("Admin Alias") + end + end + end diff --git a/spec/features/admin/budget_investments_spec.rb b/spec/features/admin/budget_investments_spec.rb index 8dd9743d3..ed96e7140 100644 --- a/spec/features/admin/budget_investments_spec.rb +++ b/spec/features/admin/budget_investments_spec.rb @@ -149,8 +149,9 @@ describe "Admin budget investments" do scenario "Filtering by admin", :js do user = create(:user, username: "Admin 1") + user2 = create(:user, username: "Admin 2") administrator = create(:administrator, user: user) - + create(:administrator, user: user2, description: "Alias") create(:budget_investment, title: "Realocate visitors", budget: budget, administrator: administrator) create(:budget_investment, title: "Destroy the city", budget: budget) @@ -166,6 +167,13 @@ describe "Admin budget investments" do expect(page).not_to have_link("Destroy the city") expect(page).to have_link("Realocate visitors") + select "Alias", from: "administrator_id" + click_button "Filter" + + expect(page).to have_content("There are no investment projects") + expect(page).not_to have_link("Destroy the city") + expect(page).not_to have_link("Realocate visitors") + select "All administrators", from: "administrator_id" click_button "Filter" @@ -1066,12 +1074,12 @@ describe "Admin budget investments" do scenario "Add administrator" do budget_investment = create(:budget_investment) user = create(:user, username: "Marta", email: "marta@admins.org") - create(:administrator, user: user) + create(:administrator, user: user, description: "Marta desc") visit admin_budget_budget_investment_path(budget_investment.budget, budget_investment) click_link "Edit classification" - select "Marta (marta@admins.org)", from: "budget_investment[administrator_id]" + select "Marta desc (marta@admins.org)", from: "budget_investment[administrator_id]" click_button "Update" expect(page).to have_content "Investment project updated succesfully." diff --git a/spec/models/administrator_spec.rb b/spec/models/administrator_spec.rb new file mode 100644 index 000000000..39b740980 --- /dev/null +++ b/spec/models/administrator_spec.rb @@ -0,0 +1,45 @@ +require "rails_helper" + +describe Administrator do + + describe "#description_or_name" do + let!(:user) { create(:user, username: "Billy Wilder" )} + + it "returns description if present" do + administrator = create(:administrator, user: user, description: "John Doe") + + expect(administrator.description_or_name).to eq("John Doe") + end + + it "returns name if description is nil" do + administrator = create(:administrator, user: user) + + expect(administrator.description_or_name).to eq("Billy Wilder") + end + + it "returns name if description is blank" do + administrator = create(:administrator, description: "") + + expect(administrator.description_or_name).to eq(administrator.name) + end + end + + describe "#description_or_name_and_email" do + let!(:user) { create(:user, username: "Billy Wilder", email: "test@test.com")} + + it "returns description and email if decription present" do + administrator = create(:administrator, + description: "John Doe", + user: user) + + expect(administrator.description_or_name_and_email).to eq("John Doe (test@test.com)") + end + + it "returns name and email if description is not present" do + administrator = create(:administrator, user: user) + + expect(administrator.description_or_name_and_email).to eq("Billy Wilder (test@test.com)") + end + end + +end From d721920b97c28606998588e2cf82eda95f2b6674 Mon Sep 17 00:00:00 2001 From: lalo Date: Tue, 4 Jun 2019 12:48:20 +0200 Subject: [PATCH 29/55] Display commenter as admin description or name --- app/views/comments/_comment.html.erb | 12 +- app/views/comments/_comment_tree.html.erb | 6 +- .../_valuation_comments.html.erb | 12 +- .../comments/budget_investments_spec.rb | 130 +++++++++++++----- 4 files changed, 116 insertions(+), 44 deletions(-) diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index 9fc0f875b..8e72d6c53 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -3,7 +3,8 @@ <% allow_votes = local_assigns.fetch(:allow_votes, true) %> <% allow_actions = local_assigns.fetch(:allow_actions, true) %> <% allow_comments = local_assigns.fetch(:allow_comments, true) %> -<% cache [locale_and_user_status(comment), comment, commentable_cache_key(comment.commentable), comment.author, (comment_flags[comment.id] if comment_flags)] do %> +<% admin_layout = local_assigns.fetch(:admin_layout, false) %> +<% cache [locale_and_user_status(comment), comment, commentable_cache_key(comment.commentable), comment.author, (comment_flags[comment.id] if comment_flags), (admin_layout if admin_layout)] do %>
    • <% if comment.hidden? || comment.user.hidden? %> @@ -30,7 +31,14 @@
      <% if comment.as_administrator? %> - <%= t("comments.comment.admin") %> #<%= comment.administrator_id %> + + <%= t("comments.comment.admin") %> + <% if admin_layout %> + <%= Administrator.find(comment.administrator_id).description_or_name %> + <% else %> + #<%= comment.administrator_id %> + <% end %> + <% elsif comment.as_moderator? %> <%= t("comments.comment.moderator") %> #<%= comment.moderator_id %> <% else %> diff --git a/app/views/comments/_comment_tree.html.erb b/app/views/comments/_comment_tree.html.erb index c75bcf2a7..0c4e3b32a 100644 --- a/app/views/comments/_comment_tree.html.erb +++ b/app/views/comments/_comment_tree.html.erb @@ -1,7 +1,8 @@ <% commentable = comment_tree.commentable %> <% valuation = local_assigns.fetch(:valuation, false) %> <% allow_comments = local_assigns.fetch(:allow_comments, true) %> -<% cache [locale_and_user_status, comment_tree.order, commentable_cache_key(commentable), comment_tree.comments, comment_tree.comment_authors, commentable.comments_count, comment_flags] do %> +<% admin_layout = local_assigns.fetch(:admin_layout, false) %> +<% cache [locale_and_user_status, comment_tree.order, commentable_cache_key(commentable), comment_tree.comments, comment_tree.comment_authors, commentable.comments_count, comment_flags, admin_layout] do %>
      @@ -46,7 +47,8 @@ valuation: valuation, allow_votes: !valuation, allow_actions: !valuation, - allow_comments: allow_comments } %> + allow_comments: allow_comments, + admin_layout: admin_layout } %> <% end %> <%= paginate comment_tree.root_comments %>
      diff --git a/app/views/valuation/budget_investments/_valuation_comments.html.erb b/app/views/valuation/budget_investments/_valuation_comments.html.erb index 9f13ccb3d..0eacfa825 100644 --- a/app/views/valuation/budget_investments/_valuation_comments.html.erb +++ b/app/views/valuation/budget_investments/_valuation_comments.html.erb @@ -1,8 +1,10 @@

      <%= t("valuation.budget_investments.valuation_comments") %>

      <% unless @comment_tree.nil? %> - <%= render partial: "/comments/comment_tree", locals: { comment_tree: @comment_tree, - comment_flags: @comment_flags, - display_comments_count: false, - valuation: true, - allow_comments: !@budget.finished? } %> + <%= render partial: "/comments/comment_tree", locals: { + comment_tree: @comment_tree, + comment_flags: @comment_flags, + display_comments_count: false, + valuation: true, + allow_comments: !@budget.finished?, + admin_layout: true } %> <% end %> diff --git a/spec/features/comments/budget_investments_spec.rb b/spec/features/comments/budget_investments_spec.rb index b5f064d93..5aeb77f7f 100644 --- a/spec/features/comments/budget_investments_spec.rb +++ b/spec/features/comments/budget_investments_spec.rb @@ -382,49 +382,109 @@ describe "Commenting Budget::Investments" do end describe "Administrators" do - scenario "can create comment as an administrator", :js do - admin = create(:administrator) + context "comment as administrator" do + scenario "can create comment", :js do + admin = create(:administrator) - login_as(admin.user) - visit budget_investment_path(investment.budget, investment) + login_as(admin.user) + visit budget_investment_path(investment.budget, investment) - fill_in "comment-body-budget_investment_#{investment.id}", with: "I am your Admin!" - check "comment-as-administrator-budget_investment_#{investment.id}" - click_button "Publish comment" + fill_in "comment-body-budget_investment_#{investment.id}", with: "I am your Admin!" + check "comment-as-administrator-budget_investment_#{investment.id}" + click_button "Publish comment" - within "#comments" do - expect(page).to have_content "I am your Admin!" - expect(page).to have_content "Administrator ##{admin.id}" + within "#comments" do + expect(page).to have_content "I am your Admin!" + expect(page).to have_content "Administrator ##{admin.id}" + expect(page).to have_css "div.is-admin" + expect(page).to have_css "img.admin-avatar" + end + end + + scenario "display administrator description on admin views", :js do + admin = create(:administrator, description: "user description") + + login_as(admin.user) + + visit admin_budget_budget_investment_path(investment.budget, investment) + + fill_in "comment-body-budget_investment_#{investment.id}", with: "I am your Admin!" + check "comment-as-administrator-budget_investment_#{investment.id}" + click_button "Publish comment" + + visit admin_budget_budget_investment_path(investment.budget, investment) + + within "#comments" do + expect(page).to have_content "I am your Admin!" + expect(page).to have_content "Administrator user description" + expect(page).to have_css "div.is-admin" + expect(page).to have_css "img.admin-avatar" + end + end + + scenario "display administrator id on public views", :js do + admin = create(:administrator, description: "user description") + + login_as(admin.user) + visit admin_budget_budget_investment_path(investment.budget, investment) + + fill_in "comment-body-budget_investment_#{investment.id}", with: "I am your Admin!" + check "comment-as-administrator-budget_investment_#{investment.id}" + click_button "Publish comment" + + within "#comments" do + expect(page).to have_content "I am your Admin!" + expect(page).to have_content "Administrator ##{admin.id}" + expect(page).to have_css "div.is-admin" + expect(page).to have_css "img.admin-avatar" + end + end + + scenario "can create reply as an administrator", :js do + citizen = create(:user, username: "Ana") + manuela = create(:user, username: "Manuela") + admin = create(:administrator, user: manuela) + comment = create(:comment, commentable: investment, user: citizen) + + login_as(manuela) + visit budget_investment_path(investment.budget, investment) + + click_link "Reply" + + within "#js-comment-form-comment_#{comment.id}" do + fill_in "comment-body-comment_#{comment.id}", with: "Top of the world!" + check "comment-as-administrator-comment_#{comment.id}" + click_button "Publish reply" + end + + within "#comment_#{comment.id}" do + expect(page).to have_content "Top of the world!" + expect(page).to have_content "Administrator ##{admin.id}" + expect(page).to have_css "img.admin-avatar" + end + + expect(page).not_to have_selector("#js-comment-form-comment_#{comment.id}", visible: true) expect(page).to have_css "div.is-admin" - expect(page).to have_css "img.admin-avatar" - end - end - - scenario "can create reply as an administrator", :js do - citizen = create(:user, username: "Ana") - manuela = create(:user, username: "Manuela") - admin = create(:administrator, user: manuela) - comment = create(:comment, commentable: investment, user: citizen) - - login_as(manuela) - visit budget_investment_path(investment.budget, investment) - - click_link "Reply" - - within "#js-comment-form-comment_#{comment.id}" do - fill_in "comment-body-comment_#{comment.id}", with: "Top of the world!" - check "comment-as-administrator-comment_#{comment.id}" - click_button "Publish reply" end - within "#comment_#{comment.id}" do - expect(page).to have_content "Top of the world!" - expect(page).to have_content "Administrator ##{admin.id}" - expect(page).to have_css "div.is-admin" - expect(page).to have_css "img.admin-avatar" + scenario "public users not see admin description", :js do + manuela = create(:user, username: "Manuela") + admin = create(:administrator, user: manuela) + comment = create(:comment, + commentable: investment, + user: manuela, + administrator_id: admin.id) + + visit budget_investment_path(investment.budget, investment) + + within "#comment_#{comment.id}" do + expect(page).to have_content comment.body + expect(page).to have_content "Administrator ##{admin.id}" + expect(page).to have_css "img.admin-avatar" + expect(page).to have_css "div.is-admin" + end end - expect(page).not_to have_selector("#js-comment-form-comment_#{comment.id}", visible: true) end scenario "can not comment as a moderator" do From 28815cb290a94081274e16eaf698753256f5e0eb Mon Sep 17 00:00:00 2001 From: lalo Date: Wed, 27 Mar 2019 12:13:43 +0100 Subject: [PATCH 30/55] Create new email template for new evaluation comment created and add it to system_emails view --- .../admin/system_emails_controller.rb | 16 +++++- app/helpers/mailer_helper.rb | 12 ++++ app/mailers/mailer.rb | 7 +++ app/models/budget/investment.rb | 6 ++ app/views/mailer/evaluation_comment.html.erb | 19 +++++++ config/locales/en/admin.yml | 4 ++ config/locales/en/mailers.yml | 6 ++ config/locales/es/admin.yml | 4 ++ config/locales/es/mailers.yml | 6 ++ lib/evaluation_comment_email.rb | 31 ++++++++++ spec/features/admin/system_emails_spec.rb | 28 ++++++++- .../budget_investments_valuation_spec.rb | 33 +++++++++-- spec/lib/evaluation_comment_email_spec.rb | 57 +++++++++++++++++++ spec/models/budget/investment_spec.rb | 27 +++++++++ 14 files changed, 248 insertions(+), 8 deletions(-) create mode 100644 app/views/mailer/evaluation_comment.html.erb create mode 100644 lib/evaluation_comment_email.rb create mode 100644 spec/lib/evaluation_comment_email_spec.rb diff --git a/app/controllers/admin/system_emails_controller.rb b/app/controllers/admin/system_emails_controller.rb index 689425569..f70c4bd53 100644 --- a/app/controllers/admin/system_emails_controller.rb +++ b/app/controllers/admin/system_emails_controller.rb @@ -14,7 +14,8 @@ class Admin::SystemEmailsController < Admin::BaseController direct_message_for_receiver: %w[view edit_info], direct_message_for_sender: %w[view edit_info], email_verification: %w[view edit_info], - user_invite: %w[view edit_info] + user_invite: %w[view edit_info], + evaluation_comment: %w[view edit_info] } end @@ -34,6 +35,8 @@ class Admin::SystemEmailsController < Admin::BaseController load_sample_user when "user_invite" @subject = t("mailers.user_invite.subject", org_name: Setting["org_name"]) + when "evaluation_comment" + load_sample_valuation_comment end end @@ -97,6 +100,17 @@ class Admin::SystemEmailsController < Admin::BaseController end end + def load_sample_valuation_comment + comment = Comment.where(commentable_type: "Budget::Investment").last + if comment + @email = EvaluationCommentEmail.new(comment) + @email_to = @email.to.first + else + redirect_to admin_system_emails_path, + alert: t("admin.system_emails.alert.no_evaluation_comments") + end + end + def load_sample_user @user = User.last @token = @user.email_verification_token || SecureRandom.hex diff --git a/app/helpers/mailer_helper.rb b/app/helpers/mailer_helper.rb index c3bda48b9..d850b8dd3 100644 --- a/app/helpers/mailer_helper.rb +++ b/app/helpers/mailer_helper.rb @@ -8,4 +8,16 @@ module MailerHelper return budget_investment_url(commentable.budget_id, commentable) if commentable.is_a?(Budget::Investment) end + def valuation_comments_url(commentable) + admin_budget_budget_investment_url( commentable.budget, commentable, anchor: "comments") + end + + def valuation_comments_link(commentable) + link_to( + commentable.title, + valuation_comments_url(@email.commentable), + target: :blank, + style: "color: #2895F1; text-decoration:none;" + ) + end end diff --git a/app/mailers/mailer.rb b/app/mailers/mailer.rb index d8fa120a4..9defb0f15 100644 --- a/app/mailers/mailer.rb +++ b/app/mailers/mailer.rb @@ -120,6 +120,13 @@ class Mailer < ApplicationMailer mail(to: @email_to, from: @newsletter.from, subject: @newsletter.subject) end + def evaluation_comment(comment, to) + @email = EvaluationCommentEmail.new(comment) + @email_to = to + + mail(to: @email_to.email, subject: @email.subject) if @email.can_be_sent? + end + private def with_user(user, &block) diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index 2eaf801c1..6750d2d75 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -377,6 +377,12 @@ class Budget milestones.published.with_status.order_by_publication_date.last&.status_id end + def admin_and_valuator_users_associated + valuator_users = (valuator_groups.map(&:valuators) + valuators).flatten + all_users = valuator_users << administrator + all_users.compact.uniq + end + private def set_denormalized_ids diff --git a/app/views/mailer/evaluation_comment.html.erb b/app/views/mailer/evaluation_comment.html.erb new file mode 100644 index 000000000..0d695766d --- /dev/null +++ b/app/views/mailer/evaluation_comment.html.erb @@ -0,0 +1,19 @@ + + +

      + <%= t("mailers.evaluation_comment.title", investment: @email.commentable.title) %> +

      + +

      + <%= t("mailers.evaluation_comment.hi") %> <%= @email_to.name %>, +

      + +

      + <%= t("mailers.evaluation_comment.new_comment_by_html", commenter: @email.comment.author.name, investment: valuation_comments_link(@email.commentable)) %> +

      + + <%= t("mailers.evaluation_comment.commenter_info", commenter: @email.comment.author.name, time: l(@email.comment.created_at)) %> +
      + <%= simple_format text_with_links(@email.comment.body), {}, sanitize: false %> +
      + diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index e3c5c597b..54958daac 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -875,6 +875,9 @@ en: user_invite: title: "User Invitation" description: "Sent to the person that has been invited to register an account." + evaluation_comment: + title: "New evaluation comment" + description: "Sent to administrators and evaluators related to commented investment" edit_info: "You can edit this email in" message_title: "Message's Title" message_body: "This is a sample of message's content." @@ -882,6 +885,7 @@ en: no_investments: "There aren't any budget investment created. Some example data is needed in order to preview the email." no_comments: "There aren't any comments created. Some example data is needed in order to preview the email." no_replies: "There aren't any replies created. Some example data is needed in order to preview the email." + no_evaluation_comments: "There aren't any evaluation comments created. Some example data is needed in order to preview the email." emails_download: index: title: Emails download diff --git a/config/locales/en/mailers.yml b/config/locales/en/mailers.yml index c92660de4..2a0e23da2 100644 --- a/config/locales/en/mailers.yml +++ b/config/locales/en/mailers.yml @@ -70,6 +70,12 @@ en: hi: "Dear user," thanks: "Thank you again for participating." sincerely: "Sincererly" + evaluation_comment: + subject: "New evaluation comment" + title: New evaluation comment for %{investment} + hi: Hi + new_comment_by_html: There is a new evaluation comment from %{commenter} to the budget investment %{investment} + commenter_info: "%{commenter}, %{time}:" new_actions_notification_rake_created: subject: "More news about your citizen proposal" hi: "Hello %{name}," diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 34fc7510b..4f7feb768 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -874,6 +874,9 @@ es: user_invite: title: "Invitación de usuarios" description: "Enviado a la persona que ha sido invitada a registrar una cuenta." + evaluation_comment: + title: "Nuevo comentario de evaluación" + description: "Enviado a administradores y evaluadores del presupuesto." edit_info: "Puedes editar este email en" message_title: "Título del mensaje" message_body: "Este es un ejemplo de contenido de un mensaje." @@ -881,6 +884,7 @@ es: no_investments: "No se ha creado ningún proyecto de gasto. Se necesita algún ejemplo para poder previsualizar el email." no_comments: "No se ha creado ningún comentario. Se necesita algún ejemplo para poder previsualizar el email." no_replies: "No se ha creado ninguna respuesta. Se necesita algún ejemplo para poder previsualizar el email." + no_evaluation_comments: "No se ha creado ningún comentario de evaluación. Se necesita algún ejemplo para poder previsualizar el email." emails_download: index: title: Descarga de emails diff --git a/config/locales/es/mailers.yml b/config/locales/es/mailers.yml index b1d0de74b..1483c1fae 100644 --- a/config/locales/es/mailers.yml +++ b/config/locales/es/mailers.yml @@ -70,6 +70,12 @@ es: hi: "Estimado/a usuario/a" thanks: "Gracias de nuevo por tu participación." sincerely: "Atentamente" + evaluation_comment: + subject: "Nuevo comentario de evaluación" + title: Nuevo comentario de evaluación para %{investment} + hi: Hola + new_comment_by_html: Hay un nuevo comentario de evaluación de %{commenter} en el presupuesto participativo %{investment} + commenter_info: "%{commenter}, %{time}" new_actions_notification_rake_created: subject: "Más novedades de tu propuesta ciudadana" hi: "Hola %{name}," diff --git a/lib/evaluation_comment_email.rb b/lib/evaluation_comment_email.rb new file mode 100644 index 000000000..6321e43dd --- /dev/null +++ b/lib/evaluation_comment_email.rb @@ -0,0 +1,31 @@ +class EvaluationCommentEmail + attr_reader :comment + + def initialize(comment) + @comment = comment + end + + def commentable + comment.commentable + end + + def to + @to ||= related_users + end + + def subject + I18n.t("mailers.evaluation_comment.subject") + end + + def can_be_sent? + commentable.present? && to.any? + end + + private + def related_users + return [] if comment.commentable.nil? + comment.commentable + .admin_and_valuator_users_associated + .reject { |associated_user| associated_user.user == comment.author } + end +end diff --git a/spec/features/admin/system_emails_spec.rb b/spec/features/admin/system_emails_spec.rb index 4c71f4f3f..b1aed5bed 100644 --- a/spec/features/admin/system_emails_spec.rb +++ b/spec/features/admin/system_emails_spec.rb @@ -14,7 +14,8 @@ describe "System Emails" do let(:system_emails) do %w[proposal_notification_digest budget_investment_created budget_investment_selected budget_investment_unfeasible budget_investment_unselected comment reply - direct_message_for_receiver direct_message_for_sender email_verification user_invite] + direct_message_for_receiver direct_message_for_sender email_verification user_invite + evaluation_comment] end context "System emails" do @@ -243,6 +244,31 @@ describe "System Emails" do visit admin_system_email_view_path("reply") expect(page).to have_content "There aren't any replies created." expect(page).to have_content "Some example data is needed in order to preview the email." + + visit admin_system_email_view_path("evaluation_comment") + expect(page).to have_content "There aren't any evaluation comments created." + expect(page).to have_content "Some example data is needed in order to preview the email." + end + + scenario "#evaluation_comment" do + admin = create(:administrator, user: create(:user, username: "Baby Doe")) + investment = create(:budget_investment, + title: "Cleaner city", + heading: heading, + author: user, + administrator: admin) + comment = create(:comment, :valuation, commentable: investment) + + visit admin_system_email_view_path("evaluation_comment") + + expect(page).to have_content "New evaluation comment for Cleaner city" + expect(page).to have_content "Hi #{admin.name}" + expect(page).to have_content "There is a new evaluation comment from #{comment.user.name} "\ + "to the budget investment Cleaner city" + expect(page).to have_content comment.body + + expect(page).to have_link "Cleaner city", + href: admin_budget_budget_investment_url( investment.budget, investment, anchor: "comments") end end diff --git a/spec/features/comments/budget_investments_valuation_spec.rb b/spec/features/comments/budget_investments_valuation_spec.rb index 1acb5819c..fd9bf56c7 100644 --- a/spec/features/comments/budget_investments_valuation_spec.rb +++ b/spec/features/comments/budget_investments_valuation_spec.rb @@ -171,7 +171,7 @@ describe "Internal valuation comments on Budget::Investments" do scenario "Create comment", :js do visit valuation_budget_budget_investment_path(budget, investment) - fill_in "comment-body-budget_investment_#{investment.id}", with: "Have you thought about...?" + fill_in "Leave your comment", with: "Have you thought about...?" click_button "Publish comment" within "#comments" do @@ -199,7 +199,7 @@ describe "Internal valuation comments on Budget::Investments" do click_link "Reply" within "#js-comment-form-comment_#{comment.id}" do - fill_in "comment-body-comment_#{comment.id}", with: "It will be done next week." + fill_in "Leave your comment", with: "It will be done next week." click_button "Publish reply" end @@ -261,8 +261,8 @@ describe "Internal valuation comments on Budget::Investments" do login_as(admin_user) visit valuation_budget_budget_investment_path(budget, investment) - fill_in "comment-body-budget_investment_#{investment.id}", with: "I am your Admin!" - check "comment-as-administrator-budget_investment_#{investment.id}" + fill_in "Leave your comment", with: "I am your Admin!" + check "Comment as admin" click_button "Publish comment" within "#comments" do @@ -282,8 +282,8 @@ describe "Internal valuation comments on Budget::Investments" do click_link "Reply" within "#js-comment-form-comment_#{comment.id}" do - fill_in "comment-body-comment_#{comment.id}", with: "Top of the world!" - check "comment-as-administrator-comment_#{comment.id}" + fill_in "Leave your comment", with: "Top of the world!" + check "Comment as admin" click_button "Publish reply" end @@ -298,4 +298,25 @@ describe "Internal valuation comments on Budget::Investments" do end end + scenario "Send email notification", :js do + ActionMailer::Base.deliveries = [] + + login_as(admin_user) + + expect(ActionMailer::Base.deliveries).to eq([]) + + visit valuation_budget_budget_investment_path(budget, investment) + fill_in "Leave your comment", with: "I am your Admin!" + check "Comment as admin" + click_button "Publish comment" + + within "#comments" do + expect(page).to have_content("I am your Admin!") + end + + expect(ActionMailer::Base.deliveries.count).to eq(1) + expect(ActionMailer::Base.deliveries.first.to).to eq([valuator_user.email]) + expect(ActionMailer::Base.deliveries.first.subject).to eq("New evaluation comment") + end + end diff --git a/spec/lib/evaluation_comment_email_spec.rb b/spec/lib/evaluation_comment_email_spec.rb new file mode 100644 index 000000000..18b85c197 --- /dev/null +++ b/spec/lib/evaluation_comment_email_spec.rb @@ -0,0 +1,57 @@ +require "rails_helper" + +describe EvaluationCommentEmail do + + let(:author) { create(:user) } + let(:administrator) { create(:administrator)} + let(:investment) { create(:budget_investment, author: author, administrator: administrator) } + let(:commenter) { create(:user, email: "email@commenter.org") } + let(:comment) { create(:comment, commentable: investment, user: commenter) } + let(:comment_email) { EvaluationCommentEmail.new(comment) } + + describe "#commentable" do + it "returns the commentable object that contains the replied comment" do + expect(comment_email.commentable).to eq investment + end + end + + describe "#to" do + it "returns an array of users related to investment" do + expect(comment_email.to).to eq [administrator] + end + + it "returns empty array if commentable not exists" do + allow(comment).to receive(:commentable).and_return(nil) + expect(comment_email.to).to eq [] + end + + it "returns empty array if not associated users" do + allow(investment).to receive(:admin_and_valuator_users_associated).and_return([]) + expect(comment_email.to).to eq [] + end + end + + describe "#subject" do + it "returns the translation for a evaluation comment email subject" do + expect(comment_email.subject).to eq "New evaluation comment" + end + end + + describe "#can_be_sent?" do + it "returns true if investment has any associated users" do + expect(comment_email.can_be_sent?).to be true + end + + it "returns false if the comment doesn't exist" do + comment.update(commentable: nil) + + expect(comment_email.can_be_sent?).to be false + end + + it "returns false if recipients are empty" do + investment.administrator = nil + expect(comment_email.can_be_sent?).to be false + end + end + +end diff --git a/spec/models/budget/investment_spec.rb b/spec/models/budget/investment_spec.rb index 044dead99..c842621ff 100644 --- a/spec/models/budget/investment_spec.rb +++ b/spec/models/budget/investment_spec.rb @@ -1218,4 +1218,31 @@ describe Budget::Investment do end end end + + describe "admin_and_valuator_users_associated" do + let(:investment) { create(:budget_investment) } + let(:valuator_group) { create(:valuator_group) } + let(:valuator) { create(:valuator) } + let(:administrator) { create(:administrator) } + + it "returns empty array if not valuators or administrator assigned" do + expect(investment.admin_and_valuator_users_associated).to eq([]) + end + + it "returns all valuator and administrator users" do + valuator_group.valuators << valuator + investment.valuator_groups << valuator_group + expect(investment.admin_and_valuator_users_associated).to eq([valuator]) + investment.administrator = administrator + expect(investment.admin_and_valuator_users_associated).to eq([valuator, administrator]) + end + + it "returns uniq valuators or administrator users" do + valuator_group.valuators << valuator + investment.valuator_groups << valuator_group + investment.valuators << valuator + investment.administrator = administrator + expect(investment.admin_and_valuator_users_associated).to eq([valuator, administrator]) + end + end end From 8061bfa5e8d467f1b44bc1fa7a95318bd0f308d1 Mon Sep 17 00:00:00 2001 From: lalo Date: Wed, 27 Mar 2019 13:40:46 +0100 Subject: [PATCH 31/55] Send email to users related with an investment when valuation comment is created --- app/controllers/comments_controller.rb | 4 ++++ app/helpers/mailer_helper.rb | 2 +- app/models/evaluation_comment_notifier.rb | 16 ++++++++++++++++ app/models/setting.rb | 1 + config/locales/ar/mailers.yml | 4 ++-- config/locales/ast/mailers.yml | 4 ++-- config/locales/ca/mailers.yml | 4 ++-- config/locales/de-DE/mailers.yml | 4 ++-- config/locales/en/mailers.yml | 6 +++--- config/locales/en/settings.yml | 2 ++ config/locales/es-PE/mailers.yml | 4 ++-- config/locales/es/mailers.yml | 6 +++--- config/locales/es/settings.yml | 2 ++ config/locales/fr/mailers.yml | 4 ++-- config/locales/gl/mailers.yml | 4 ++-- config/locales/he/mailers.yml | 4 ++-- config/locales/id-ID/mailers.yml | 4 ++-- config/locales/it/mailers.yml | 4 ++-- config/locales/nl/mailers.yml | 4 ++-- config/locales/pl-PL/mailers.yml | 4 ++-- config/locales/pt-BR/mailers.yml | 4 ++-- config/locales/ru/mailers.yml | 4 ++-- config/locales/sl-SI/mailers.yml | 4 ++-- config/locales/so-SO/mailers.yml | 4 ++-- config/locales/sq-AL/mailers.yml | 4 ++-- config/locales/sv-SE/mailers.yml | 4 ++-- config/locales/val/mailers.yml | 4 ++-- config/locales/zh-CN/mailers.yml | 4 ++-- config/locales/zh-TW/mailers.yml | 4 ++-- 29 files changed, 74 insertions(+), 49 deletions(-) create mode 100644 app/models/evaluation_comment_notifier.rb diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 47434ba16..18ee03e09 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -14,6 +14,7 @@ class CommentsController < ApplicationController if @comment.save CommentNotifier.new(comment: @comment).process add_notification @comment + EvaluationCommentNotifier.new(comment: @comment).process if send_evaluation_notification? else render :new end @@ -107,4 +108,7 @@ class CommentsController < ApplicationController end end + def send_evaluation_notification? + @comment.valuation && Setting["feature.valuation_comment_notification"] + end end diff --git a/app/helpers/mailer_helper.rb b/app/helpers/mailer_helper.rb index d850b8dd3..1f6f4d3a6 100644 --- a/app/helpers/mailer_helper.rb +++ b/app/helpers/mailer_helper.rb @@ -9,7 +9,7 @@ module MailerHelper end def valuation_comments_url(commentable) - admin_budget_budget_investment_url( commentable.budget, commentable, anchor: "comments") + admin_budget_budget_investment_url(commentable.budget, commentable, anchor: "comments") end def valuation_comments_link(commentable) diff --git a/app/models/evaluation_comment_notifier.rb b/app/models/evaluation_comment_notifier.rb new file mode 100644 index 000000000..e1231298c --- /dev/null +++ b/app/models/evaluation_comment_notifier.rb @@ -0,0 +1,16 @@ +class EvaluationCommentNotifier + def initialize(args = {}) + @comment = args.fetch(:comment) + end + + def process + send_evaluation_comment_email + end + + private + def send_evaluation_comment_email + EvaluationCommentEmail.new(@comment).to.each do |to| + Mailer.evaluation_comment(@comment, to).deliver_later + end + end +end diff --git a/app/models/setting.rb b/app/models/setting.rb index 6210b6ba5..666252f59 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -58,6 +58,7 @@ class Setting < ApplicationRecord "feature.allow_attached_documents": true, "feature.allow_images": true, "feature.help_page": true, + "feature.valuation_comment_notification": true, "homepage.widgets.feeds.debates": true, "homepage.widgets.feeds.processes": true, "homepage.widgets.feeds.proposals": true, diff --git a/config/locales/ar/mailers.yml b/config/locales/ar/mailers.yml index db0ab258f..7cc1434e2 100644 --- a/config/locales/ar/mailers.yml +++ b/config/locales/ar/mailers.yml @@ -3,7 +3,7 @@ ar: no_reply: "تم إرسال هذه الرسالة من عنوان بريد إلكتروني لا يقبل الردود." comment: hi: مرحبا - new_comment_by_html: هناك تعليق جديد من %{commenter} + new_comment_by_html: هناك تعليق جديد من %{commenter} subject: تم التعليق على %{commentable} title: تعليق جديد config: @@ -16,7 +16,7 @@ ar: title: أكد حسابك باستخدام الرابط التالي reply: hi: مرحبا - new_reply_by_html: وهناك رد جديد من %{commenter} على التعليق الخاص بك + new_reply_by_html: وهناك رد جديد من %{commenter} على التعليق الخاص بك subject: يوجد رد على تعليقك title: اجابة جديدة على تعليقك unfeasible_spending_proposal: diff --git a/config/locales/ast/mailers.yml b/config/locales/ast/mailers.yml index 9bb3ab25c..a0a17c6d7 100644 --- a/config/locales/ast/mailers.yml +++ b/config/locales/ast/mailers.yml @@ -3,7 +3,7 @@ ast: no_reply: "Este mensaje se ha enviado desde una dirección de correo electrónico que no admite respuestas." comment: hi: Hola - new_comment_by_html: Hay un nuevo comentario de %{commenter} en + new_comment_by_html: Hay un nuevo comentario de %{commenter} en subject: Alguien ha comentado en tu %{commentable} title: Nuevo comentario config: @@ -16,7 +16,7 @@ ast: title: Verifica tu cuenta con el siguiente enlace reply: hi: Hola - new_reply_by_html: Hay una nueva respuesta de %{commenter} a tu comentario en + new_reply_by_html: Hay una nueva respuesta de %{commenter} a tu comentario en subject: Alguien ha respondido a tu comentario title: Nueva respuesta a tu comentario unfeasible_spending_proposal: diff --git a/config/locales/ca/mailers.yml b/config/locales/ca/mailers.yml index fc3c46535..6f4014ce2 100644 --- a/config/locales/ca/mailers.yml +++ b/config/locales/ca/mailers.yml @@ -3,7 +3,7 @@ ca: no_reply: "Aquest missatge s'ha enviat des d'una adreça de correu electrònic que no admet respostes." comment: hi: Hola - new_comment_by_html: Hi ha un nou comentari de %{commenter} en + new_comment_by_html: Hi ha un nou comentari de %{commenter} en subject: Algú ha comentat en el teu %{commentable} title: Nou comentari config: @@ -16,7 +16,7 @@ ca: title: Verifica el teu compte amb el següent enllaç reply: hi: Hola - new_reply_by_html: Hi ha una nova resposta de %{commenter} al teu comentari en + new_reply_by_html: Hi ha una nova resposta de %{commenter} al teu comentari en subject: Algú ha respost al teu comentari title: Nova resposta al teu comentari unfeasible_spending_proposal: diff --git a/config/locales/de-DE/mailers.yml b/config/locales/de-DE/mailers.yml index 3bfbaa81a..1b9fbb7b7 100644 --- a/config/locales/de-DE/mailers.yml +++ b/config/locales/de-DE/mailers.yml @@ -3,7 +3,7 @@ de: no_reply: "Diese Nachricht wurde von einer E-Mail-Adresse gesendet, die keine Antworten akzeptiert." comment: hi: Hallo - new_comment_by_html: Es gibt einen neuen Kommentar von %{commenter} + new_comment_by_html: Es gibt einen neuen Kommentar von %{commenter} subject: Jemand hat Ihre %{commentable} kommentiert. title: Neuer Kommentar config: @@ -16,7 +16,7 @@ de: title: Bestätigen Sie Ihr Konto über den folgenden Link reply: hi: Hallo - new_reply_by_html: Es gibt eine neue Antwort von %{commenter} auf Ihren Kommentar + new_reply_by_html: Es gibt eine neue Antwort von %{commenter} auf Ihren Kommentar subject: Jemand hat auf Ihren Kommentar geantwortet title: Neue Antwort auf Ihren Kommentar unfeasible_spending_proposal: diff --git a/config/locales/en/mailers.yml b/config/locales/en/mailers.yml index 2a0e23da2..a1151d57d 100644 --- a/config/locales/en/mailers.yml +++ b/config/locales/en/mailers.yml @@ -3,7 +3,7 @@ en: no_reply: "This message was sent from an email address that does not accept replies." comment: hi: Hi - new_comment_by_html: There is a new comment from %{commenter} + new_comment_by_html: There is a new comment from %{commenter} subject: Someone has commented on your %{commentable} title: New comment config: @@ -17,7 +17,7 @@ en: title: Confirm your account using the following link reply: hi: Hi - new_reply_by_html: There is a new response from %{commenter} to your comment on + new_reply_by_html: There is a new response from %{commenter} to your comment on subject: Someone has responded to your comment title: New response to your comment proposal_notification_digest: @@ -74,7 +74,7 @@ en: subject: "New evaluation comment" title: New evaluation comment for %{investment} hi: Hi - new_comment_by_html: There is a new evaluation comment from %{commenter} to the budget investment %{investment} + new_comment_by_html: There is a new evaluation comment from %{commenter} to the budget investment %{investment} commenter_info: "%{commenter}, %{time}:" new_actions_notification_rake_created: subject: "More news about your citizen proposal" diff --git a/config/locales/en/settings.yml b/config/locales/en/settings.yml index cb0363f48..932221c98 100644 --- a/config/locales/en/settings.yml +++ b/config/locales/en/settings.yml @@ -116,6 +116,8 @@ en: public_stats_description: "Display public stats in the Administration panel" help_page: "Help page" help_page_description: 'Displays a Help menu that contains a page with an info section about each enabled feature. Also custom pages and menus can be created in the "Custom pages" and "Custom content blocks" sections' + valuation_comment_notification: "Valuation comment notification" + valuation_comment_notification_description: "Send an email to all associated users except valuation commenter to budget investment when a new valuation comment is created" map: latitude: "Latitude" latitude_description: "Latitude to show the map position" diff --git a/config/locales/es-PE/mailers.yml b/config/locales/es-PE/mailers.yml index ea972263a..f04a585ab 100644 --- a/config/locales/es-PE/mailers.yml +++ b/config/locales/es-PE/mailers.yml @@ -3,7 +3,7 @@ es-PE: no_reply: "Este mensaje se ha enviado desde una dirección de correo electrónico que no admite respuestas." comment: hi: Hola - new_comment_by_html: Hay un nuevo comentario de %{commenter} en + new_comment_by_html: Hay un nuevo comentario de %{commenter} en subject: Alguien ha comentado en tu %{commentable} title: Nuevo comentario config: @@ -16,7 +16,7 @@ es-PE: title: Verifica tu cuenta con el siguiente enlace reply: hi: Hola - new_reply_by_html: Hay una nueva respuesta de %{commenter} a tu comentario en + new_reply_by_html: Hay una nueva respuesta de %{commenter} a tu comentario en subject: Alguien ha respondido a tu comentario title: Nueva respuesta a tu comentario unfeasible_spending_proposal: diff --git a/config/locales/es/mailers.yml b/config/locales/es/mailers.yml index 1483c1fae..92a810b39 100644 --- a/config/locales/es/mailers.yml +++ b/config/locales/es/mailers.yml @@ -3,7 +3,7 @@ es: no_reply: "Este mensaje se ha enviado desde una dirección de correo electrónico que no admite respuestas." comment: hi: Hola - new_comment_by_html: Hay un nuevo comentario de %{commenter} en + new_comment_by_html: Hay un nuevo comentario de %{commenter} en subject: Alguien ha comentado en tu %{commentable} title: Nuevo comentario config: @@ -17,7 +17,7 @@ es: title: Verifica tu cuenta con el siguiente enlace reply: hi: Hola - new_reply_by_html: Hay una nueva respuesta de %{commenter} a tu comentario en + new_reply_by_html: Hay una nueva respuesta de %{commenter} a tu comentario en subject: Alguien ha respondido a tu comentario title: Nueva respuesta a tu comentario proposal_notification_digest: @@ -74,7 +74,7 @@ es: subject: "Nuevo comentario de evaluación" title: Nuevo comentario de evaluación para %{investment} hi: Hola - new_comment_by_html: Hay un nuevo comentario de evaluación de %{commenter} en el presupuesto participativo %{investment} + new_comment_by_html: Hay un nuevo comentario de evaluación de %{commenter} en el presupuesto participativo %{investment} commenter_info: "%{commenter}, %{time}" new_actions_notification_rake_created: subject: "Más novedades de tu propuesta ciudadana" diff --git a/config/locales/es/settings.yml b/config/locales/es/settings.yml index 42b0726d1..fc1c94f4d 100644 --- a/config/locales/es/settings.yml +++ b/config/locales/es/settings.yml @@ -116,6 +116,8 @@ es: public_stats_description: "Muestra las estadísticas públicas en el panel de Administración" help_page: "Página de ayuda" help_page_description: 'Muestra un menú Ayuda que contiene una página con una sección de información sobre cada funcionalidad habilitada. También se pueden crear páginas y menús personalizados en las secciones "Personalizar páginas" y "Personalizar bloques"' + valuation_comment_notification: "Notificar comentarios de evaluación" + valuation_comment_notification_description: "Envía un email a todos los usuarios menos al que haya comentado asociados a un presupuesto participativo cuando se cree un nuevo comentario de evaluación" map: latitude: "Latitud" latitude_description: "Latitud para mostrar la posición del mapa" diff --git a/config/locales/fr/mailers.yml b/config/locales/fr/mailers.yml index 4c46ab118..4df25de69 100644 --- a/config/locales/fr/mailers.yml +++ b/config/locales/fr/mailers.yml @@ -3,7 +3,7 @@ fr: no_reply: "Ce message a été envoyé d'une adresse qui n'accepte pas les réponses." comment: hi: Bonjour - new_comment_by_html: Il y a un nouveau commentaire de %{commenter} + new_comment_by_html: Il y a un nouveau commentaire de %{commenter} subject: Quelqu'un a commenté votre %{commentable} title: Nouveau commentaire config: @@ -17,7 +17,7 @@ fr: title: Confirmer votre compte en utilisant le lien suivant reply: hi: Bonjour - new_reply_by_html: Il y a une nouvelle réponse de %{commenter} à votre commentaire sur + new_reply_by_html: Il y a une nouvelle réponse de %{commenter} à votre commentaire sur subject: Quelqu'un a répondu à votre commentaire title: Nouvelle réponse à votre commentaire unfeasible_spending_proposal: diff --git a/config/locales/gl/mailers.yml b/config/locales/gl/mailers.yml index e8b37c71c..fb4b6e634 100644 --- a/config/locales/gl/mailers.yml +++ b/config/locales/gl/mailers.yml @@ -3,7 +3,7 @@ gl: no_reply: "Esta mensaxe enviouse desde un enderezo de correo electrónico que non admite respostas." comment: hi: Ola - new_comment_by_html: Hai un novo comentario de %{commenter} en + new_comment_by_html: Hai un novo comentario de %{commenter} en subject: Alguén comentou no teu %{commentable} title: Novo comentario config: @@ -17,7 +17,7 @@ gl: title: Verifica a túa conta co seguinte enlace reply: hi: Ola - new_reply_by_html: Hai unha nova resposta de %{commenter} ao tu comentario en + new_reply_by_html: Hai unha nova resposta de %{commenter} ao tu comentario en subject: Alguén respondeu ao teu comentario title: Nova resposta ao teu comentario unfeasible_spending_proposal: diff --git a/config/locales/he/mailers.yml b/config/locales/he/mailers.yml index 98b9a0d44..7629188b9 100644 --- a/config/locales/he/mailers.yml +++ b/config/locales/he/mailers.yml @@ -3,7 +3,7 @@ he: no_reply: "נא לא לענות לכתובת דואר זו." comment: hi: שלום - new_comment_by_html: ישנה הערה חדשה מ %{commenter} + new_comment_by_html: ישנה הערה חדשה מ %{commenter} subject: ישנה הערה חדשה %{commentable} title: הערה חדשה config: @@ -17,7 +17,7 @@ he: title: אשר את חשבונך באמצעות הקישור הבא reply: hi: שלום - new_reply_by_html: ישנה התייחסות חדשה מ %{commenter} לתגובתך בנושא + new_reply_by_html: ישנה התייחסות חדשה מ %{commenter} לתגובתך בנושא subject: מישהו הגיב להערה שלך title: התקבלה תגובה חדשה להערה שלך unfeasible_spending_proposal: diff --git a/config/locales/id-ID/mailers.yml b/config/locales/id-ID/mailers.yml index 939a868f9..6141cc2ad 100644 --- a/config/locales/id-ID/mailers.yml +++ b/config/locales/id-ID/mailers.yml @@ -3,7 +3,7 @@ id: no_reply: "Pesan ini dikirim dari alamat email yang tidak menerima balasan." comment: hi: Hai - new_comment_by_html: Ada komentar baru dari %{commenter} + new_comment_by_html: Ada komentar baru dari %{commenter} subject: Seseorang telah berkomentar tentang anda %{commentable} title: Komentar baru config: @@ -16,7 +16,7 @@ id: title: Konfirmasikan akun anda menggunakan tautan berikut reply: hi: Hai - new_reply_by_html: Ada respon baru dari %{commenter} untuk komentar anda + new_reply_by_html: Ada respon baru dari %{commenter} untuk komentar anda subject: Seseorang telah menanggapi komentar anda title: Tanggapan baru atas komentar anda unfeasible_spending_proposal: diff --git a/config/locales/it/mailers.yml b/config/locales/it/mailers.yml index f6bcfaa59..5f789b6f7 100644 --- a/config/locales/it/mailers.yml +++ b/config/locales/it/mailers.yml @@ -3,7 +3,7 @@ it: no_reply: "Questo messaggio è stato inviato da un indirizzo di posta elettronica che non accetta risposte." comment: hi: Salve - new_comment_by_html: C'è un nuovo commento da %{commenter} + new_comment_by_html: C'è un nuovo commento da %{commenter} subject: Qualcuno ha commentato sul tuo %{commentable} title: Nuovo commento config: @@ -17,7 +17,7 @@ it: title: Conferma il tuo account utilizzando il seguente link reply: hi: Ciao - new_reply_by_html: C'è una nuova risposta da %{commenter} per il tuo commento su + new_reply_by_html: C'è una nuova risposta da %{commenter} per il tuo commento su subject: Qualcuno ha risposto al tuo commento title: Nuova risposta al tuo commento unfeasible_spending_proposal: diff --git a/config/locales/nl/mailers.yml b/config/locales/nl/mailers.yml index 53126e894..ead75920f 100644 --- a/config/locales/nl/mailers.yml +++ b/config/locales/nl/mailers.yml @@ -3,7 +3,7 @@ nl: no_reply: "This message was sent from an email address that does not accept replies." comment: hi: Hi - new_comment_by_html: There is a new comment from %{commenter} + new_comment_by_html: There is a new comment from %{commenter} subject: Someone has commented on your %{commentable} title: New comment config: @@ -17,7 +17,7 @@ nl: title: Confirm your account using the following link reply: hi: Hoi - new_reply_by_html: There is a new response from %{commenter} to your comment on + new_reply_by_html: There is a new response from %{commenter} to your comment on subject: Someone has responded to your comment title: New response to your comment unfeasible_spending_proposal: diff --git a/config/locales/pl-PL/mailers.yml b/config/locales/pl-PL/mailers.yml index f9eedc9ba..aed305cc6 100644 --- a/config/locales/pl-PL/mailers.yml +++ b/config/locales/pl-PL/mailers.yml @@ -3,7 +3,7 @@ pl: no_reply: "Ta wiadomość została wysłana z adresu e-mail, który nie akceptuje odpowiedzi." comment: hi: Cześć - new_comment_by_html: Nowy komentarz od %{commenter} + new_comment_by_html: Nowy komentarz od %{commenter} subject: Ktoś skomentował Twój %{commentable} title: Nowy komentarz config: @@ -17,7 +17,7 @@ pl: title: Potwierdź swoje konto za pomocą następującego linku reply: hi: Cześć - new_reply_by_html: Nowa odpowiedź od %{commenter} na Twój komentarz odnośnie + new_reply_by_html: Nowa odpowiedź od %{commenter} na Twój komentarz odnośnie subject: Ktoś odpowiedział na Twój komentarz title: Nowa odpowiedź na Twój komentarz unfeasible_spending_proposal: diff --git a/config/locales/pt-BR/mailers.yml b/config/locales/pt-BR/mailers.yml index bd7fa72c0..67c00eab7 100644 --- a/config/locales/pt-BR/mailers.yml +++ b/config/locales/pt-BR/mailers.yml @@ -3,7 +3,7 @@ pt-BR: no_reply: "Esta mensagem foi enviada de um endereço de e-mail que não aceita respostas." comment: hi: Oi - new_comment_by_html: Há um novo comentário de %{commenter} + new_comment_by_html: Há um novo comentário de %{commenter} subject: Alguém comentou sobre seu %{commentable} title: Novo comentário config: @@ -17,7 +17,7 @@ pt-BR: title: Confirme sua conta usando o seguinte link reply: hi: Oi - new_reply_by_html: Há uma nova resposta de %{commenter} ao seu comentário sobre + new_reply_by_html: Há uma nova resposta de %{commenter} ao seu comentário sobre subject: Alguém respondeu ao seu comentário title: Nova resposta ao seu comentário unfeasible_spending_proposal: diff --git a/config/locales/ru/mailers.yml b/config/locales/ru/mailers.yml index e0de1d260..a12f4ae46 100644 --- a/config/locales/ru/mailers.yml +++ b/config/locales/ru/mailers.yml @@ -3,7 +3,7 @@ ru: no_reply: "Это сообщение было отправлено с email адреса, который не принимает ответы." comment: hi: Здравствуйте - new_comment_by_html: Появился новый комментарий от %{commenter} + new_comment_by_html: Появился новый комментарий от %{commenter} subject: Кто-то прокомментировал ваш %{commentable} title: Новый комментарий config: @@ -17,7 +17,7 @@ ru: title: Подтвердить ваш аккаунт при помощи следующей ссылки reply: hi: Здравствуйте - new_reply_by_html: Появился новый ответ от %{commenter} на ваш комментарий на + new_reply_by_html: Появился новый ответ от %{commenter} на ваш комментарий на subject: Кто-то ответил на ваш комментарий title: Новый ответ на ваш комментарий unfeasible_spending_proposal: diff --git a/config/locales/sl-SI/mailers.yml b/config/locales/sl-SI/mailers.yml index c0379edd6..adaed2cd2 100644 --- a/config/locales/sl-SI/mailers.yml +++ b/config/locales/sl-SI/mailers.yml @@ -3,7 +3,7 @@ sl: no_reply: "To sporočilo je bilo poslano z e-naslova, ki ne sprejema odgovorov." comment: hi: Hej - new_comment_by_html: Imaš nov komentar uporabnika/-ce %{commenter} + new_comment_by_html: Imaš nov komentar uporabnika/-ce %{commenter} subject: Nekdo je komentiral na tvoj %{commentable} title: Nov komentar config: @@ -17,7 +17,7 @@ sl: title: Potrdi svoj račun z uporabo naslednje povezave reply: hi: Hej - new_reply_by_html: Imaš nov odgovor uporabnika/-ce %{commenter} na tvoj komentar o + new_reply_by_html: Imaš nov odgovor uporabnika/-ce %{commenter} na tvoj komentar o subject: Nekdo se je odzval na tvoj komentar title: Nov odgovor na tvoj komentar unfeasible_spending_proposal: diff --git a/config/locales/so-SO/mailers.yml b/config/locales/so-SO/mailers.yml index 9361e99e0..66fb5bbb7 100644 --- a/config/locales/so-SO/mailers.yml +++ b/config/locales/so-SO/mailers.yml @@ -3,7 +3,7 @@ so: no_reply: "Farriintan waxaa loo direy cinwaanka emailka ah ee aan aqbalin jawaab-celinta." comment: hi: Haye - new_comment_by_html: Waxa jira faalo cusub%{commenter} + new_comment_by_html: Waxa jira faalo cusub%{commenter} subject: Qof ayaa ka falooday%{commentable} title: Faalo cusub config: @@ -17,7 +17,7 @@ so: title: Xaqiiji xisaabtaada adigoo isticmaalaya xiriirka soo socda reply: hi: Haye - new_reply_by_html: Waxaa jira jawaab cusub oo ka socota %{commenter} si aad faallo uga bixiso + new_reply_by_html: Waxaa jira jawaab cusub oo ka socota %{commenter} si aad faallo uga bixiso subject: Qof ayaa kajawaabay faladadii title: Jawaabta cusub ee faalladaada unfeasible_spending_proposal: diff --git a/config/locales/sq-AL/mailers.yml b/config/locales/sq-AL/mailers.yml index 4dfbf0869..58896f106 100644 --- a/config/locales/sq-AL/mailers.yml +++ b/config/locales/sq-AL/mailers.yml @@ -3,7 +3,7 @@ sq: no_reply: "Ky mesazh u dërgua nga një adresë e-mail që nuk pranon përgjigje." comment: hi: Përshëndetje! - new_comment_by_html: Ka një koment të ri nga%{commenter} + new_comment_by_html: Ka një koment të ri nga%{commenter} subject: Dikush ka komentuar mbi %{commentable} tuaj title: Komenti i ri config: @@ -17,7 +17,7 @@ sq: title: Konfirmo llogarinë tënd duke përdorur lidhjen e mëposhtme reply: hi: Hi - new_reply_by_html: Ka një përgjigje të re nga %{commenter} në komentin tuaj + new_reply_by_html: Ka një përgjigje të re nga %{commenter} në komentin tuaj subject: Dikush i është përgjigjur komentit tuaj title: Përgjigje e re tek komentit tuaj unfeasible_spending_proposal: diff --git a/config/locales/sv-SE/mailers.yml b/config/locales/sv-SE/mailers.yml index ce305afb6..39567de93 100644 --- a/config/locales/sv-SE/mailers.yml +++ b/config/locales/sv-SE/mailers.yml @@ -3,7 +3,7 @@ sv: no_reply: "Det går inte att svara till den här e-postadressen." comment: hi: Hej - new_comment_by_html: Det finns en ny kommentar från %{commenter} + new_comment_by_html: Det finns en ny kommentar från %{commenter} subject: Någon har kommenterat på ditt %{commentable} title: Ny kommentar config: @@ -17,7 +17,7 @@ sv: title: Bekräfta kontot via länken nedan reply: hi: Hej - new_reply_by_html: Det finns ett nytt svar från %{commenter} till din kommentar på + new_reply_by_html: Det finns ett nytt svar från %{commenter} till din kommentar på subject: Någon har svarat på din kommentar title: Nytt svar på din kommentar unfeasible_spending_proposal: diff --git a/config/locales/val/mailers.yml b/config/locales/val/mailers.yml index efa3c79de..a0474520b 100644 --- a/config/locales/val/mailers.yml +++ b/config/locales/val/mailers.yml @@ -3,7 +3,7 @@ val: no_reply: "Aquest missatge s'ha enviat des d'una direcció de correu electrònic que no admet respostes." comment: hi: Hola - new_comment_by_html: Hi ha un nou comentari de %{commenter} en + new_comment_by_html: Hi ha un nou comentari de %{commenter} en subject: Algú ha comentat en el teu %{commentable} title: Nou comentari config: @@ -17,7 +17,7 @@ val: title: Verifica el teu compte amb el següent enllaç reply: hi: Hola - new_reply_by_html: Hi ha noves respostes de %{commenter} al teu comentari en + new_reply_by_html: Hi ha noves respostes de %{commenter} al teu comentari en subject: Algú ha contestat el teu comentari title: Nova resposta al teu comentari unfeasible_spending_proposal: diff --git a/config/locales/zh-CN/mailers.yml b/config/locales/zh-CN/mailers.yml index 84376169c..c591c3942 100644 --- a/config/locales/zh-CN/mailers.yml +++ b/config/locales/zh-CN/mailers.yml @@ -3,7 +3,7 @@ zh-CN: no_reply: "此消息是从一个不接受回复的电子邮件地址发送的。" comment: hi: 您好 - new_comment_by_html: 来自%{commenter}的新评论 + new_comment_by_html: 来自%{commenter}的新评论 subject: 有人对您的%{commentable} 做了评论 title: 新评论 config: @@ -17,7 +17,7 @@ zh-CN: title: 使用以下链接来确认您的账户 reply: hi: 您好 - new_reply_by_html: %{commenter} 对您评论有新回复 + new_reply_by_html: %{commenter} 对您评论有新回复 subject: 有人回复了您的评论 title: 对您评论的新回复 unfeasible_spending_proposal: diff --git a/config/locales/zh-TW/mailers.yml b/config/locales/zh-TW/mailers.yml index b63e23491..ea06c2522 100644 --- a/config/locales/zh-TW/mailers.yml +++ b/config/locales/zh-TW/mailers.yml @@ -3,7 +3,7 @@ zh-TW: no_reply: "此郵件是從一個不接受回覆的電郵地址發送的。" comment: hi: 您好 - new_comment_by_html: %{commenter}發表了新評論 + new_comment_by_html: %{commenter}發表了新評論 subject: 有人評論了您的%{commentable} title: 新評論 config: @@ -17,7 +17,7 @@ zh-TW: title: 使用以下鏈接確認您的帳戶 reply: hi: 您好 - new_reply_by_html: %{commenter}對您的評論有新的回覆 + new_reply_by_html: %{commenter}對您的評論有新的回覆 subject: 有人回覆了您的評論 title: 對您的評論的新回覆 unfeasible_spending_proposal: From c78211af49c35850433a53a539d7655ee1fa2476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 3 Jun 2019 11:57:23 +0200 Subject: [PATCH 32/55] Add task to upgrade to version 1.0.0 It includes every task needed for the upgrade. --- lib/tasks/consul.rake | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 lib/tasks/consul.rake diff --git a/lib/tasks/consul.rake b/lib/tasks/consul.rake new file mode 100644 index 000000000..acaa3686a --- /dev/null +++ b/lib/tasks/consul.rake @@ -0,0 +1,13 @@ +namespace :consul do + desc "Runs tasks needed to upgrade to the latest version" + task execute_release_tasks: "execute_release_1.0.0_tasks" + + desc "Runs tasks needed to upgrade from 1.0.0-beta to 1.0.0" + task "execute_release_1.0.0_tasks": [ + "poll:generate_slugs", + "stats_and_results:migrate_to_reports", + "budgets:calculate_ballot_lines", + "settings:remove_deprecated_settings", + "stats:generate" + ] +end From 685c96c40d76b0fbc4f617cd9e2841bdb6176451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 3 Jun 2019 16:34:06 +0200 Subject: [PATCH 33/55] Add capistrano task to deploy and upgrade CONSUL --- config/deploy.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/config/deploy.rb b/config/deploy.rb index bb7627f3a..eb48fb135 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -51,6 +51,13 @@ namespace :deploy do after :published, "refresh_sitemap" after :finishing, "deploy:cleanup" + + + desc "Deploys and runs the tasks needed to upgrade to a new release" + task :upgrade do + after "add_new_settings", "execute_release_tasks" + invoke "deploy" + end end task :install_bundler_gem do @@ -78,3 +85,13 @@ task :add_new_settings do end end end + +task :execute_release_tasks do + on roles(:app) do + within release_path do + with rails_env: fetch(:rails_env) do + execute :rake, "consul:execute_release_tasks" + end + end + end +end From 3ab2085e05ba80048c3561120241c3a13323c22f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 3 Jun 2019 16:43:42 +0200 Subject: [PATCH 34/55] Move calculate_ballot_lines task to the right file We already have a file with the proper budgets namespace. --- lib/tasks/budgets.rake | 7 +++++++ lib/tasks/regenerate_ballot_lines_cache.rake | 8 -------- 2 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 lib/tasks/regenerate_ballot_lines_cache.rake diff --git a/lib/tasks/budgets.rake b/lib/tasks/budgets.rake index c53c71103..4e2961198 100644 --- a/lib/tasks/budgets.rake +++ b/lib/tasks/budgets.rake @@ -1,4 +1,11 @@ namespace :budgets do + desc "Regenerate ballot_lines_count cache" + task calculate_ballot_lines: :environment do + Budget::Ballot.find_each do |ballot| + Budget::Ballot.reset_counters ballot.id, :lines + end + end + namespace :email do desc "Sends emails to authors of selected investments" diff --git a/lib/tasks/regenerate_ballot_lines_cache.rake b/lib/tasks/regenerate_ballot_lines_cache.rake deleted file mode 100644 index d008ec556..000000000 --- a/lib/tasks/regenerate_ballot_lines_cache.rake +++ /dev/null @@ -1,8 +0,0 @@ -namespace :budgets do - desc "Regenerate ballot_lines_count cache" - task calculate_ballot_lines: :environment do - Budget::Ballot.find_each do |ballot| - Budget::Ballot.reset_counters ballot.id, :lines - end - end -end From 1d0b05832cb9e1588db07d76c09ce9fe7c3fa9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 3 Jun 2019 16:59:40 +0200 Subject: [PATCH 35/55] Add info messages for release 1.0.0 tasks So users know what's going on when they upgrade CONSUL. --- lib/application_logger.rb | 11 +++++++++++ lib/tasks/budgets.rake | 5 ++++- lib/tasks/polls.rake | 2 ++ lib/tasks/settings.rake | 2 ++ lib/tasks/stats.rake | 2 ++ lib/tasks/stats_and_results.rake | 1 + 6 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 lib/application_logger.rb diff --git a/lib/application_logger.rb b/lib/application_logger.rb new file mode 100644 index 000000000..a9f953f48 --- /dev/null +++ b/lib/application_logger.rb @@ -0,0 +1,11 @@ +class ApplicationLogger + def info(message) + logger.info(message) + end + + def logger + @logger ||= Logger.new(STDOUT).tap do |logger| + logger.formatter = proc { |severity, _datetime, _progname, msg| "#{severity} #{msg}\n" } + end + end +end diff --git a/lib/tasks/budgets.rake b/lib/tasks/budgets.rake index 4e2961198..d9fc8f50f 100644 --- a/lib/tasks/budgets.rake +++ b/lib/tasks/budgets.rake @@ -1,8 +1,11 @@ namespace :budgets do desc "Regenerate ballot_lines_count cache" task calculate_ballot_lines: :environment do - Budget::Ballot.find_each do |ballot| + ApplicationLogger.new.info "Calculating ballot lines" + + Budget::Ballot.find_each.with_index do |ballot, index| Budget::Ballot.reset_counters ballot.id, :lines + print "." if (index % 10_000).zero? end end diff --git a/lib/tasks/polls.rake b/lib/tasks/polls.rake index 0e689e00c..2bd16c46f 100644 --- a/lib/tasks/polls.rake +++ b/lib/tasks/polls.rake @@ -1,6 +1,8 @@ namespace :poll do desc "Generate slugs polls" task generate_slugs: :environment do + ApplicationLogger.new.info "Generating poll slugs" + Poll.find_each do |poll| poll.update_columns(slug: poll.generate_slug, updated_at: Time.current) if poll.generate_slug? end diff --git a/lib/tasks/settings.rake b/lib/tasks/settings.rake index c206af98d..5c4a4db19 100644 --- a/lib/tasks/settings.rake +++ b/lib/tasks/settings.rake @@ -2,6 +2,8 @@ namespace :settings do desc "Remove deprecated settings" task remove_deprecated_settings: :environment do + ApplicationLogger.new.info "Removing deprecated settings" + deprecated_keys = [ "place_name", "banner-style.banner-style-one", diff --git a/lib/tasks/stats.rake b/lib/tasks/stats.rake index a454d88cd..2d68613c5 100644 --- a/lib/tasks/stats.rake +++ b/lib/tasks/stats.rake @@ -1,6 +1,8 @@ namespace :stats do desc "Generates stats which are not cached yet" task generate: :environment do + ApplicationLogger.new.info "Updating budget and poll stats" + Budget.find_each do |budget| Budget::Stats.new(budget).generate print "." diff --git a/lib/tasks/stats_and_results.rake b/lib/tasks/stats_and_results.rake index 5aa8f7e39..bc4cdacca 100644 --- a/lib/tasks/stats_and_results.rake +++ b/lib/tasks/stats_and_results.rake @@ -1,6 +1,7 @@ namespace :stats_and_results do desc "Migrates stats_enabled and results_enabled data to enabled reports" task migrate_to_reports: :environment do + ApplicationLogger.new.info "Migrating stats and results" Migrations::Reports.new.migrate end end From d2c075cc938cd9bb0d172ba68a3db287c1f9e515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Wed, 5 Jun 2019 19:28:31 +0200 Subject: [PATCH 36/55] Add new settings on servers with the `db` role This task adds records to the database, so the role `db` is more appropriate. --- config/deploy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/deploy.rb b/config/deploy.rb index eb48fb135..602e86ae1 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -77,7 +77,7 @@ task :refresh_sitemap do end task :add_new_settings do - on roles(:app) do + on roles(:db) do within release_path do with rails_env: fetch(:rails_env) do execute :rake, "settings:add_new_settings" From 99fd838ca1b0eb8283ca569224788288b6375b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 27 May 2019 15:13:52 +0200 Subject: [PATCH 37/55] Extract partial to render finished budgets --- app/views/budgets/_finished.html.erb | 32 +++++++++++++++++++++++++++ app/views/budgets/index.html.erb | 33 +--------------------------- 2 files changed, 33 insertions(+), 32 deletions(-) create mode 100644 app/views/budgets/_finished.html.erb diff --git a/app/views/budgets/_finished.html.erb b/app/views/budgets/_finished.html.erb new file mode 100644 index 000000000..97ad2aa18 --- /dev/null +++ b/app/views/budgets/_finished.html.erb @@ -0,0 +1,32 @@ +
      +
      +

      <%= t("budgets.index.finished_budgets") %>

      + +
      + <% budgets.each do |budget| %> +
      +
      +
      +
      +
      +

      <%= budget.name %>

      +
      +
      + +
      +
      + <%= link_to t("budgets.index.see_results"), + budget_results_path(budget.id), + class: "button" %> + <%= link_to t("budgets.index.milestones"), + budget_executions_path(budget.id), + class: "button" %> +
      +
      +
      +
      +
      + <% end %> +
      +
      +
      diff --git a/app/views/budgets/index.html.erb b/app/views/budgets/index.html.erb index 9e198156f..e19a13416 100644 --- a/app/views/budgets/index.html.erb +++ b/app/views/budgets/index.html.erb @@ -126,38 +126,7 @@
      <% if @finished_budgets.present? %> -
      -
      -

      <%= t("budgets.index.finished_budgets") %>

      - -
      - <% @finished_budgets.each do |budget| %> -
      -
      -
      -
      -
      -

      <%= budget.name %>

      -
      -
      - -
      -
      - <%= link_to t("budgets.index.see_results"), - budget_results_path(budget.id), - class: "button" %> - <%= link_to t("budgets.index.milestones"), - budget_executions_path(budget.id), - class: "button" %> -
      -
      -
      -
      -
      - <% end %> -
      -
      -
      + <%= render "finished", budgets: @finished_budgets %> <% end %>
      <% else %> From c8b6a1a4a1be5e5c1f5263b7c64afe059282b074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 27 May 2019 15:24:20 +0200 Subject: [PATCH 38/55] Use the same view code for finished budgets The differences between the custom code and the code in CONSUL didn't make much sense anymore: the path can be passed a budget instead of an ID (so it uses a slug) and there's no reason to hide the link to the executions for the 2018 budget anymore. Furthermore, removing the `status: 1` parameter makes the link consistent with the rest of the application, since it was the only link to budget executions using that parameter. --- app/views/budgets/_finished.html.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/budgets/_finished.html.erb b/app/views/budgets/_finished.html.erb index 97ad2aa18..f7c17fc58 100644 --- a/app/views/budgets/_finished.html.erb +++ b/app/views/budgets/_finished.html.erb @@ -16,10 +16,10 @@
      <%= link_to t("budgets.index.see_results"), - budget_results_path(budget.id), + budget_results_path(budget), class: "button" %> <%= link_to t("budgets.index.milestones"), - budget_executions_path(budget.id), + budget_executions_path(budget), class: "button" %>
      From eb7dc3ef2d7f130af076b609c7af4bb834102213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Tue, 4 Jun 2019 11:48:14 +0200 Subject: [PATCH 39/55] Extract partial to render budget navigation links --- app/helpers/budgets_helper.rb | 14 ++++++++++++++ app/views/budgets/_subnav.html.erb | 17 +++++++++++++++++ app/views/budgets/executions/show.html.erb | 16 +--------------- app/views/budgets/results/show.html.erb | 17 +---------------- app/views/budgets/stats/show.html.erb | 17 +---------------- 5 files changed, 34 insertions(+), 47 deletions(-) create mode 100644 app/views/budgets/_subnav.html.erb diff --git a/app/helpers/budgets_helper.rb b/app/helpers/budgets_helper.rb index d8425c217..5ac9b12a1 100644 --- a/app/helpers/budgets_helper.rb +++ b/app/helpers/budgets_helper.rb @@ -105,4 +105,18 @@ module BudgetsHelper ends_at: balloting_phase.ends_at }), method: :post end + + def budget_subnav_items_for(budget) + { + results: t("budgets.results.link"), + stats: t("stats.budgets.link"), + executions: t("budgets.executions.link") + }.map do |section, text| + { + text: text, + url: send("budget_#{section}_path", budget), + active: controller_name == section.to_s + } + end + end end diff --git a/app/views/budgets/_subnav.html.erb b/app/views/budgets/_subnav.html.erb new file mode 100644 index 000000000..f5f960377 --- /dev/null +++ b/app/views/budgets/_subnav.html.erb @@ -0,0 +1,17 @@ +
      +
      +
        + <% budget_subnav_items_for(budget).each do |item| %> + <% if item[:active] %> +
      • + <%= t("shared.you_are_in") %> + + <%= link_to item[:text], item[:url], class: "is-active" %> +
      • + <% else %> +
      • <%= link_to item[:text], item[:url] %>
      • + <% end %> + <% end %> +
      +
      +
      diff --git a/app/views/budgets/executions/show.html.erb b/app/views/budgets/executions/show.html.erb index a0e3324bc..acab50de4 100644 --- a/app/views/budgets/executions/show.html.erb +++ b/app/views/budgets/executions/show.html.erb @@ -25,21 +25,7 @@ -
      -
      -
        -
      • - <%= link_to t("budgets.results.link"), budget_results_path(@budget) %> -
      • -
      • - <%= link_to t("stats.budgets.link"), budget_stats_path(@budget) %> -
      • -
      • - <%= link_to t("budgets.executions.link"), budget_executions_path(@budget), class: "is-active" %> -
      • -
      -
      -
      +<%= render "budgets/subnav", budget: @budget %>
      diff --git a/app/views/budgets/results/show.html.erb b/app/views/budgets/results/show.html.erb index d77f50b77..6a8829437 100644 --- a/app/views/budgets/results/show.html.erb +++ b/app/views/budgets/results/show.html.erb @@ -24,22 +24,7 @@
      -
      -
      -
        -
      • - <%= t("shared.you_are_in") %> - <%= link_to t("budgets.results.link"), budget_results_path(@budget), class: "is-active" %> -
      • -
      • - <%= link_to t("stats.budgets.link"), budget_stats_path(@budget) %> -
      • -
      • - <%= link_to t("budgets.executions.link"), budget_executions_path(@budget) %> -
      • -
      -
      -
      +<%= render "budgets/subnav", budget: @budget %>
      diff --git a/app/views/budgets/stats/show.html.erb b/app/views/budgets/stats/show.html.erb index bbbfda4bb..60697135d 100644 --- a/app/views/budgets/stats/show.html.erb +++ b/app/views/budgets/stats/show.html.erb @@ -21,22 +21,7 @@
      -
      -
      -
        -
      • - <%= t("shared.you_are_in") %> - <%= link_to t("budgets.results.link"), budget_results_path(@budget) %> -
      • -
      • - <%= link_to t("stats.budgets.link"), budget_stats_path(@budget), class: "is-active" %> -
      • -
      • - <%= link_to t("budgets.executions.link"), budget_executions_path(@budget) %> -
      • -
      -
      -
      + <%= render "budgets/subnav", budget: @budget %>