aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/send-alerts25
-rwxr-xr-xbin/send-reports9
-rw-r--r--conf/general-example1
-rw-r--r--perllib/FixMyStreet/Alert.pm242
-rw-r--r--perllib/FixMyStreet/App/Controller/Alert.pm16
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/New.pm18
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/Update.pm13
-rw-r--r--perllib/FixMyStreet/App/Controller/Reports.pm1
-rw-r--r--perllib/FixMyStreet/App/Controller/Tokens.pm13
-rw-r--r--perllib/FixMyStreet/DB/Result/Alert.pm12
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/AlertType.pm204
-rw-r--r--perllib/FixMyStreet/TestMech.pm5
-rw-r--r--t/app/controller/alert_new.t141
-rw-r--r--t/app/controller/report_new.t17
-rw-r--r--templates/web/default/alert/list.html4
-rw-r--r--templates/web/default/report/display.html2
16 files changed, 399 insertions, 324 deletions
diff --git a/bin/send-alerts b/bin/send-alerts
index c52af4059..89dc18ee7 100755
--- a/bin/send-alerts
+++ b/bin/send-alerts
@@ -5,37 +5,18 @@
#
# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: send-alerts,v 1.5 2010-01-06 16:50:25 louise Exp $
use strict;
require 5.8.0;
use CGI; # XXX
-
-# Horrible boilerplate to set up appropriate library paths.
-use FindBin;
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
use CronFns;
use mySociety::Config;
-use mySociety::DBHandle qw(dbh);
-use FixMyStreet::Alert;
-
-BEGIN {
- mySociety::Config::set_file("$FindBin::Bin/../conf/general");
- mySociety::DBHandle::configure(
- Name => mySociety::Config::get('BCI_DB_NAME'),
- User => mySociety::Config::get('BCI_DB_USER'),
- Password => mySociety::Config::get('BCI_DB_PASS'),
- Host => mySociety::Config::get('BCI_DB_HOST', undef),
- Port => mySociety::Config::get('BCI_DB_PORT', undef)
- );
-}
+use FixMyStreet::App;
my $site = CronFns::site(mySociety::Config::get('BASE_URL'));
CronFns::language($site);
-my $testing_email = mySociety::Config::get('TESTING_EMAIL');
-FixMyStreet::Alert::email_alerts($testing_email);
+
+FixMyStreet::App->model('DB::AlertType')->email_alerts();
diff --git a/bin/send-reports b/bin/send-reports
index feee74c76..fbbd3db5e 100755
--- a/bin/send-reports
+++ b/bin/send-reports
@@ -192,14 +192,11 @@ while (my $row = $unsent->next) {
die 'Report not going anywhere for ID ' . $row->id . '!';
}
- my $testing_email = mySociety::Config::get('TESTING_EMAIL');
- if ($row->user->email eq $testing_email) {
- @recips = ( $testing_email );
- $send_web = 0;
- $send_email = 1;
- } elsif (mySociety::Config::get('STAGING_SITE')) {
+ if (mySociety::Config::get('STAGING_SITE')) {
# on a staging server send emails to ourselves rather than the councils
@recips = ( mySociety::Config::get('CONTACT_EMAIL') );
+ $send_web = 0;
+ $send_email = 1;
} elsif ($site eq 'emptyhomes') {
my $council = $row->council;
my $country = $areas_info->{$council}->{country};
diff --git a/conf/general-example b/conf/general-example
index 7e750de96..510557e46 100644
--- a/conf/general-example
+++ b/conf/general-example
@@ -30,7 +30,6 @@ define('OPTION_BASE_URL', 'http://www.example.org');
# Which country are you operating in? ISO3166-alpha2 code please
define('OPTION_COUNTRY', 'GB');
-define('OPTION_TESTING_EMAIL', 'testing@example.com');
define('OPTION_EMAIL_DOMAIN', 'example.org');
define('OPTION_CONTACT_EMAIL', 'team@'.OPTION_EMAIL_DOMAIN);
define('OPTION_TEST_EMAIL_PREFIX', null);
diff --git a/perllib/FixMyStreet/Alert.pm b/perllib/FixMyStreet/Alert.pm
deleted file mode 100644
index 1857abbba..000000000
--- a/perllib/FixMyStreet/Alert.pm
+++ /dev/null
@@ -1,242 +0,0 @@
-#!/usr/bin/perl -w
-#
-# FixMyStreet::Alert.pm
-# Alerts by email or RSS.
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: Alert.pm,v 1.71 2010-01-06 16:50:27 louise Exp $
-
-package FixMyStreet::Alert::Error;
-
-use Error qw(:try);
-
-@FixMyStreet::Alert::Error::ISA = qw(Error::Simple);
-
-package FixMyStreet::Alert;
-
-use strict;
-use Error qw(:try);
-use File::Slurp;
-use FindBin;
-use POSIX qw(strftime);
-
-use Cobrand;
-use mySociety::AuthToken;
-use mySociety::DBHandle qw(dbh);
-use mySociety::Email;
-use mySociety::EmailUtil;
-use mySociety::Gaze;
-use mySociety::Locale;
-use mySociety::MaPit;
-use mySociety::Random qw(random_bytes);
-
-# Add a new alert
-sub create ($$$$;@) {
- my ($email, $alert_type, $cobrand, $cobrand_data, @params) = @_;
- my $already = 0;
- if (0==@params) {
- ($already) = dbh()->selectrow_array('select id from alert where alert_type=? and email=? limit 1',
- {}, $alert_type, $email);
- } elsif (1==@params) {
- ($already) = dbh()->selectrow_array('select id from alert where alert_type=? and email=? and parameter=? limit 1',
- {}, $alert_type, $email, @params);
- } elsif (2==@params) {
- ($already) = dbh()->selectrow_array('select id from alert where alert_type=? and email=? and parameter=? and parameter2=? limit 1',
- {}, $alert_type, $email, @params);
- }
- return $already if $already;
-
- my $id = dbh()->selectrow_array("select nextval('alert_id_seq');");
- my $lang = $mySociety::Locale::lang;
- if (0==@params) {
- dbh()->do('insert into alert (id, alert_type, email, lang, cobrand, cobrand_data)
- values (?, ?, ?, ?, ?, ?)', {}, $id, $alert_type, $email, $lang, $cobrand, $cobrand_data);
- } elsif (1==@params) {
- dbh()->do('insert into alert (id, alert_type, parameter, email, lang, cobrand, cobrand_data)
- values (?, ?, ?, ?, ?, ?, ?)', {}, $id, $alert_type, @params, $email, $lang, $cobrand, $cobrand_data);
- } elsif (2==@params) {
- dbh()->do('insert into alert (id, alert_type, parameter, parameter2, email, lang, cobrand, cobrand_data)
- values (?, ?, ?, ?, ?, ?, ?, ?)', {}, $id, $alert_type, @params, $email, $lang, $cobrand, $cobrand_data);
- }
- dbh()->commit();
- return $id;
-}
-
-sub confirm ($) {
- my $id = shift;
- dbh()->do("update alert set confirmed=1, whendisabled=null where id=?", {}, $id);
- dbh()->commit();
-}
-
-# Delete an alert
-sub delete ($) {
- my $id = shift;
- dbh()->do('update alert set whendisabled = ms_current_timestamp() where id = ?', {}, $id);
- dbh()->commit();
-}
-
-# This makes load of assumptions, but still should be useful
-#
-# Child must have confirmed, id, email, state(!) columns
-# If parent/child, child table must also have name and text
-# and foreign key to parent must be PARENT_id
-
-sub email_alerts ($) {
- my ($testing_email) = @_;
- my $url;
- my $q = dbh()->prepare("select * from alert_type where ref not like '%local_problems%'");
- $q->execute();
- my $testing_email_clause = '';
- while (my $alert_type = $q->fetchrow_hashref) {
- my $ref = $alert_type->{ref};
- my $head_table = $alert_type->{head_table};
- my $item_table = $alert_type->{item_table};
- my $testing_email_clause = "and $item_table.email <> '$testing_email'" if $testing_email;
- my $query = 'select alert.id as alert_id, alert.email as alert_email, alert.lang as alert_lang, alert.cobrand as alert_cobrand,
- alert.cobrand_data as alert_cobrand_data, alert.parameter as alert_parameter, alert.parameter2 as alert_parameter2, ';
- if ($head_table) {
- $query .= "
- $item_table.id as item_id, $item_table.name as item_name, $item_table.text as item_text,
- $head_table.*
- from alert
- inner join $item_table on alert.parameter::integer = $item_table.${head_table}_id
- inner join $head_table on alert.parameter::integer = $head_table.id";
- } else {
- $query .= " $item_table.*,
- $item_table.id as item_id
- from alert, $item_table";
- }
- $query .= "
- where alert_type='$ref' and whendisabled is null and $item_table.confirmed >= whensubscribed
- and $item_table.confirmed >= ms_current_timestamp() - '7 days'::interval
- and (select whenqueued from alert_sent where alert_sent.alert_id = alert.id and alert_sent.parameter::integer = $item_table.id) is null
- and $item_table.email <> alert.email
- $testing_email_clause
- and $alert_type->{item_where}
- and alert.confirmed = 1
- order by alert.id, $item_table.confirmed";
- # XXX Ugh - needs work
- $query =~ s/\?/alert.parameter/ if ($query =~ /\?/);
- $query =~ s/\?/alert.parameter2/ if ($query =~ /\?/);
- $query = dbh()->prepare($query);
- $query->execute();
- my $last_alert_id;
- my %data = ( template => $alert_type->{template}, data => '' );
- while (my $row = $query->fetchrow_hashref) {
- # Cobranded and non-cobranded messages can share a database. In this case, the conf file
- # should specify a vhost to send the reports for each cobrand, so that they don't get sent
- # more than once if there are multiple vhosts running off the same database. The email_host
- # call checks if this is the host that sends mail for this cobrand.
- next unless (Cobrand::email_host($row->{alert_cobrand}));
-
- dbh()->do('insert into alert_sent (alert_id, parameter) values (?,?)', {}, $row->{alert_id}, $row->{item_id});
- if ($last_alert_id && $last_alert_id != $row->{alert_id}) {
- _send_aggregated_alert_email(%data);
- %data = ( template => $alert_type->{template}, data => '' );
- }
-
- # create problem status message for the templates
- $data{state_message} =
- $row->{state} eq 'fixed'
- ? _("This report is currently marked as fixed.")
- : _("This report is currently marked as open.");
-
- $url = Cobrand::base_url_for_emails($row->{alert_cobrand}, $row->{alert_cobrand_data});
- if ($row->{item_text}) {
- $data{problem_url} = $url . "/report/" . $row->{id};
- $data{data} .= $row->{item_name} . ' : ' if $row->{item_name};
- $data{data} .= $row->{item_text} . "\n\n------\n\n";
- } else {
- $data{data} .= $url . "/report/" . $row->{id} . " - $row->{title}\n\n";
- }
- if (!$data{alert_email}) {
- %data = (%data, %$row);
- if ($ref eq 'area_problems' || $ref eq 'council_problems' || $ref eq 'ward_problems') {
- my $va_info = mySociety::MaPit::call('area', $row->{alert_parameter});
- $data{area_name} = $va_info->{name};
- }
- if ($ref eq 'ward_problems') {
- my $va_info = mySociety::MaPit::call('area', $row->{alert_parameter2});
- $data{ward_name} = $va_info->{name};
- }
- }
- $data{cobrand} = $row->{alert_cobrand};
- $data{cobrand_data} = $row->{alert_cobrand_data};
- $data{lang} = $row->{alert_lang};
- $last_alert_id = $row->{alert_id};
- }
- if ($last_alert_id) {
- _send_aggregated_alert_email(%data);
- }
- }
-
- # Nearby done separately as the table contains the parameters
- my $template = dbh()->selectrow_array("select template from alert_type where ref = 'local_problems'");
- my $query = "select * from alert where alert_type='local_problems' and whendisabled is null and confirmed=1 order by id";
- $query = dbh()->prepare($query);
- $query->execute();
- while (my $alert = $query->fetchrow_hashref) {
- next unless (Cobrand::email_host($alert->{cobrand}));
- my $longitude = $alert->{parameter};
- my $latitude = $alert->{parameter2};
- $url = Cobrand::base_url_for_emails($alert->{cobrand}, $alert->{cobrand_data});
- my ($site_restriction, $site_id) = Cobrand::site_restriction($alert->{cobrand}, $alert->{cobrand_data});
- my $d = mySociety::Gaze::get_radius_containing_population($latitude, $longitude, 200000);
- # Convert integer to GB locale string (with a ".")
- $d = mySociety::Locale::in_gb_locale {
- sprintf("%f", int($d*10+0.5)/10);
- };
- my $testing_email_clause = "and problem.email <> '$testing_email'" if $testing_email;
- my %data = ( template => $template, data => '', alert_id => $alert->{id}, alert_email => $alert->{email}, lang => $alert->{lang}, cobrand => $alert->{cobrand}, cobrand_data => $alert->{cobrand_data} );
- my $q = "select * from problem_find_nearby(?, ?, ?) as nearby, problem
- where nearby.problem_id = problem.id and problem.state in ('confirmed', 'fixed')
- and problem.confirmed >= ? and problem.confirmed >= ms_current_timestamp() - '7 days'::interval
- and (select whenqueued from alert_sent where alert_sent.alert_id = ? and alert_sent.parameter::integer = problem.id) is null
- and problem.email <> ?
- $testing_email_clause
- $site_restriction
- order by confirmed desc";
- $q = dbh()->prepare($q);
- $q->execute($latitude, $longitude, $d, $alert->{whensubscribed}, $alert->{id}, $alert->{email});
- while (my $row = $q->fetchrow_hashref) {
- dbh()->do('insert into alert_sent (alert_id, parameter) values (?,?)', {}, $alert->{id}, $row->{id});
- $data{data} .= $url . "/report/" . $row->{id} . " - $row->{title}\n\n";
- }
- _send_aggregated_alert_email(%data) if $data{data};
- }
-}
-
-sub _send_aggregated_alert_email(%) {
- my %data = @_;
- Cobrand::set_lang_and_domain($data{cobrand}, $data{lang}, 1);
-
- $data{unsubscribe_url} = Cobrand::base_url_for_emails($data{cobrand}, $data{cobrand_data}) . '/A/'
- . mySociety::AuthToken::store('alert', { id => $data{alert_id}, type => 'unsubscribe', email => $data{alert_email} } );
- my $template = "$FindBin::Bin/../templates/emails/$data{template}";
- if ($data{cobrand}) {
- my $template_cobrand = "$FindBin::Bin/../templates/emails/$data{cobrand}/$data{template}";
- $template = $template_cobrand if -e $template_cobrand;
- }
- $template = File::Slurp::read_file($template);
- my $sender = Cobrand::contact_email($data{cobrand});
- my $sender_name = Cobrand::contact_name($data{cobrand});
- (my $from = $sender) =~ s/team/fms-DO-NOT-REPLY/; # XXX
- my $email = mySociety::Email::construct_email({
- _template_ => _($template),
- _parameters_ => \%data,
- From => [ $from, _($sender_name) ],
- To => $data{alert_email},
- 'Message-ID' => sprintf('<alert-%s-%s@mysociety.org>', time(), unpack('h*', random_bytes(5, 1))),
- });
-
- my $result = mySociety::EmailUtil::send_email($email, $sender, $data{alert_email});
- if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
- dbh()->commit();
- } else {
- dbh()->rollback();
- throw FixMyStreet::Alert::Error("Failed to send alert $data{alert_id}!");
- }
-}
-
diff --git a/perllib/FixMyStreet/App/Controller/Alert.pm b/perllib/FixMyStreet/App/Controller/Alert.pm
index bea5345e3..b3067abc9 100644
--- a/perllib/FixMyStreet/App/Controller/Alert.pm
+++ b/perllib/FixMyStreet/App/Controller/Alert.pm
@@ -63,6 +63,9 @@ sub subscribe : Path('subscribe') : Args(0) {
elsif ( exists $c->req->params->{'rznvy'} ) {
$c->detach('subscribe_email');
}
+ elsif ( $c->req->params->{'id'} ) {
+ $c->go('updates');
+ }
# shouldn't get to here but if we have then do something sensible
$c->go('index');
@@ -140,7 +143,7 @@ sub subscribe_email : Private {
$c->forward('set_local_alert_options');
}
else {
- throw FixMyStreet::Alert::Error('Invalid type');
+ $c->detach( '/page_error_404_not_found', [ 'Invalid type' ] );
}
$c->forward('create_alert');
@@ -162,7 +165,7 @@ sub updates : Path('updates') : Args(0) {
=head2 confirm
-Confirm signup to an alert. Forwarded here from Tokens.
+Confirm signup to or unsubscription from an alert. Forwarded here from Tokens.
=cut
@@ -173,11 +176,9 @@ sub confirm : Private {
if ( $c->stash->{confirm_type} eq 'subscribe' ) {
$alert->confirm();
- $alert->update;
}
elsif ( $c->stash->{confirm_type} eq 'unsubscribe' ) {
- $alert->delete();
- $alert->update;
+ $alert->disable();
}
}
@@ -198,8 +199,9 @@ sub create_alert : Private {
unless ($alert) {
$options->{cobrand} = $c->cobrand->moniker();
$options->{cobrand_data} = $c->cobrand->extra_update_data();
+ $options->{lang} = $c->stash->{lang_code};
- if ( $c->user && $c->user->id == $c->stash->{alert_user}->id ) {
+ if ( $c->user && $c->stash->{alert_user}->in_storage && $c->user->id == $c->stash->{alert_user}->id ) {
$options->{confirmed} = 1;
}
@@ -258,7 +260,7 @@ sub set_local_alert_options : Private {
m{ \A local: ( [\+\-]? \d+ \.? \d* ) : ( [\+\-]? \d+ \.? \d* ) }xms )
{
$type = 'local_problems';
- push @params, $1, $2;
+ push @params, $2, $1; # Note alert parameters are lon,lat
}
my $options = {
diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm
index 671454272..9e0193d3f 100644
--- a/perllib/FixMyStreet/App/Controller/Report/New.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/New.pm
@@ -931,8 +931,10 @@ sub redirect_or_confirm_creation : Private {
my ( $self, $c ) = @_;
my $report = $c->stash->{report};
- # If confirmed send the user straigh there.
+ # If confirmed send the user straight there.
if ( $report->confirmed ) {
+ # Subscribe problem reporter to email updates
+ $c->forward( 'create_reporter_alert' );
my $report_uri = $c->uri_for( '/report', $report->id );
$c->res->redirect($report_uri);
$c->detach;
@@ -950,6 +952,20 @@ sub redirect_or_confirm_creation : Private {
$c->stash->{email_type} = 'problem';
}
+sub create_reporter_alert : Private {
+ my ( $self, $c ) = @_;
+
+ my $problem = $c->stash->{report};
+ my $alert = $c->model('DB::Alert')->find_or_create( {
+ user => $problem->user,
+ alert_type => 'new_updates',
+ parameter => $problem->id,
+ cobrand => $problem->cobrand,
+ cobrand_data => $problem->cobrand_data,
+ lang => $problem->lang,
+ } )->confirm;
+}
+
=head2 redirect_to_around
Redirect the user to '/around' passing along all the relevant parameters.
diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm
index eadf2beea..a1120470b 100644
--- a/perllib/FixMyStreet/App/Controller/Report/Update.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm
@@ -315,12 +315,15 @@ sub signup_for_alerts : Private {
my ( $self, $c ) = @_;
if ( $c->stash->{add_alert} ) {
+ my $update = $c->stash->{update};
my $alert = $c->model('DB::Alert')->find_or_create(
- user => $c->stash->{update}->user,
- alert_type => 'new_updates',
- parameter => $c->stash->{update}->problem_id,
- confirmed => 1,
- );
+ user => $update->user,
+ alert_type => 'new_updates',
+ parameter => $update->problem_id,
+ cobrand => $update->cobrand,
+ cobrand_data => $update->cobrand_data,
+ lang => $update->lang,
+ )->confirm();
$alert->update;
}
diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm
index 92dbced15..993cd752e 100644
--- a/perllib/FixMyStreet/App/Controller/Reports.pm
+++ b/perllib/FixMyStreet/App/Controller/Reports.pm
@@ -4,7 +4,6 @@ use namespace::autoclean;
use Problems;
use POSIX qw(strcoll);
-# use FixMyStreet::Alert;
use mySociety::MaPit;
use mySociety::VotingArea;
diff --git a/perllib/FixMyStreet/App/Controller/Tokens.pm b/perllib/FixMyStreet/App/Controller/Tokens.pm
index 792c875fd..92a44e756 100644
--- a/perllib/FixMyStreet/App/Controller/Tokens.pm
+++ b/perllib/FixMyStreet/App/Controller/Tokens.pm
@@ -4,8 +4,6 @@ use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller'; }
-use FixMyStreet::Alert;
-
=head1 NAME
FixMyStreet::App::Controller::Tokens - Handle auth tokens
@@ -57,15 +55,8 @@ sub confirm_problem : Path('/P') {
) if $problem->state eq 'unconfirmed';
# Subscribe problem reporter to email updates
- my $alert = $c->model('DB::Alert')->find_or_create(
- {
- user => $problem->user,
- alert_type => 'new_updates',
- cobrand => $problem->cobrand,
- cobrand_data => $problem->cobrand_data,
- parameter => $problem->id
- }
- )->confirm;
+ $c->stash->{report} = $c->stash->{problem};
+ $c->forward( '/report/new/create_reporter_alert' );
# log the problem creation user in to the site
$c->authenticate( { email => $problem->user->email }, 'no_password' );
diff --git a/perllib/FixMyStreet/DB/Result/Alert.pm b/perllib/FixMyStreet/DB/Result/Alert.pm
index b99c83a45..53cb96ff4 100644
--- a/perllib/FixMyStreet/DB/Result/Alert.pm
+++ b/perllib/FixMyStreet/DB/Result/Alert.pm
@@ -85,10 +85,18 @@ Sets the state of the alert to confirmed.
sub confirm {
my $self = shift;
- return if $self->confirmed and $self->confirmed == 1 and $self->whendisabled ne 'null';
-
$self->confirmed(1);
$self->whendisabled(undef);
+ $self->update;
+
+ return 1;
+}
+
+sub disable {
+ my $self = shift;
+
+ $self->whendisabled( \'ms_current_timestamp()' );
+ $self->update;
return 1;
}
diff --git a/perllib/FixMyStreet/DB/ResultSet/AlertType.pm b/perllib/FixMyStreet/DB/ResultSet/AlertType.pm
new file mode 100644
index 000000000..405210861
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/AlertType.pm
@@ -0,0 +1,204 @@
+package FixMyStreet::DB::ResultSet::AlertType;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+
+use File::Slurp;
+
+use mySociety::DBHandle qw(dbh);
+use mySociety::EmailUtil;
+use mySociety::Gaze;
+use mySociety::Locale;
+use mySociety::MaPit;
+
+# Child must have confirmed, id, email, state(!) columns
+# If parent/child, child table must also have name and text
+# and foreign key to parent must be PARENT_id
+sub email_alerts ($) {
+ my ( $rs ) = @_;
+
+ my $q = $rs->search( { ref => { -not_like => '%local_problems%' } } );
+ while (my $alert_type = $q->next) {
+ my $ref = $alert_type->ref;
+ my $head_table = $alert_type->head_table;
+ my $item_table = $alert_type->item_table;
+ my $query = 'select alert.id as alert_id, alert_user.email as alert_email, alert.lang as alert_lang, alert.cobrand as alert_cobrand,
+ alert.cobrand_data as alert_cobrand_data, alert.parameter as alert_parameter, alert.parameter2 as alert_parameter2, ';
+ if ($head_table) {
+ $query .= "
+ $item_table.id as item_id, $item_table.name as item_name, $item_table.text as item_text,
+ $head_table.*
+ from alert
+ inner join $item_table on alert.parameter::integer = $item_table.${head_table}_id
+ inner join $head_table on alert.parameter::integer = $head_table.id
+ inner join users as alert_user on alert.user_id = alert_user.id";
+ } else {
+ $query .= " $item_table.*,
+ $item_table.id as item_id
+ from alert
+ cross join $item_table
+ inner join users as alert_user on alert.user_id = alert_user.id";
+ }
+ $query .= "
+ where alert_type='$ref' and whendisabled is null and $item_table.confirmed >= whensubscribed
+ and $item_table.confirmed >= ms_current_timestamp() - '7 days'::interval
+ and (select whenqueued from alert_sent where alert_sent.alert_id = alert.id and alert_sent.parameter::integer = $item_table.id) is null
+ and $item_table.user_id <> alert.user_id
+ and " . $alert_type->item_where . "
+ and alert.confirmed = 1
+ order by alert.id, $item_table.confirmed";
+ # XXX Ugh - needs work
+ $query =~ s/\?/alert.parameter/ if ($query =~ /\?/);
+ $query =~ s/\?/alert.parameter2/ if ($query =~ /\?/);
+ $query = dbh()->prepare($query);
+ $query->execute();
+ my $last_alert_id;
+ my %data = ( template => $alert_type->template, data => '' );
+ while (my $row = $query->fetchrow_hashref) {
+
+ my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($row->{alert_cobrand})->new();
+
+ # Cobranded and non-cobranded messages can share a database. In this case, the conf file
+ # should specify a vhost to send the reports for each cobrand, so that they don't get sent
+ # more than once if there are multiple vhosts running off the same database. The email_host
+ # call checks if this is the host that sends mail for this cobrand.
+ next unless $cobrand->email_host;
+
+ FixMyStreet::App->model('DB::AlertSent')->create( {
+ alert_id => $row->{alert_id},
+ parameter => $row->{item_id},
+ } );
+ if ($last_alert_id && $last_alert_id != $row->{alert_id}) {
+ _send_aggregated_alert_email(%data);
+ %data = ( template => $alert_type->template, data => '' );
+ }
+
+ # create problem status message for the templates
+ $data{state_message} =
+ $row->{state} eq 'fixed'
+ ? _("This report is currently marked as fixed.")
+ : _("This report is currently marked as open.");
+
+ my $url = $cobrand->base_url_for_emails( $row->{alert_cobrand_data} );
+ if ($row->{item_text}) {
+ $data{problem_url} = $url . "/report/" . $row->{id};
+ $data{data} .= $row->{item_name} . ' : ' if $row->{item_name};
+ $data{data} .= $row->{item_text} . "\n\n------\n\n";
+ } else {
+ $data{data} .= $url . "/report/" . $row->{id} . " - $row->{title}\n\n";
+ }
+ if (!$data{alert_email}) {
+ %data = (%data, %$row);
+ if ($ref eq 'area_problems' || $ref eq 'council_problems' || $ref eq 'ward_problems') {
+ my $va_info = mySociety::MaPit::call('area', $row->{alert_parameter});
+ $data{area_name} = $va_info->{name};
+ }
+ if ($ref eq 'ward_problems') {
+ my $va_info = mySociety::MaPit::call('area', $row->{alert_parameter2});
+ $data{ward_name} = $va_info->{name};
+ }
+ }
+ $data{cobrand} = $row->{alert_cobrand};
+ $data{cobrand_data} = $row->{alert_cobrand_data};
+ $data{lang} = $row->{alert_lang};
+ $last_alert_id = $row->{alert_id};
+ }
+ if ($last_alert_id) {
+ _send_aggregated_alert_email(%data);
+ }
+ }
+
+ # Nearby done separately as the table contains the parameters
+ my $template = $rs->find( { ref => 'local_problems' } )->template;
+ my $query = FixMyStreet::App->model('DB::Alert')->search( {
+ alert_type => 'local_problems',
+ whendisabled => undef,
+ confirmed => 1
+ }, {
+ order_by => 'id'
+ } );
+ while (my $alert = $query->next) {
+ my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($alert->cobrand)->new();
+ next unless $cobrand->email_host;
+
+ my $longitude = $alert->parameter;
+ my $latitude = $alert->parameter2;
+ my $url = $cobrand->base_url_for_emails( $alert->cobrand_data );
+ my ($site_restriction, $site_id) = $cobrand->site_restriction( $alert->cobrand_data );
+ my $d = mySociety::Gaze::get_radius_containing_population($latitude, $longitude, 200000);
+ # Convert integer to GB locale string (with a ".")
+ $d = mySociety::Locale::in_gb_locale {
+ sprintf("%f", int($d*10+0.5)/10);
+ };
+ my %data = ( template => $template, data => '', alert_id => $alert->id, alert_email => $alert->user->email, lang => $alert->lang, cobrand => $alert->cobrand, cobrand_data => $alert->cobrand_data );
+ my $q = "select problem.id, problem.title from problem_find_nearby(?, ?, ?) as nearby, problem, users
+ where nearby.problem_id = problem.id
+ and problem.user_id = users.id
+ and problem.state in ('confirmed', 'fixed')
+ and problem.confirmed >= ? and problem.confirmed >= ms_current_timestamp() - '7 days'::interval
+ and (select whenqueued from alert_sent where alert_sent.alert_id = ? and alert_sent.parameter::integer = problem.id) is null
+ and users.email <> ?
+ $site_restriction
+ order by confirmed desc";
+ $q = dbh()->prepare($q);
+ $q->execute($latitude, $longitude, $d, $alert->whensubscribed, $alert->id, $alert->user->email);
+ while (my $row = $q->fetchrow_hashref) {
+ FixMyStreet::App->model('DB::AlertSent')->create( {
+ alert_id => $alert->id,
+ parameter => $row->{id},
+ } );
+ $data{data} .= $url . "/report/" . $row->{id} . " - $row->{title}\n\n";
+ }
+ _send_aggregated_alert_email(%data) if $data{data};
+ }
+}
+
+sub _send_aggregated_alert_email(%) {
+ my %data = @_;
+
+ my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($data{cobrand})->new();
+
+ $cobrand->set_lang_and_domain( $data{lang}, 1 );
+
+ my $token = FixMyStreet::App->model("DB::Token")->new_result( {
+ scope => 'alert',
+ data => {
+ id => $data{alert_id},
+ type => 'unsubscribe',
+ email => $data{alert_email},
+ }
+ } );
+ $data{unsubscribe_url} = $cobrand->base_url_for_emails( $data{cobrand_data} ) . '/A/' . $token->token;
+
+ my $template = FixMyStreet->path_to(
+ "templates", "email", $cobrand->moniker, "$data{template}.txt"
+ )->stringify;
+ my $template_cobrand = FixMyStreet->path_to(
+ "templates", "email", $cobrand->moniker, $data{lang}, "$data{template}.txt"
+ )->stringify;
+ $template = $template_cobrand if -e $template_cobrand;
+ $template = File::Slurp::read_file($template);
+
+ my $sender = $cobrand->contact_email;
+ (my $from = $sender) =~ s/team/fms-DO-NOT-REPLY/; # XXX
+ my $result = FixMyStreet::App->send_email_cron(
+ {
+ _template_ => $template,
+ _parameters_ => \%data,
+ From => [ $from, _($cobrand->contact_name) ],
+ To => $data{alert_email},
+ },
+ $sender,
+ [ $data{alert_email} ],
+ 0,
+ );
+
+ if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
+ $token->insert();
+ } else {
+ print "Failed to send alert $data{alert_id}!";
+ }
+}
+
+1;
diff --git a/perllib/FixMyStreet/TestMech.pm b/perllib/FixMyStreet/TestMech.pm
index c9f1d7dde..3dd4e84e9 100644
--- a/perllib/FixMyStreet/TestMech.pm
+++ b/perllib/FixMyStreet/TestMech.pm
@@ -151,8 +151,11 @@ sub delete_user {
ok( $_->delete, "delete questionnaire " . $_->id ) for $p->questionnaires;
ok( $p->delete, "delete problem " . $p->title );
}
+ for my $a ( $user->alerts ) {
+ $a->alert_sents->delete;
+ ok( $a->delete, "delete alert " . $a->alert_type );
+ }
ok( $_->delete, "delete comment " . $_->text ) for $user->comments;
- ok( $_->delete, "delete alert " . $_->alert_type ) for $user->alerts;
ok $user->delete, "delete test user " . $user->email;
return 1;
diff --git a/t/app/controller/alert_new.t b/t/app/controller/alert_new.t
index 91e86bd46..ff1acd480 100644
--- a/t/app/controller/alert_new.t
+++ b/t/app/controller/alert_new.t
@@ -43,8 +43,8 @@ foreach my $test (
email_text => 'confirm the alert',
uri =>
'/alert/subscribe?type=local&rznvy=test@example.com&feed=local:10.2:20.1',
- param1 => 10.2,
- param2 => 20.1,
+ param1 => 20.1,
+ param2 => 10.2,
},
{
email => 'test@example.com',
@@ -66,7 +66,6 @@ foreach my $test (
->find( { email => $test->{email} } );
# we don't want an alert
- my $alert;
if ($user) {
$mech->delete_user($user);
}
@@ -80,7 +79,7 @@ foreach my $test (
ok $user, 'user created for alert';
- $alert = FixMyStreet::App->model('DB::Alert')->find(
+ my $alert = FixMyStreet::App->model('DB::Alert')->find(
{
user => $user,
alert_type => $type,
@@ -129,7 +128,7 @@ foreach my $test (
);
ok $token, 'new token found in database';
- ok $token->data->{id} == $existing_id, 'subscribed to exsiting alert';
+ ok $token->data->{id} == $existing_id, 'subscribed to existing alert';
$mech->get_ok("/A/$url_token");
$mech->content_contains('successfully confirmed');
@@ -138,6 +137,7 @@ foreach my $test (
FixMyStreet::App->model('DB::Alert')->find( { id => $existing_id, } );
ok $alert->confirmed, 'alert set to confirmed';
+ $mech->delete_user($user);
};
}
@@ -162,18 +162,14 @@ foreach my $test (
FixMyStreet::App->model('DB::User')
->find_or_create( { email => $test->{email} } );
- my $alert;
- if ($user) {
- $alert = FixMyStreet::App->model('DB::Alert')->find(
- {
- user => $user,
- alert_type => $type
- }
- );
-
- # clear existing data so we can be sure we're creating it
- ok $alert->delete() if $alert;
- }
+ my $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $user,
+ alert_type => $type
+ }
+ );
+ # clear existing data so we can be sure we're creating it
+ ok $alert->delete() if $alert;
$mech->get_ok( $test->{uri} );
@@ -190,6 +186,7 @@ foreach my $test (
$mech->content_contains( 'Now check your email' );
ok $alert, 'New alert created with existing user';
+ $mech->delete_user($user);
};
}
@@ -199,8 +196,6 @@ foreach my $test (
user => 'test-login@example.com',
email => 'test-login@example.com',
type => 'council',
- content => 'your alert will not be activated',
- email_text => 'confirm the alert',
param1 => 2651,
param2 => 2651,
confirmed => 1,
@@ -210,8 +205,6 @@ foreach my $test (
user => 'loggedin@example.com',
email => 'test-login@example.com',
type => 'council',
- content => 'your alert will not be activated',
- email_text => 'confirm the alert',
param1 => 2651,
param2 => 2651,
confirmed => 0,
@@ -350,6 +343,112 @@ for my $test (
ok !$alert->confirmed, 'alert not set to confirmed';
$abuse->delete;
+ $mech->delete_user($user);
};
}
+
+subtest "Test normal alert signups and that alerts are sent" => sub {
+ my $user1 = FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'reporter@example.com', name => 'Reporter User' } );
+ ok $user1, "created test user";
+ $user1->alerts->delete;
+
+ my $user2 = FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'alerts@example.com', name => 'Alert User' } );
+ ok $user2, "created test user";
+ $user2->alerts->delete;
+
+ for my $alert (
+ { feed => 'local:55.951963:-3.189944', email_confirm => 1 },
+ { feed => 'council:2651:City_of_Edinburgh', },
+ ) {
+ $mech->get_ok( '/alert' );
+ $mech->submit_form_ok( { with_fields => { pc => 'EH11BB' } } );
+ $mech->submit_form_ok( {
+ button => 'alert',
+ with_fields => {
+ rznvy => $user2->email,
+ feed => $alert->{feed},
+ }
+ } );
+ if ( $alert->{email_confirm} ) {
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+ my ( $url, $url_token ) = $email->body =~ m{http://\S+(/A/(\S+))};
+ my $token = FixMyStreet::App->model('DB::Token')->find( { token => $url_token, scope => 'alert' } );
+ $mech->get_ok( $url );
+ $mech->content_contains('successfully confirmed');
+ } else {
+ $mech->content_contains('successfully created');
+ }
+ }
+
+ my $dt = DateTime->now()->add( days => 2);
+
+ my $report_time = '2011-03-01 12:00:00';
+ my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( {
+ postcode => 'EH1 1BB',
+ council => '2651',
+ areas => ',11808,135007,14419,134935,2651,20728,',
+ category => 'Street lighting',
+ title => 'Testing',
+ detail => 'Testing Detail',
+ used_map => 1,
+ name => $user1->name,
+ anonymous => 0,
+ state => 'confirmed',
+ confirmed => $dt,
+ lastupdate => $dt,
+ whensent => $dt->clone->add( minutes => 5 ),
+ lang => 'en-gb',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ send_questionnaire => 1,
+ latitude => '55.951963',
+ longitude => '-3.189944',
+ user_id => $user1->id,
+ } );
+ my $report_id = $report->id;
+ ok $report, "created test report - $report_id";
+
+ my $alert = FixMyStreet::App->model('DB::Alert')->create( {
+ parameter => $report_id,
+ alert_type => 'new_updates',
+ user => $user1,
+ } )->confirm;
+ ok $alert, 'created alert for reporter';
+
+ my $update = FixMyStreet::App->model('DB::Comment')->create( {
+ problem_id => $report_id,
+ user_id => $user2->id,
+ name => 'Other User',
+ mark_fixed => 'false',
+ text => 'This is some update text',
+ state => 'confirmed',
+ confirmed => $dt->clone->add( hours => 7 ),
+ anonymous => 'f',
+ } );
+ my $update_id = $update->id;
+ ok $update, "created test update - $update_id";
+
+ FixMyStreet::App->model('DB::AlertType')->email_alerts();
+ $mech->email_count_is(3);
+ my @emails = $mech->get_email;
+ my $count;
+ for (@emails) {
+ $count++ if $_->body =~ /The following updates have been left on this problem:/;
+ $count++ if $_->body =~ /The following new problems have been reported to City of\s*Edinburgh Council:/;
+ $count++ if $_->body =~ /The following nearby problems have been added:/;
+ }
+ is $count, 3, 'Three emails with the right things in them';
+
+ my ( $url, $url_token ) = $emails[0]->body =~ m{http://\S+(/A/(\S+))};
+ $mech->get_ok( $url );
+ $mech->content_contains('successfully deleted');
+
+ $mech->delete_user($user1);
+ $mech->delete_user($user2);
+};
+
done_testing();
diff --git a/t/app/controller/report_new.t b/t/app/controller/report_new.t
index 760dfacc6..01c29ecf4 100644
--- a/t/app/controller/report_new.t
+++ b/t/app/controller/report_new.t
@@ -343,10 +343,17 @@ subtest "test report creation for a user who does not have an account" => sub {
$mech->get_ok($url);
$report->discard_changes;
is $report->state, 'confirmed', "Report is now confirmed";
- is $report->state, 'confirmed', "report is now confirmed";
$mech->get_ok( '/report/' . $report->id );
+ # check that the reporter has an alert
+ my $alert = FixMyStreet::App->model('DB::Alert')->find( {
+ user => $report->user,
+ alert_type => 'new_updates',
+ parameter => $report->id,
+ } );
+ ok $alert, "created new alert";
+
# user is created and logged in
$mech->logged_in_ok;
@@ -440,6 +447,14 @@ foreach my $test (
is $report->state, 'confirmed', "report is now confirmed";
$mech->get_ok( '/report/' . $report->id );
+ # check that the reporter has an alert
+ my $alert = FixMyStreet::App->model('DB::Alert')->find( {
+ user => $report->user,
+ alert_type => 'new_updates',
+ parameter => $report->id,
+ } );
+ ok $alert, "created new alert";
+
# user is still logged in
$mech->logged_in_ok;
diff --git a/templates/web/default/alert/list.html b/templates/web/default/alert/list.html
index a8f962c5d..bb515232a 100644
--- a/templates/web/default/alert/list.html
+++ b/templates/web/default/alert/list.html
@@ -35,13 +35,13 @@
[% IF pretty_pc %]
[% tprintf( loc('Here are the types of local problem alerts for &lsquo;%s&rsquo;.'), pretty_pc ) %]
[% END %]
- [% loc('Select which type of alert you\'d like and click the button for an RSS feed, or enter your email address to subscribe to an email alert') %]
+ [% loc('Select which type of alert you\'d like and click the button for an RSS feed, or enter your email address to subscribe to an email alert.') %]
</p>
[% INCLUDE 'errors.html' %]
<p>
- [% loc('The simplest alert is our geographic one') %]
+ [% loc('The simplest alert is our geographic one:') %]
</p>
<p id="rss_local">
diff --git a/templates/web/default/report/display.html b/templates/web/default/report/display.html
index 4fa58c1db..b877874e5 100644
--- a/templates/web/default/report/display.html
+++ b/templates/web/default/report/display.html
@@ -39,7 +39,7 @@
</p>
<div id="alert_links">
- <a rel="nofollow" id="email_alert" href="[% c.uri_for( '/alert', { type => 'updates', id => problem.id } ) %]">[% loc('Email me updates' ) %]</a>
+ <a rel="nofollow" id="email_alert" href="[% c.uri_for( '/alert/subscribe', { id => problem.id } ) %]">[% loc('Email me updates' ) %]</a>
<form action="[% c.uri_for( '/alert/subscribe' ) %]" method="post" id="email_alert_box">
<p>[% loc('Receive email when updates are left on this problem.' ) %]</p>