aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rwxr-xr-xbin/process-inactive-accounts43
-rw-r--r--perllib/FixMyStreet/Script/Inactive.pm93
-rw-r--r--t/script/inactive.t28
-rw-r--r--templates/email/default/inactive-account.html26
-rw-r--r--templates/email/default/inactive-account.txt18
6 files changed, 209 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 104ad1c3c..13d45bd5b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -54,6 +54,7 @@
- Deleted body categories now hidden by default #1962
- Display contents of report's extra field #1809
- Store user creation and last active times.
+ - Add script to anonymize/email inactive users.
- Development improvements:
- Add HTML email previewer.
- Add CORS header to Open311 output. #2022
diff --git a/bin/process-inactive-accounts b/bin/process-inactive-accounts
new file mode 100755
index 000000000..3df200d3d
--- /dev/null
+++ b/bin/process-inactive-accounts
@@ -0,0 +1,43 @@
+#!/usr/bin/env perl
+
+use v5.14;
+use warnings;
+
+BEGIN {
+ use File::Basename qw(dirname);
+ use File::Spec;
+ my $d = dirname(File::Spec->rel2abs($0));
+ require "$d/../setenv.pl";
+}
+
+use Getopt::Long;
+use FixMyStreet::Script::Inactive;
+use Pod::Usage;
+
+my %h;
+GetOptions(\%h, 'anonymize=i', 'email=i', 'verbose|v', 'help|h', 'dry-run|n');
+pod2usage(0) if $h{help};
+pod2usage(1) if !$h{anonymize};
+pod2usage("Anonymize time must be greater than email time")
+ if $h{email} && $h{email} >= $h{anonymize};
+
+FixMyStreet::Script::Inactive->new(%h)->users;
+
+__END__
+
+=head1 NAME
+
+process-inactive-accounts - deal with anonymizing old inactive accounts
+
+=head1 SYNOPSIS
+
+process-inactive-accounts --anonymize N [--email N]
+
+ Options:
+ --anonymize Anonymize accounts inactive longer than this time (months)
+ --email Email accounts inactive longer than this time (months)
+ --dry-run Don't actually anonymize anything or send any emails
+ --verbose Output as to which users are being affected
+ --help This help message
+
+=cut
diff --git a/perllib/FixMyStreet/Script/Inactive.pm b/perllib/FixMyStreet/Script/Inactive.pm
new file mode 100644
index 000000000..766135e12
--- /dev/null
+++ b/perllib/FixMyStreet/Script/Inactive.pm
@@ -0,0 +1,93 @@
+package FixMyStreet::Script::Inactive;
+
+use v5.14;
+use warnings;
+
+use Moo;
+use CronFns;
+use FixMyStreet;
+use FixMyStreet::Cobrand;
+use FixMyStreet::DB;
+use FixMyStreet::Email;
+
+has anonymize => ( is => 'ro' );
+has email => ( is => 'ro' );
+has verbose => ( is => 'ro' );
+has dry_run => ( is => 'ro' );
+
+sub BUILDARGS {
+ my ($cls, %args) = @_;
+ $args{dry_run} = delete $args{'dry-run'};
+ return \%args;
+}
+
+has cobrand => (
+ is => 'lazy',
+ default => sub {
+ my $base_url = FixMyStreet->config('BASE_URL');
+ my $site = CronFns::site($base_url);
+ my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($site)->new;
+ $cobrand->set_lang_and_domain(undef, 1);
+ $cobrand;
+ },
+);
+
+sub users {
+ my $self = shift;
+
+ say "DRY RUN" if $self->dry_run;
+ $self->anonymize_users;
+ $self->email_inactive_users if $self->email;
+}
+
+sub anonymize_users {
+ my $self = shift;
+
+ my $users = FixMyStreet::DB->resultset("User")->search({
+ last_active => { '<', interval($self->anonymize) },
+ });
+
+ while (my $user = $users->next) {
+ say "Anonymizing user #" . $user->id if $self->verbose;
+ next if $self->dry_run;
+ $user->anonymize_account;
+ }
+}
+
+sub email_inactive_users {
+ my $self = shift;
+
+ my $users = FixMyStreet::DB->resultset("User")->search({
+ last_active => [ -and => { '<', interval($self->email) },
+ { '>=', interval($self->anonymize) } ],
+ });
+ while (my $user = $users->next) {
+ next if $user->get_extra_metadata('inactive_email_sent');
+
+ say "Emailing user #" . $user->id if $self->verbose;
+ next if $self->dry_run;
+ FixMyStreet::Email::send_cron(
+ $user->result_source->schema,
+ 'inactive-account.txt',
+ {
+ email_from => $self->email,
+ anonymize_from => $self->anonymize,
+ user => $user,
+ url => $self->cobrand->base_url_with_lang . '/my',
+ },
+ { To => [ $user->email, $user->name ] },
+ undef, 0, $self->cobrand,
+ );
+
+ $user->set_extra_metadata('inactive_email_sent', 1);
+ $user->update;
+ }
+}
+
+sub interval {
+ my $interval = shift;
+ my $s = "current_timestamp - '$interval months'::interval";
+ return \$s;
+}
+
+1;
diff --git a/t/script/inactive.t b/t/script/inactive.t
new file mode 100644
index 000000000..0eaeea2ad
--- /dev/null
+++ b/t/script/inactive.t
@@ -0,0 +1,28 @@
+use FixMyStreet::TestMech;
+
+use_ok 'FixMyStreet::Script::Inactive';
+
+my $in = FixMyStreet::Script::Inactive->new( anonymize => 6, email => 3 );
+my $mech = FixMyStreet::TestMech->new;
+
+my $user = FixMyStreet::DB->resultset("User")->find_or_create({ email => 'test@example.com' });
+my $t = DateTime->new(year => 2016, month => 1, day => 1, hour => 12);
+$user->last_active($t);
+$user->update;
+
+my $user_inactive = FixMyStreet::DB->resultset("User")->find_or_create({ email => 'inactive@example.com' });
+$t = DateTime->now->subtract(months => 4);
+$user_inactive->last_active($t);
+$user_inactive->update;
+
+subtest 'Anonymization of inactive users' => sub {
+ $in->users;
+
+ my $email = $mech->get_email;
+ like $email->as_string, qr/inactive\@example.com/, 'Inactive email sent';
+
+ $user->discard_changes;
+ is $user->email, 'removed-' . $user->id . '@example.org', 'User has been anonymized';
+};
+
+done_testing;
diff --git a/templates/email/default/inactive-account.html b/templates/email/default/inactive-account.html
new file mode 100644
index 000000000..78b277877
--- /dev/null
+++ b/templates/email/default/inactive-account.html
@@ -0,0 +1,26 @@
+[%
+
+email_summary = "Your inactive account on " _ site_name;
+email_columns = 1;
+
+PROCESS '_email_settings.html';
+
+INCLUDE '_email_top.html';
+
+%]
+
+<th style="[% td_style %][% only_column_style %]">
+ <h1 style="[% h1_style %]">Your inactive account</h1>
+ <p style="[% p_style %]">
+Your account on [% site_name %] has been inactive for [% email_from %]
+[% nget('month', 'months', email_from) %], and we automatically remove
+accounts that have been inactive after [% anonymize_from %]
+[% nget('month', 'months', anonymize_from) %]. If you wish to keep your
+account, please log in to the site and that will keep it active:
+</p>
+ <p style="margin: 20px auto; text-align: center">
+ <a style="[% button_style %]" href="[% url %]">Visit [% site_name %]</a>
+ </p>
+ <p style="[% p_style %]">Thanks for using the site.</p>
+
+[% INCLUDE '_email_bottom.html' %]
diff --git a/templates/email/default/inactive-account.txt b/templates/email/default/inactive-account.txt
new file mode 100644
index 000000000..1b6b8b5bd
--- /dev/null
+++ b/templates/email/default/inactive-account.txt
@@ -0,0 +1,18 @@
+Subject: Your inactive account on [% site_name %]
+
+Hello [% user.name %],
+
+Your account on [% site_name %] has been inactive for [% email_from %]
+[% nget('month', 'months', email_from) %], and we automatically remove
+accounts that have been inactive after [% anonymize_from %]
+[% nget('month', 'months', anonymize_from) %]. If you wish to keep your
+account, please log in to the site and that will keep it active:
+
+[% url %]
+
+Thanks for using the site.
+
+[% INCLUDE 'signature.txt' %]
+
+This email was sent automatically, from an unmonitored email account - so
+please do not reply to it.