diff --git a/.deploy/Dockerfile b/.deploy/Dockerfile index c914bb70..9caa295d 100644 --- a/.deploy/Dockerfile +++ b/.deploy/Dockerfile @@ -58,6 +58,9 @@ WORKDIR /home/app/resj RUN bundle install RUN --mount=type=secret,id=master_key,uid=9999 RAILS_MASTER_KEY=$(cat /run/secrets/master_key) bundle exec rake assets:precompile +# run the cards update check daily +COPY --chmod=0755 cards-check.sh /etc/cron.daily/ + USER root # baseimage-docker's init process -CMD ["/sbin/my_init"] \ No newline at end of file +CMD ["/sbin/my_init"] diff --git a/.deploy/cards-check.sh b/.deploy/cards-check.sh new file mode 100644 index 00000000..263c335a --- /dev/null +++ b/.deploy/cards-check.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# This script should be placed in /etc/cron.daily to check the cards status +# every day and send an email to the card's owner accordingly. + +set -e + +setuser app bundle exec rake cards_update:check \ No newline at end of file diff --git a/app/assets/stylesheets/pages/users/cards/edit.scss b/app/assets/stylesheets/pages/users/cards/edit.scss index 488a549a..6965e31a 100644 --- a/app/assets/stylesheets/pages/users/cards/edit.scss +++ b/app/assets/stylesheets/pages/users/cards/edit.scss @@ -5,6 +5,12 @@ padding: 0 $content-padding; margin: auto; + .standard-popup { + p { + padding: 5px 0; + } + } + section.top-container { .title { display: flex; diff --git a/app/controllers/users/cards_controller.rb b/app/controllers/users/cards_controller.rb index 9a349830..dfb9c27c 100644 --- a/app/controllers/users/cards_controller.rb +++ b/app/controllers/users/cards_controller.rb @@ -3,14 +3,19 @@ class Users::CardsController < ApplicationController layout "admin" def edit + @confirmed = params[:confirmed] == "true" @card = card end def update @card = card - @card.last_updated = Time.current - if @card.update(card_params) - redirect_to edit_users_card_path(@card), success: "Ton groupe a été mis à jour" + @card.assign_attributes(card_params) + was_outdated = !@card.maintained? + if @card.valid? + @card.last_updated = Time.current + @card.validity = :maintained + @card.save + redirect_to edit_users_card_path(@card, confirmed: was_outdated), success: "Ton groupe a été mis à jour" else render 'edit' end diff --git a/app/mailers/admin/card_mailer.rb b/app/mailers/admin/card_mailer.rb index ddf6e3b7..571d6000 100644 --- a/app/mailers/admin/card_mailer.rb +++ b/app/mailers/admin/card_mailer.rb @@ -5,4 +5,9 @@ def submit(card) mail(to: "nkcr.je@gmail.com", subject: "Un nouveau groupe a été créé") end + def deactivated(card) + @card = card + mail(to: "nkcr.je@gmail.com", subject: "Un groupe est désormais désactivé") + end + end diff --git a/app/mailers/card_mailer.rb b/app/mailers/card_mailer.rb index 8cab2543..a4b5c924 100644 --- a/app/mailers/card_mailer.rb +++ b/app/mailers/card_mailer.rb @@ -29,6 +29,14 @@ def offline(card, message) mail(to: card.user.email, subject: "Ton groupe sur Réseau Jeunesse n'est désormais plus visible !") end + # step indicates the number of reminder sent. 0 is the first "normal" request. + # After the 4th reminder the group is marked as "Deactivated". + def update_check(card, step) + @card = card + @step = step + mail(to: card.user.email, subject: "Vérifie ton groupe sur Réseau Jeunesse") + end + def migration(user) @user = user mail(to: user.email, subject: "Du nouveau sur Réseau Jeunesse") diff --git a/app/models/card.rb b/app/models/card.rb index 20a3a9a4..bf2c9e0c 100644 --- a/app/models/card.rb +++ b/app/models/card.rb @@ -9,6 +9,7 @@ class Card < ApplicationRecord enum card_type: [:youth, :adult, :activist, :organization, :network, :training] enum status: [:pending, :online, :incomplete, :change] + enum validity: [:maintained, :solicited, :disabled] belongs_to :location, optional: true # validation made manually according to the step belongs_to :user diff --git a/app/views/admin/card_mailer/deactivated.erb b/app/views/admin/card_mailer/deactivated.erb new file mode 100644 index 00000000..479ac777 --- /dev/null +++ b/app/views/admin/card_mailer/deactivated.erb @@ -0,0 +1,9 @@ +<% content_for :prehead, "Un groupe est désactivé sur Réseau Jeunesse" %> + +

