Merge pull request #1733 from internetee/1177-bulk-force-delete

Admin: Mass action for soft force delete
This commit is contained in:
Timo Võhmar 2021-01-06 16:16:33 +02:00 committed by GitHub
commit d387113d86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 227 additions and 5 deletions

View file

@ -0,0 +1,27 @@
# frozen_string_literal: true
module Admin
class MassActionsController < BaseController
before_action :authorize_admin
# GET /admin/mass_actions
def index; end
# POST /admin/mass_actions
def create
res = MassAction.process(params[:mass_action], params[:entry_list].path)
notice = if res
"#{params[:mass_action]} completed for #{res[:ok]}.\n" \
"Failed: #{res[:fail]}"
else
"Dataset integrity validation failed for #{params[:mass_action]}"
end
redirect_to(admin_mass_actions_path, notice: notice)
end
def authorize_admin
authorize! :manage, :mass_actions
end
end
end

View file

@ -10,6 +10,9 @@ module Domains
boolean :notify_by_email, boolean :notify_by_email,
default: false, default: false,
description: 'Do we need to send email notification' description: 'Do we need to send email notification'
string :reason,
default: nil,
description: 'Which mail template to use explicitly'
validates :type, inclusion: { in: %i[fast_track soft] } validates :type, inclusion: { in: %i[fast_track soft] }
end end

View file

@ -8,7 +8,7 @@ module Domains
send_email send_email
domain.update(contact_notification_sent_date: Time.zone.today) domain.update(contact_notification_sent_date: Time.zone.today)
else else
domain.update(template_name: domain.notification_template) domain.update(template_name: domain.notification_template(explicit: reason))
end end
end end

View file

@ -109,6 +109,7 @@ class Ability
can :destroy, :pending can :destroy, :pending
can :create, :zonefile can :create, :zonefile
can :access, :settings_menu can :access, :settings_menu
can :manage, :mass_actions
can :manage, BouncedMailAddress can :manage, BouncedMailAddress
end end

View file

@ -19,7 +19,10 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength
end end
end end
def notification_template def notification_template(explicit: nil)
reason = explicit&.downcase
return reason if %w[invalid_email invalid_phone].include?(reason)
if contact_emails_verification_failed.present? if contact_emails_verification_failed.present?
'invalid_email' 'invalid_email'
elsif registrant.org? elsif registrant.org?
@ -33,9 +36,8 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength
statuses.include?(DomainStatus::FORCE_DELETE) statuses.include?(DomainStatus::FORCE_DELETE)
end end
def schedule_force_delete(type: :fast_track, notify_by_email: false) def schedule_force_delete(type: :fast_track, notify_by_email: false, reason: nil)
Domains::ForceDelete::SetForceDelete.run(domain: self, Domains::ForceDelete::SetForceDelete.run(domain: self, type: type, reason: reason,
type: type,
notify_by_email: notify_by_email) notify_by_email: notify_by_email)
end end

42
app/models/mass_action.rb Normal file
View file

@ -0,0 +1,42 @@
class MassAction
def self.process(action_type, entries)
entries = CSV.read(entries, headers: true)
case action_type
when 'force_delete'
process_force_delete(entries)
else
false
end
rescue StandardError
false
end
def self.process_force_delete(entries)
return false unless force_delete_entries_valid?(entries)
apply_force_deletes(entries)
end
def self.apply_force_deletes(entries)
log = { ok: [], fail: [] }
entries.each do |e|
dn = Domain.find_by(name_puny: e['domain_name'])
log[:fail] << e['domain_name'] and next unless dn
dn.schedule_force_delete(type: :soft, notify_by_email: true, reason: e['delete_reason'])
log[:ok] << dn.name
end
log
end
def self.force_delete_entries_valid?(entries)
entries.each do |e|
reasons = %w[ENTITY_BURIED INVALID_EMAIL INVALID_PHONE]
return false unless e['domain_name'].present? && reasons.include?(e['delete_reason'])
end
true
end
end

View file

@ -33,6 +33,7 @@
%li= link_to t('.blocked_domains'), admin_blocked_domains_path %li= link_to t('.blocked_domains'), admin_blocked_domains_path
%li= link_to t('.reserved_domains'), admin_reserved_domains_path %li= link_to t('.reserved_domains'), admin_reserved_domains_path
%li= link_to t('.disputed_domains'), admin_disputes_path %li= link_to t('.disputed_domains'), admin_disputes_path
%li= link_to t('.bulk_actions'), admin_mass_actions_path
%li= link_to t('.bounced_email_addresses'), admin_bounced_mail_addresses_path %li= link_to t('.bounced_email_addresses'), admin_bounced_mail_addresses_path
%li= link_to t('.epp_log'), admin_epp_logs_path(created_after: 'today') %li= link_to t('.epp_log'), admin_epp_logs_path(created_after: 'today')
%li= link_to t('.repp_log'), admin_repp_logs_path(created_after: 'today') %li= link_to t('.repp_log'), admin_repp_logs_path(created_after: 'today')

