diff --git a/.env-example b/.env-example index e1d1489e..c0de2565 100644 --- a/.env-example +++ b/.env-example @@ -15,3 +15,4 @@ BACKUP_S3RETENTION_ENABLED= DECIDIM_SESSION_TIMEOUT= # SKIP_FIRST_LOGIN_AUTHORIZATION=true +# RAILS_LOG_LEVEL=warn \ No newline at end of file diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 147c2c6f..c2971c60 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -10,8 +10,8 @@ env: NODE_VERSION: 16.9.1 RUBYOPT: '-W:no-deprecated' # Set locales available for i18n tasks - ENFORCED_LOCALES: "en,fr" - AVAILABLE_LOCALES: "en,fr" + ENFORCED_LOCALES: "en,fr,ca,es" + AVAILABLE_LOCALES: "en,fr,ca,es" jobs: todo: @@ -254,4 +254,4 @@ jobs: namespace: ${{ vars.REGISTRY_NAMESPACE }} password: ${{ secrets.TOKEN }} image_name: ${{ vars.IMAGE_NAME }} - tag: ${{ steps.tag_version.outputs.new_tag }} \ No newline at end of file + tag: ${{ steps.tag_version.outputs.new_tag }} diff --git a/Dockerfile.local b/Dockerfile.local index 979fea4a..5f4deb11 100644 --- a/Dockerfile.local +++ b/Dockerfile.local @@ -26,8 +26,8 @@ RUN yarn install --frozen-lock COPY . . RUN bundle exec bootsnap precompile --gemfile app/ lib/ config/ bin/ db/ && \ - bundle exec rails deface:precompile && \ - bundle exec rails assets:precompile + bundle exec rails assets:precompile && \ + bundle exec rails deface:precompile run mkdir certificate-https-local RUN openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=FR/ST=France/L=Paris/O=decidim/CN=decidim.eu" -keyout ./certificate-https-local/key.pem -out ./certificate-https-local/cert.pem; diff --git a/Gemfile.lock b/Gemfile.lock index 61a87518..d28124e8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,7 +12,7 @@ GIT GIT remote: https://github.com/OpenSourcePolitics/decidim-module-slider - revision: cf5632125da12749589ec321f3112e51dfd3a589 + revision: 776378746abd5a637d654fbe068ca98f5bde2371 branch: rc/0.27 specs: decidim-slider (0.0.3) @@ -129,7 +129,7 @@ GEM public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) aws-eventstream (1.3.0) - aws-partitions (1.901.0) + aws-partitions (1.905.0) aws-sdk-core (3.191.5) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) @@ -138,13 +138,13 @@ GEM aws-sdk-kms (1.78.0) aws-sdk-core (~> 3, >= 3.191.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.146.0) + aws-sdk-s3 (1.146.1) aws-sdk-core (~> 3, >= 3.191.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) aws-sigv4 (1.8.0) aws-eventstream (~> 1, >= 1.0.2) - axe-core-api (4.8.2) + axe-core-api (4.9.0) dumb_delegator virtus axe-core-rspec (4.1.0) @@ -566,8 +566,8 @@ GEM kramdown (~> 2.0) launchy (2.5.2) addressable (~> 2.8) - letter_opener (1.9.0) - launchy (>= 2.2, < 3) + letter_opener (1.10.0) + launchy (>= 2.2, < 4) letter_opener_web (1.4.1) actionmailer (>= 3.2) letter_opener (~> 1.0) @@ -647,11 +647,11 @@ GEM rack-protection omniauth-facebook (5.0.0) omniauth-oauth2 (~> 1.2) - omniauth-google-oauth2 (1.1.1) + omniauth-google-oauth2 (1.1.2) jwt (>= 2.0) - oauth2 (~> 2.0.6) + oauth2 (~> 2.0) omniauth (~> 2.0) - omniauth-oauth2 (~> 1.8.0) + omniauth-oauth2 (~> 1.8) omniauth-oauth (1.2.0) oauth omniauth (>= 1.0, < 3) @@ -687,7 +687,7 @@ GEM actionmailer (>= 3) net-smtp premailer (~> 1.7, >= 1.7.9) - public_suffix (5.0.4) + public_suffix (5.0.5) puma (5.6.8) nio4r (~> 2.0) raabro (1.4.0) @@ -739,7 +739,7 @@ GEM rake (>= 12.2) thor (~> 1.0) rainbow (3.1.1) - rake (13.1.0) + rake (13.2.0) ransack (2.4.2) activerecord (>= 5.2.4) activesupport (>= 5.2.4) @@ -817,7 +817,7 @@ GEM ruby-progressbar (1.13.0) ruby-vips (2.2.1) ffi (~> 1.12) - rubyXL (3.4.25) + rubyXL (3.4.26) nokogiri (>= 1.10.8) rubyzip (>= 1.3.0) ruby_http_client (3.5.5) @@ -1000,4 +1000,4 @@ RUBY VERSION ruby 3.0.6p216 BUNDLED WITH - 2.2.33 + 2.4.9 diff --git a/app/jobs/notifications_digest_mail_job.rb b/app/jobs/notifications_digest_mail_job.rb new file mode 100644 index 00000000..c73e26df --- /dev/null +++ b/app/jobs/notifications_digest_mail_job.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require "decidim/notifications_digest" + +class NotificationsDigestMailJob < ApplicationJob + queue_as :scheduled + + def perform(frequency) + Decidim::NotificationsDigest.notifications_digest(frequency.to_sym) + end +end diff --git a/config/environments/production.rb b/config/environments/production.rb index 5771520c..76a94329 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -56,7 +56,8 @@ # Use the lowest log level to ensure availability of diagnostic information # when problems arise. - config.log_level = :info + # The available log levels are: :debug, :info, :warn, :error, :fatal, and :unknown + config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "warn").to_sym # Prepend all log lines with the following tags. config.log_tags = [:request_id] diff --git a/config/initializers/decidim.rb b/config/initializers/decidim.rb index acfc0ebf..f6643d98 100644 --- a/config/initializers/decidim.rb +++ b/config/initializers/decidim.rb @@ -7,7 +7,7 @@ config.mailer_sender = "OSP Agora " config.default_locale = ENV.fetch("DEFAULT_LOCALE", "en").to_sym - config.available_locales = ENV.fetch("AVAILABLE_LOCALES", "en,fr").split(",").map(&:to_sym) + config.available_locales = ENV.fetch("AVAILABLE_LOCALES", "en,fr,ca,es").split(",").map(&:to_sym) # Timeout session config.expire_session_after = ENV.fetch("DECIDIM_SESSION_TIMEOUT", 180).to_i.minutes diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb index 7bd0f9ce..4ce81477 100644 --- a/config/initializers/health_check.rb +++ b/config/initializers/health_check.rb @@ -16,8 +16,8 @@ # sensitive information config.include_error_in_response_body = false - # Log level (success or failure message with error details is sent to rails log unless this is set to nil) - config.log_level = "info" + # The available log levels are: :debug, :info, :warn, :error, :fatal, and :unknown + config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "warn").to_sym # Timeout in seconds used when checking smtp server config.smtp_timeout = 30.0 diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 1fe19881..8527bae1 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -1,4 +1,5 @@ :concurrency: 5 +:max_retries: 5 :queues: - default - backups @@ -27,3 +28,13 @@ cron: '0 0 4 * * *' # Run at 04:00 class: BackupJob queue: backups + SendNotificationMailDaily: + cron: '0 0 7 * * *' # Run at 07:00AM every days + class: NotificationsDigestMailJob + queue: mailers + args: :daily + SendNotificationMailWeekly: + cron: '0 0 8 * * 2' # Run at 08:00AM on Tuesday + class: NotificationsDigestMailJob + queue: mailers + args: :weekly \ No newline at end of file diff --git a/lib/decidim/notifications_digest.rb b/lib/decidim/notifications_digest.rb new file mode 100644 index 00000000..b09bc91c --- /dev/null +++ b/lib/decidim/notifications_digest.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Decidim + module NotificationsDigest + def self.notifications_digest(frequency) + return unless [:daily, :weekly].include?(frequency) + + time = Time.now.utc + Decidim::User.where(notifications_sending_frequency: frequency).find_each do |user| + Decidim::EmailNotificationsDigestGeneratorJob.perform_later(user.id, frequency, time: time) + end + end + end +end diff --git a/spec/i18n_spec.rb b/spec/i18n_spec.rb index 1b1a151f..cd59b181 100644 --- a/spec/i18n_spec.rb +++ b/spec/i18n_spec.rb @@ -4,7 +4,7 @@ describe "I18n sanity" do let(:locales) do - ENV["ENFORCED_LOCALES"].presence || "en,fr" + "en,fr" end let(:i18n) { I18n::Tasks::BaseTask.new(locales: locales.split(",")) } diff --git a/spec/jobs/notifications_digest_mail_job_spec.rb b/spec/jobs/notifications_digest_mail_job_spec.rb new file mode 100644 index 00000000..30c1dca2 --- /dev/null +++ b/spec/jobs/notifications_digest_mail_job_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe NotificationsDigestMailJob, type: :job do + describe "#perform" do + [:daily, :weekly].each do |frequency| + it "calls notifications digest with #{frequency}" do + expect(Decidim::NotificationsDigest).to receive(:notifications_digest).with(frequency) + + subject.perform(frequency) + end + end + end +end diff --git a/spec/lib/decidim/notifications_digest_spec.rb b/spec/lib/decidim/notifications_digest_spec.rb new file mode 100644 index 00000000..d1884489 --- /dev/null +++ b/spec/lib/decidim/notifications_digest_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/notifications_digest" + +describe Decidim::NotificationsDigest do + describe "#notifications_digest" do + subject { described_class.notifications_digest(frequency) } + + let(:frequency) { :daily } + let!(:users_daily) { create_list :user, 4, notifications_sending_frequency: :daily } + let!(:users_weekly) { create_list :user, 2, notifications_sending_frequency: :weekly } + + before do + allow(Decidim::EmailNotificationsDigestGeneratorJob).to receive(:perform_later).and_return(true) + end + + it "executes DigestGeneratorJob on daily users" do + subject + + expect(Decidim::EmailNotificationsDigestGeneratorJob).to have_received(:perform_later).exactly(4) + end + + context "and frequency is weekly" do + let(:frequency) { :weekly } + + it "executes DigestGeneratorJob on weekly users" do + subject + + expect(Decidim::EmailNotificationsDigestGeneratorJob).to have_received(:perform_later).exactly(2) + end + end + + context "and frequency is not in list" do + let(:frequency) { :unknown } + + it "executes DigestGeneratorJob on weekly users" do + subject + + expect(Decidim::EmailNotificationsDigestGeneratorJob).not_to have_received(:perform_later) + end + end + end +end diff --git a/spec/lib/decidim_app/k8s/organization_exporter_spec.rb b/spec/lib/decidim_app/k8s/organization_exporter_spec.rb index d4435d63..0e143b48 100644 --- a/spec/lib/decidim_app/k8s/organization_exporter_spec.rb +++ b/spec/lib/decidim_app/k8s/organization_exporter_spec.rb @@ -167,7 +167,7 @@ it "returns the organization columns" do expect(subject.organization_columns).to eq({ "available_authorizations" => [], - "available_locales" => %w(en fr), + "available_locales" => %w(en fr ca es), "default_locale" => "en", "file_upload_settings" => { "allowed_content_types" => { diff --git a/spec/system/authentication_spec.rb b/spec/system/authentication_spec.rb index 038c4247..4198403f 100644 --- a/spec/system/authentication_spec.rb +++ b/spec/system/authentication_spec.rb @@ -22,19 +22,19 @@ fill_in :registration_user_nickname, with: "responsible" fill_in :registration_user_password, with: "DfyvHn425mYAy2HL" fill_in :registration_user_password_confirmation, with: "DfyvHn425mYAy2HL" - check :registration_user_tos_agreement + find("*[type=submit]").click end - expect(page).to have_content("A message with a confirmation link has been sent to your email address. Please follow the link to activate your account.") + expect(page).to have_content("confirmation link") end end context "when using another langage" do before do within_language_menu do - click_link "Français" + click_link "Castellano" end end @@ -47,13 +47,13 @@ fill_in :registration_user_nickname, with: "responsible" fill_in :registration_user_password, with: "DfyvHn425mYAy2HL" fill_in :registration_user_password_confirmation, with: "DfyvHn425mYAy2HL" - check :registration_user_tos_agreement + find("*[type=submit]").click end - expect(page).to have_content("Un message avec un lien de confirmation a été envoyé à votre adresse e-mail. Veuillez suivre le lien pour activer votre compte.") - expect(last_user.locale).to eq("fr") + expect(page).to have_content("Se ha enviado un mensaje con un enlace de confirmación") + expect(last_user.locale).to eq("es") end end @@ -68,13 +68,12 @@ fill_in :registration_user_nickname, with: "responsible" fill_in :registration_user_password, with: "DfyvHn425mYAy2HL" fill_in :registration_user_password_confirmation, with: "DfyvHn425mYAy2HL" - check :registration_user_tos_agreement + find("*[type=submit]").click end expect(page).not_to have_content("confirmation link") - expect(page).not_to have_content("You have signed up successfully") end end @@ -240,6 +239,7 @@ fill_in :registration_user_password, with: "DfyvHn425mYAy2HL" fill_in :registration_user_password_confirmation, with: "DfyvHn425mYAy2HL" check :registration_user_tos_agreement + find("*[type=submit]").click end @@ -335,10 +335,10 @@ expect(page).to have_content("Sign in with Facebook") within_language_menu do - click_link "Français" + click_link "Català" end - expect(page).to have_content("S'identifier avec Facebook") + expect(page).to have_content("Inicia sessió amb Facebook") end end @@ -368,8 +368,6 @@ end describe "Reset password" do - let(:current_url) { page.current_path } - before do perform_enqueued_jobs { user.send_reset_password_instructions } end @@ -391,15 +389,15 @@ visit last_email_link within ".new_user" do - fill_in :password_user_password, with: "example" - fill_in :password_user_password_confirmation, with: "example" + fill_in :password_user_password, with: "whatislove" + fill_in :password_user_password_confirmation, with: "whatislove" find("*[type=submit]").click end expect(page).to have_content("10 characters minimum") expect(page).to have_content("must be different from your nickname and your email") expect(page).to have_content("must not be too common") - expect(current_url).to eq("/users/password/edit") + expect(page).to have_current_path "/users/password" end it "enforces the minimum length for the password in the front-end" do @@ -416,23 +414,6 @@ end end - describe "Sign Out" do - before do - login_as user, scope: :user - visit decidim.root_path - end - - it "signs out the user" do - within ".topbar__user__logged" do - find("a", text: user.name).click - find(".sign-out-link").click - end - - expect(page).to have_content("Signed out successfully.") - expect(page).to have_no_content(user.name) - end - end - context "with lockable account" do Devise.maximum_attempts = 3 let!(:maximum_attempts) { Devise.maximum_attempts } @@ -634,12 +615,12 @@ fill_in :registration_user_nickname, with: "responsible" fill_in :registration_user_password, with: "DfyvHn425mYAy2HL" fill_in :registration_user_password_confirmation, with: "DfyvHn425mYAy2HL" - check :registration_user_tos_agreement + find("*[type=submit]").click end - expect(page).to have_content("A message with a confirmation link has been sent to your email address. Please follow the link to activate your account.") + expect(page).to have_content("confirmation link") end end end