Le groupe: <%= @card.name %>

+ +

... est désormais désactivé.

+ +<%= render "mailers/button_center" do %> + <%= link_to "Voir le groupe", edit_admin_card_url(@card), target: "_blank" %> +<% end %> \ No newline at end of file diff --git a/app/views/card_mailer/update_check.html.erb b/app/views/card_mailer/update_check.html.erb new file mode 100644 index 00000000..078cc11e --- /dev/null +++ b/app/views/card_mailer/update_check.html.erb @@ -0,0 +1,29 @@ +

Ton groupe <%= @card.name %> est-il encore à jour ?

+ +

Voilà <%= time_ago_in_words(@card.last_updated) %> que ton groupe sur Réseau Jeunesse n'a pas été mis à jour. +<% if @step >= 4 %> +Ton groupe est maintenant désactivé, il faut impérativement le vérifier et/ou le mettre à jour. +<% end %> +

+ +

Afin de garder des informations valides, nous te demandons de vérifier et mettre à jour si nécessaire les informations de ton groupe. +Cette étape est nécessaire pour garder ton groupe sur le site.

+ +<%= render "mailers/button_center" do %> + <%= link_to "Confirmer / Mettre à jour mon groupe", edit_users_card_url(@card, confirm:true), "_target"=>"_blank" %> +<% end %> + +

Tu peux changer en tout temps les informations de ton groupe à partir de +<%= link_to "ton compte", profile_url, target: "_blank" %> sur le site. Tu recevras tous les 6 mois une invitation à contrôler les informations de ton groupe.

+ +<% if @step == 1 %> +

Ceci est le premier rappel.

+<% elsif @step > 1 && @step <= 2 %> +

Ceci est le <%= @step %>e rappel.

+<% elsif @step == 3 %> +

Ceci est le <%= @step %>e rappel.
Attention: le groupe sera désactivé dans 2 semaines s'il n'est pas mis à jour.

+<% elsif @step >= 4 %> +

Ceci est le <%= @step %>e rappel. Nous nous réservons le droit de supprimer ton groupe si aucune action n'est entreprise.

+<% end %> + +

Tu peux en savoir plus sur notre politque de mise à jour des groupes via notre FAQ.

\ No newline at end of file diff --git a/app/views/pages/faq.html.erb b/app/views/pages/faq.html.erb index f2b41b30..601471b7 100644 --- a/app/views/pages/faq.html.erb +++ b/app/views/pages/faq.html.erb @@ -38,6 +38,22 @@ Les demandes pour devenir orateur sont traitées manuellement. Une fois la demande acceptée tu recevras un lien pour te créer un compte orateur.

+ +
+

Pourquoi est-ce que je reçois des mails pour mettre à jour mon groupe ?

+

+ Il est essentiel que les groupes restent à jour. C'est pour ça que nous avons besoin de toi. +

+

+ Pour que les groupes restent à jour, chaque responsable de groupe reçoit tous les 6 mois une invitation à vérifier et mettre à jour si nécessaire son groupe. + Notre système vérifie tous les jours les groupes dont la dernière mise à jour remonte à 6 mois et notifie son responsable par email. Même si le groupe est à jour, + il est nécessaire, via la page d'édition du groupe, de cliquer sur le bouton "Sauvegarder et mettre à jour". +

+

+ Si aucune action n'est effectuée après le premier email, 4 rappels sont envoyés à 2 semaines d'interval chacun. À partir du 4e rappel le groupe est marqué comme "Désactivé" sur le site. Un email est envoyé + tous les deux mois en guise d’ultime invitation à mettre à jour son groupe. L'équipe se réserve ensuite le droit de supprimer définitivement le groupe. +