View file

@ -0,0 +1,19 @@
<div class="page-header">
<h1>Bulk actions</h1>
</div>
<div class="panel panel-default">
<div class="panel-heading">Bulk Domain Force Delete</div>
<div class="panel-body">
<p>Triggers <b>soft</b> force delete procedure for uploaded domain list. List must be in <b>CSV</b> format. First row of the CSV file must contain column headings with <b>domain_name</b> for the first and <b>delete_reason</b> for the second column. Each domain entry must be on separate line. Domain names are expected to be in punycode format, valid reasons are listed below.</p>
<p>Allowed delete reasons: <b>ENTITY_BURIED</b> | <b>INVALID_PHONE</b> | <b>INVALID_EMAIL</b></p>
<%= form_tag admin_mass_actions_path, multipart: true, method: :post do %>
<%= label_tag :entry_list %>
<%= file_field_tag :entry_list, required: true, accept: 'text/csv' %>
<%= hidden_field_tag :mass_action, 'force_delete' %>
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
<br>
<%= submit_tag "Start force delete process", class: 'btn btn-danger', id: 'fd_submit' %>
<% end %>
</div>
</div>

View file

@ -0,0 +1,47 @@
<p>Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt</p>
<p>Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontakti(de) telefoni number või numbrid on puudulikud.</p>
<p>Et see olukord on vastuolus .ee <a href='https://www.internet.ee/domains/ee-domain-regulation'>domeenireeglitega</a> algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.</p>
<p>Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul <a href="https://registrant.internet.ee/">registreerija portaali</a>.</p>
<p>Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile <a href="https://auction.internet.ee">.ee oksjonikeskkonda</a>. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe <a href="https://www.internet.ee/domeenid/domeenide-oksjonikeskkonna-kasutajatingimused#3-oksjonikeskkonna-enampakkumisel-osalemise-tingimused">siit</a>.</p>
<p>Lisaküsimuste korral võtke palun ühendust oma registripidajaga:</p>
<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %>
<%= render 'mailers/shared/signatures/signature.et.html' %>
<hr>
<p>Dear registrant/administrative contact of .ee domain,</p>
<p>Estonian Internet Foundation has learned that contact(s) phone number data of the domain <%= @domain.name %> are invalid.</p>
<p>Since this is a violation of <a href='https://www.internet.ee/domains/ee-domain-regulation'>Estonian domain regulations</a>, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.</p>
<p>Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use <a href="https://registrant.internet.ee/">.ee portal for registrants</a></p>
<p>If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the <a href="https://auction.internet.ee">.ee auction environment</a>. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results <a href="https://www.internet.ee/domains/auction-environment-user-agreement#3-terms-and-conditions-for-participation-in-the-auction-of-the-auction-environment">here</a>.</p>
<p>Should you have additional questions, please contact your registrar:</p>
<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %>
<%= render 'mailers/shared/signatures/signature.en.html' %>
<hr>
<p>Уважаемый регистрант/административный контакт домена .ee</p>
<p>Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @domain.name %> неверны - телефонные номера.</p>
<p>Так как это является нарушением <a href='https://www.internet.ee/domains/ee-domain-regulation'>Правил домена .ee</a>, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.</p>
<p>Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь <a href="https://registrant.internet.ee/">порталом для регистрантов</a></p>
<p>Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в <a href="https://auction.internet.ee">аукционной среде.ee</a>. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте <a href="https://www.internet.ee/domains/auction-environment-user-agreement#3-terms-and-conditions-for-participation-in-the-auction-of-the-auction-environment">здесь</a>.</p>
<p>В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором:
<%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %></p>
<%= render 'mailers/shared/signatures/signature.ru.html' %>

View file

@ -0,0 +1,47 @@
<p>Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt</p>
<p>Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontakti(de) telefoni number või numbrid on puudulikud.</p>
<p>Et see olukord on vastuolus .ee <a href='https://www.internet.ee/domains/ee-domain-regulation'>domeenireeglitega</a> algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.</p>
<p>Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul <a href="https://registrant.internet.ee/">registreerija portaali</a>.</p>
<p>Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile <a href="https://auction.internet.ee">.ee oksjonikeskkonda</a>. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe <a href="https://www.internet.ee/domeenid/domeenide-oksjonikeskkonna-kasutajatingimused#3-oksjonikeskkonna-enampakkumisel-osalemise-tingimused">siit</a>.</p>
<p>Lisaküsimuste korral võtke palun ühendust oma registripidajaga:</p>
<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %>
<%= render 'mailers/shared/signatures/signature.et.html' %>
<hr>
<p>Dear registrant/administrative contact of .ee domain,</p>
<p>Estonian Internet Foundation has learned that contact(s) phone number data of the domain <%= @domain.name %> are invalid.</p>
<p>Since this is a violation of <a href='https://www.internet.ee/domains/ee-domain-regulation'>Estonian domain regulations</a>, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.</p>
<p>Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use <a href="https://registrant.internet.ee/">.ee portal for registrants</a></p>
<p>If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the <a href="https://auction.internet.ee">.ee auction environment</a>. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results <a href="https://www.internet.ee/domains/auction-environment-user-agreement#3-terms-and-conditions-for-participation-in-the-auction-of-the-auction-environment">here</a>.</p>
<p>Should you have additional questions, please contact your registrar:</p>
<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %>
<%= render 'mailers/shared/signatures/signature.en.html' %>
<hr>
<p>Уважаемый регистрант/административный контакт домена .ee</p>
<p>Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @domain.name %> неверны - телефонные номера.</p>
<p>Так как это является нарушением <a href='https://www.internet.ee/domains/ee-domain-regulation'>Правил домена .ee</a>, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.</p>
<p>Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь <a href="https://registrant.internet.ee/">порталом для регистрантов</a></p>
<p>Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в <a href="https://auction.internet.ee">аукционной среде.ee</a>. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте <a href="https://www.internet.ee/domains/auction-environment-user-agreement#3-terms-and-conditions-for-participation-in-the-auction-of-the-auction-environment">здесь</a>.</p>
<p>В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором:
<%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %></p>
<%= render 'mailers/shared/signatures/signature.ru.html' %>

View file

@ -14,6 +14,7 @@ en:
blocked_domains: Blocked domains blocked_domains: Blocked domains
reserved_domains: Reserved domains reserved_domains: Reserved domains
disputed_domains: Disputed domains disputed_domains: Disputed domains
bulk_actions: Bulk actions
bounced_email_addresses: Bounced emails bounced_email_addresses: Bounced emails
epp_log: EPP log epp_log: EPP log
repp_log: REPP log repp_log: REPP log

View file

@ -322,6 +322,7 @@ Rails.application.routes.draw do
resources :delayed_jobs resources :delayed_jobs
resources :epp_logs resources :epp_logs
resources :repp_logs resources :repp_logs
resources :mass_actions, only: %i[index create]
resources :bounced_mail_addresses, only: %i[index show destroy] resources :bounced_mail_addresses, only: %i[index show destroy]
authenticate :admin_user do authenticate :admin_user do

View file

@ -0,0 +1,2 @@
domain_name,delete_reason
sh\á;[]c'
1 domain_name,delete_reason
2 sh\á;[]c'

View file

@ -0,0 +1,5 @@
domain_name,delete_reason
shop.test,ENTITY_BURIED
airport.test,INVALID_PHONE
library.test,INVALID_EMAIL
nonexistant.test,ENTITY_BURIED
1 domain_name delete_reason
2 shop.test ENTITY_BURIED
3 airport.test INVALID_PHONE
4 library.test INVALID_EMAIL
5 nonexistant.test ENTITY_BURIED

View file

@ -0,0 +1,24 @@
require 'application_system_test_case'
require 'test_helper'
class AdminAreaMassActionsForceDeleteTest < ApplicationSystemTestCase
def setup
sign_in users(:admin)
end
def test_processes_uploaded_valid_csv
visit admin_mass_actions_path
attach_file('entry_list', Rails.root.join('test', 'fixtures', 'files', 'mass_actions', 'valid_mass_force_delete_list.csv').to_s)
click_link_or_button 'Start force delete process'
assert_text 'force_delete completed for ["shop.test", "airport.test", "library.test"]. Failed: ["nonexistant.test"]'
end
def test_processes_uploaded_invalid_csv
visit admin_mass_actions_path
attach_file(:entry_list, Rails.root.join('test', 'fixtures', 'files', 'mass_actions', 'invalid_mass_force_delete_list.csv').to_s)
click_link_or_button 'Start force delete process'
assert_text 'Dataset integrity validation failed for force_delete'
end
end