+
diff --git a/app/views/users/cards/_confirmed.html.erb b/app/views/users/cards/_confirmed.html.erb new file mode 100644 index 00000000..887e7ab6 --- /dev/null +++ b/app/views/users/cards/_confirmed.html.erb @@ -0,0 +1,12 @@ +
+
+
+ +

Merci d'avoir confirmé ton groupe. Aucune autre action de ta part n'est nécessaire.

+ +
+ +
+
+
+
\ No newline at end of file diff --git a/app/views/users/cards/_outdated.html.erb b/app/views/users/cards/_outdated.html.erb new file mode 100644 index 00000000..b174c2fe --- /dev/null +++ b/app/views/users/cards/_outdated.html.erb @@ -0,0 +1,13 @@ +
+
+
+ +

Merci de vérifier (et mettre à jour si nécessaire) les informations de ton groupe.

+

Quand tu as fini, clique sur le bouton "Sauvegarder et mettre à jour", même si tu n'a fait aucun changement.

+ +
+ +
+
+
+
diff --git a/app/views/users/cards/edit.html.erb b/app/views/users/cards/edit.html.erb index 7f2ed6b0..fd222018 100644 --- a/app/views/users/cards/edit.html.erb +++ b/app/views/users/cards/edit.html.erb @@ -5,6 +5,10 @@ <%= link_to "Voir le groupe", card_path(@card) %> <% end %> +<%= render "outdated" if !@card.maintained? %> + +<%= render "confirmed" if @confirmed %> +
diff --git a/db/migrate/20211219170017_add_validity_to_cards.rb b/db/migrate/20211219170017_add_validity_to_cards.rb new file mode 100644 index 00000000..13e32368 --- /dev/null +++ b/db/migrate/20211219170017_add_validity_to_cards.rb @@ -0,0 +1,5 @@ +class AddValidityToCards < ActiveRecord::Migration[6.0] + def change + add_column :cards, :validity, :integer, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index 5c7932b6..505c8900 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_11_06_163807) do +ActiveRecord::Schema.define(version: 2021_12_19_170017) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -112,6 +112,7 @@ t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.datetime "last_updated" + t.integer "validity", default: 0 t.index ["location_id"], name: "index_cards_on_location_id" t.index ["user_id"], name: "index_cards_on_user_id" end diff --git a/features/cards/edit.feature b/features/cards/edit.feature index 0103b200..1b6e0d6c 100644 --- a/features/cards/edit.feature +++ b/features/cards/edit.feature @@ -1,4 +1,4 @@ -Feature: update a card +Feature: Update a card So that the card's information are update as the owner @@ -13,6 +13,7 @@ Feature: update a card When I visit the card's update page And I update the card's name with "Spacestation" Then I should see "Spacestation" + And I should not see "Merci d'avoir confirmé ton groupe" Scenario: I update the card with an error When I visit the card's update page diff --git a/features/cards/validity.feature b/features/cards/validity.feature new file mode 100644 index 00000000..982b961b --- /dev/null +++ b/features/cards/validity.feature @@ -0,0 +1,25 @@ +Feature: Confirm the validity of a card + + So that my card is not disabled + as the owner + I want to update the card + + Background: + Given I am a confirmed user + And I am signed in + + Scenario: The card is up-to-date + Given I have a complete card + When I visit the card's update page + Then I should not see "Merci de vérifier" + + Scenario: The owner received a request to confirm the card + Given I have a card that must be confirmed + When I visit the card's update page + Then I should see "Merci de vérifier" + + Scenario: I update an card after a request + Given I have a card that must be confirmed + When I visit the card's update page + And I click the button "Sauvegarder et mettre à jour" + Then I should see "Merci d'avoir confirmé ton groupe" diff --git a/features/step_definitions/cards/confirmation_steps.rb b/features/step_definitions/cards/confirmation_steps.rb index 4630a0ce..445c1253 100644 --- a/features/step_definitions/cards/confirmation_steps.rb +++ b/features/step_definitions/cards/confirmation_steps.rb @@ -2,6 +2,10 @@ @card = create(:card, user: @user) end +Given('I have a card that must be confirmed') do + @card = create(:card, user: @user, validity: :solicited) +end + Given("I have an incomplete card") do visit "/cards/wizards/new" step "I complete the first step and submit it" diff --git a/lib/tasks/cards_update_check.rake b/lib/tasks/cards_update_check.rake new file mode 100644 index 00000000..2ebcf087 --- /dev/null +++ b/lib/tasks/cards_update_check.rake @@ -0,0 +1,59 @@ +namespace :cards_update do + + desc "Check if a card must be checked by its owner" + task check: :environment do + Card.all.each do |card| + # Here is the schedule reminder: + # + # | Step | Days | + # | ---- | ---- | + # | 0 | 180 | + # | 1 | 180 + 14 | + # | 2 | 180 + 14*2 | + # | 3 | 180 + 14*3 | + # | 4 | 180 + 14*4 | // deactivate the card + # | 5 | 180 + 14*4 + 60 | + # | 5 +i | 180 + 14*4 + 60 * (i+1) | // send every two months + + # to be fair we cap the last_updated to the release of V2 - 6 months to be + # adjusted so that when we release the task we quickly get 180 days. To be + # removed once all cards have received their warnings. + release = Date.strptime("4/7/2021", "%d/%m/%Y") + + if Rails.env == "test" + release = Date.strptime("1/1/1970", "%d/%m/%Y") + end + + base = [release, card.last_updated.to_date].max + + days_from_update = (Time.current.to_date - base).to_i + + days2step = {180 => 0, 180+14 => 1, 180+14*2 => 2, 180+14*3 => 3, 180+14*4 => 4} + + step = days2step[days_from_update] || -1 + + if step == -1 && days_from_update > 180 + 14*4 && (days_from_update - (180 + 14*4)) % 60 == 0 + step = 4 + (days_from_update - (180 + 14*4)) / 60 + end + + # p "step: #{step}, days_from_update: #{days_from_update}" + + # flag the card and notify the admin if not already done + if step >= 4 && !card.disabled? + card.update_attribute(:validity, :disabled) + Admin::CardMailer.deactivated(card).deliver_now + end + + # flag the card if not already done + if step >= 0 && step <= 3 && !card.solicited? + card.update_attribute(:validity, :solicited) + end + + # notify the card owner + if step >= 0 + CardMailer.update_check(card, step).deliver_now + end + end + end + +end \ No newline at end of file diff --git a/spec/factories.rb b/spec/factories.rb index 7e9f7463..6d955c51 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -28,6 +28,7 @@ location { Location.find_by_official_name("Bulle") || create(:location) } card_type { :youth } status { :online } + validity { :maintained } end factory :location do diff --git a/spec/lib/cards_update_check_spec.rb b/spec/lib/cards_update_check_spec.rb new file mode 100644 index 00000000..f648691c --- /dev/null +++ b/spec/lib/cards_update_check_spec.rb @@ -0,0 +1,125 @@ +require 'rails_helper' +require 'rake' + +Rails.application.load_tasks + +RSpec.describe "cards_update_check.rake" do + + cases = [ + # days_since_update, before_validity, expected_validity, user_email (step), admin_email + [0, :maintained, "maintained", -1, false], + [1, :maintained, "maintained", -1, false], + + [180-1, :maintained, "maintained", -1, false], + [180-1, :solicited, "solicited", -1, false], + [180, :maintained, "solicited", 0, false], # normal case + [180, :solicited, "solicited", 0, false], + [180+1, :solicited, "solicited", -1, false], + + [180+14-1, :solicited, "solicited", -1, false], + [180+14, :solicited, "solicited", 1, false], # normal case + [180+14, :maintained, "solicited", 1, false], + [180+14+1, :solicited, "solicited", -1, false], + + [180+14*2-1, :solicited, "solicited", -1, false], + [180+14*2, :solicited, "solicited", 2, false], # normal case + [180+14*2+1, :solicited, "solicited", -1, false], + + [180+14*3-1, :solicited, "solicited", -1, false], + [180+14*3, :solicited, "solicited", 3, false], # normal case + [180+14*3+1, :solicited, "solicited", -1, false], + + [180+14*4-1, :solicited, "solicited", -1, false], + [180+14*4-1, :maintained, "maintained", -1, false], + [180+14*4-1, :disabled, "disabled", -1, false], + [180+14*4, :solicited, "disabled", 4, true], # normal case + [180+14*4, :maintained, "disabled", 4, true], + [180+14*4, :disabled, "disabled", 4, false], + [180+14*4+1, :disabled, "disabled", -1, false], + + [180+14*4+60-1, :disabled, "disabled", -1, false], + [180+14*4+60, :disabled, "disabled", 5, false], # normal case + [180+14*4+60, :maintained, "disabled", 5, true], + [180+14*4+60, :solicited, "disabled", 5, true], + [180+14*4+60+1, :disabled, "disabled", -1, false], + + [180+14*4+60*2-1, :disabled, "disabled", -1, false], + [180+14*4+60*2, :disabled, "disabled", 6, false], # normal case + [180+14*4+60*2+1, :disabled, "disabled", -1, false], + + [180+14*4+60*10-1, :disabled, "disabled", -1, false], + [180+14*4+60*10, :disabled, "disabled", 14, false], # normal case + [180+14*4+60*10+1, :disabled, "disabled", -1, false], + ] + + cases.each do |t| + describe "#{t}" do + it "should have correct validity" do + b = CardValidity.new(self, t[0], t[1]) + b.should_have_validity(t[2]) + end + + it "should send email to user accordingly" do + b = CardValidity.new(self, t[0], t[1]) + if t[3] == -1 + b.should_not_send_user_email + else + b.should_send_user_email_with_step(t[3]) + end + end + + it "should send email to admin accordingly" do + b = CardValidity.new(self, t[0], t[1]) + if t[4] + b.should_send_admin_email + else + b.should_not_send_admin_email + end + end + end + end +end + +# This is a helper class around a card to check its validity and state after +# calling the "cards_update:check" task. +class CardValidity + def initialize(rspec, days_since_update, before_validity) + @r = rspec + + @card = FactoryBot.create(:card, validity: before_validity) + @card.update_attribute(:last_updated, Time.current - days_since_update.days) + end + + def should_have_validity(validity) + run_task + @r.expect(@card.validity).to @r.eq(validity) + end + + def should_send_user_email_with_step(step) + @r.expect(CardMailer).to @r.receive(:update_check).with(@card, step).and_call_original + run_task + end + + def should_not_send_user_email + @r.expect(CardMailer).not_to @r.receive(:update_check).and_call_original + run_task + end + + def should_send_admin_email + @r.expect(Admin::CardMailer).to @r.receive(:deactivated).with(@card).and_call_original + run_task + end + + def should_not_send_admin_email + @r.expect(Admin::CardMailer).not_to @r.receive(:deactivated).with(@card).and_call_original + run_task + end + + private + + def run_task + Rake::Task['cards_update:check'].invoke + Rake::Task['cards_update:check'].reenable + @card.reload + end +end \ No newline at end of file diff --git a/spec/mailers/previews/admin/card_mailer_preview.rb b/spec/mailers/previews/admin/card_mailer_preview.rb index 9af6dd92..1361f3ce 100644 --- a/spec/mailers/previews/admin/card_mailer_preview.rb +++ b/spec/mailers/previews/admin/card_mailer_preview.rb @@ -9,4 +9,10 @@ def submit Admin::CardMailer.submit(card) end + def deactivated + card = FactoryBot.build(:card) + card.id = 1 + Admin::CardMailer.deactivated(card) + end + end diff --git a/spec/mailers/previews/card_mailer_preview.rb b/spec/mailers/previews/card_mailer_preview.rb index 6118f3f0..bcf43a55 100644 --- a/spec/mailers/previews/card_mailer_preview.rb +++ b/spec/mailers/previews/card_mailer_preview.rb @@ -32,6 +32,13 @@ def offline CardMailer.offline(card, "Il manque une description complète") end + def update_check + card = FactoryBot.build(:card) + card.id = 1 + card.last_updated = Time.current - 6.month + CardMailer.update_check(card, 7) + end + def migration user = FactoryBot.build(:user) CardMailer.migration(user)