diff options
Diffstat (limited to 'perllib')
39 files changed, 855 insertions, 266 deletions
diff --git a/perllib/CrossSell.pm b/perllib/CrossSell.pm index f9cde6936..231bb9361 100644 --- a/perllib/CrossSell.pm +++ b/perllib/CrossSell.pm @@ -142,6 +142,15 @@ details. You can unsubscribe at any time.</p> EOF } +sub display_survey_link { + return <<EOF; +<h1 style="padding-top:0.5em">User Survey</h1> +<p> +We're running a survey to help us understand who uses our sites. If you have 10-15 minutes to spare then we'd be grateful if you could <a href="http://questions.mysociety.org/S/fms/w/" target="_blank">take part</a>. +</p> +EOF +} + # Not currently used, needs more explanation and testing; perhaps in future. sub display_gny_groups { my ($lon, $lat) = @_; @@ -201,6 +210,11 @@ sub display_advert ($$;$%) { #EOF #unless (defined $data{done_tms} && $data{done_tms}==1) { + $c->stash->{scratch} = 'advert=survey'; + return '<div style="margin: 0 5em; border-top: dotted 1px #666666;">' + . display_survey_link() + . '</div>'; + $c->stash->{scratch} = 'advert=news'; my $auth_signature = ''; unless (defined $data{emailunvalidated} && $data{emailunvalidated}==1) { diff --git a/perllib/FixMyStreet.pm b/perllib/FixMyStreet.pm index be488a796..cc5286bbb 100644 --- a/perllib/FixMyStreet.pm +++ b/perllib/FixMyStreet.pm @@ -7,6 +7,7 @@ use Path::Class; my $ROOT_DIR = file(__FILE__)->parent->parent->absolute->resolve; use Readonly; +use Sub::Override; use mySociety::Config; use mySociety::DBHandle; @@ -85,6 +86,42 @@ sub config { return exists $CONFIG{$key} ? $CONFIG{$key} : undef; } +sub override_config($&) { + my $config = shift; + my $code = \&{shift @_}; + + mySociety::MaPit::configure($config->{MAPIT_URL}) if $config->{MAPIT_URL}; + + # For historical reasons, we have two ways of asking for config variables. + # Override them both, I'm sure we'll find time to get rid of one eventually. + my $override_guard1 = Sub::Override->new( + "FixMyStreet::config", + sub { + my ($class, $key) = @_; + return { %CONFIG, %$config } unless $key; + return $config->{$key} if exists $config->{$key}; + my $orig_config = mySociety::Config::load_default(); + return $orig_config->{$key} if exists $orig_config->{$key}; + } + ); + my $override_guard2 = Sub::Override->new( + "mySociety::Config::get", + sub ($;$) { + my ($key, $default) = @_; + return $config->{$key} if exists $config->{$key}; + my $orig_config = mySociety::Config::load_default(); + return $orig_config->{$key} if exists $orig_config->{$key}; + return $default if @_ == 2; + } + ); + + $code->(); + + $override_guard1->restore(); + $override_guard2->restore(); + mySociety::MaPit::configure() if $config->{MAPIT_URL};; +} + =head2 dbic_connect_info $connect_info = FixMyStreet->dbic_connect_info(); diff --git a/perllib/FixMyStreet/App.pm b/perllib/FixMyStreet/App.pm index cf766348f..4f70d2c68 100644 --- a/perllib/FixMyStreet/App.pm +++ b/perllib/FixMyStreet/App.pm @@ -208,6 +208,14 @@ sub setup_request { DateTime->DefaultLocale( 'en_US' ); } + if (FixMyStreet->test_mode) { + # Is there a better way of altering $c->config that may have + # override_config involved? + $c->setup_finished(0); + $c->config( %{ FixMyStreet->config() } ); + $c->setup_finished(1); + } + return $c; } diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index 133c83024..9c0018f38 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -9,6 +9,8 @@ use POSIX qw(strftime strcoll); use Digest::SHA qw(sha1_hex); use mySociety::EmailUtil qw(is_valid_email); use if !$ENV{TRAVIS}, 'Image::Magick'; +use DateTime::Format::Strptime; + use FixMyStreet::SendReport; @@ -130,9 +132,21 @@ sub index : Path : Args(0) { $c->stash->{categories} = $c->cobrand->problems->categories_summary(); + $c->stash->{total_bodies} = $c->model('DB::Body')->count(); + return 1; } +sub config_page : Path( 'config' ) : Args(0) { + my ($self, $c) = @_; + my $dir = $c->stash->{additional_template_paths}->[0]; + my $git_version = `cd $dir && git describe --tags`; + chomp $git_version; + $c->stash( + git_version => $git_version, + ); +} + sub timeline : Path( 'timeline' ) : Args(0) { my ($self, $c) = @_; @@ -411,7 +425,7 @@ sub update_contacts : Private { sub body_params : Private { my ( $self, $c ) = @_; - my @fields = qw/name endpoint jurisdiction api_key send_method send_comments suppress_alerts send_extended_statuses comment_user_id can_be_devolved parent/; + my @fields = qw/name endpoint jurisdiction api_key send_method send_comments suppress_alerts send_extended_statuses comment_user_id can_be_devolved parent deleted/; my %defaults = map { $_ => '' } @fields; %defaults = ( %defaults, send_comments => 0, @@ -420,6 +434,7 @@ sub body_params : Private { send_extended_statuses => 0, can_be_devolved => 0, parent => undef, + deleted => 0, ); my %params = map { $_ => $c->req->param($_) || $defaults{$_} } @fields; return \%params; @@ -430,6 +445,7 @@ sub display_contacts : Private { my $contacts = $c->stash->{body}->contacts->search(undef, { order_by => [ 'category' ] } ); $c->stash->{contacts} = $contacts; + $c->stash->{live_contacts} = $contacts->search({ deleted => 0 }); if ( $c->req->param('text') && $c->req->param('text') == 1 ) { $c->stash->{template} = 'admin/council_contacts.txt'; @@ -546,10 +562,15 @@ sub reports : Path('reports') { $query->{'-or'} = [ 'me.areas' => { like => "%,$1,%" } ]; + } elsif ($search =~ /^ref:(\d+)$/) { + $query->{'-or'} = [ + 'me.external_id' => { like => "%$1%" } + ]; } else { $query->{'-or'} = [ 'me.id' => $search_n, 'user.email' => { ilike => $like_search }, + 'me.external_id' => { ilike => $like_search }, 'me.name' => { ilike => $like_search }, 'me.title' => { ilike => $like_search }, detail => { ilike => $like_search }, @@ -749,11 +770,15 @@ sub report_edit : Path('report_edit') : Args(1) { $problem->user( $user ); } + # Deal with photos if ( $c->req->param('remove_photo') ) { $problem->photo(undef); } + if ( $new_state eq 'hidden' ) { + unlink glob FixMyStreet->path_to( 'web', 'photo', $problem->id . '.*' ); + } - if ( $new_state eq 'confirmed' and $old_state eq 'unconfirmed' ) { + if ( $problem->is_visible() and $old_state eq 'unconfirmed' ) { $problem->confirmed( \'ms_current_timestamp()' ); } @@ -894,7 +919,7 @@ sub update_edit : Path('update_edit') : Args(1) { $update->name( $c->req->param('name') || '' ); $update->text( $c->req->param('text') ); $update->anonymous( $c->req->param('anonymous') ); - $update->state( $c->req->param('state') ); + $update->state( $new_state ); if ( $c->req->param('email') ne $update->user->email ) { my $user = @@ -914,6 +939,10 @@ sub update_edit : Path('update_edit') : Args(1) { } } + if ( $new_state eq 'hidden' ) { + unlink glob FixMyStreet->path_to( 'web', 'photo', 'c', $update->id . '.*' ); + } + $update->update; $c->stash->{status_message} = '<p><em>' . _('Updated!') . '</em></p>'; @@ -954,6 +983,11 @@ sub user_add : Path('user_edit') : Args(0) { $c->forward('check_token'); + if ( $c->cobrand->moniker eq 'zurich' and $c->req->param('email') eq '' ) { + $c->stash->{field_errors}->{email} = _('Please enter a valid email'); + return 1; + } + return unless $c->req->param('name') && $c->req->param('email'); my $user = $c->model('DB::User')->find_or_create( { @@ -1001,6 +1035,11 @@ sub user_edit : Path('user_edit') : Args(1) { $user->email( $c->req->param('email') ); $user->from_body( $c->req->param('body') || undef ); $user->flagged( $c->req->param('flagged') || 0 ); + + if ( $c->cobrand->moniker eq 'zurich' and $user->email eq '' ) { + $c->stash->{field_errors}->{email} = _('Please enter a valid email'); + return 1; + } $user->update; if ($edited) { @@ -1024,8 +1063,20 @@ sub flagged : Path('flagged') : Args(0) { $c->stash->{problems} = [ $problems->all ]; my $users = $c->model('DB::User')->search( { flagged => 1 } ); + my @users = $users->all; + my %email2user = map { $_->email => $_ } @users; + $c->stash->{users} = [ @users ]; - $c->stash->{users} = $users; + my @abuser_emails = $c->model('DB::Abuse')->all(); + + foreach my $email (@abuser_emails) { + # Slight abuse of the boolean flagged value + if ($email2user{$email->email}) { + $email2user{$email->email}->flagged( 2 ); + } else { + push @{$c->stash->{users}}, { email => $email->email, flagged => 2 }; + } + } return 1; } @@ -1042,26 +1093,15 @@ sub stats : Path('stats') : Args(0) { if ( $c->req->param('getcounts') ) { my ( $start_date, $end_date, @errors ); + my $parser = DateTime::Format::Strptime->new( pattern => '%d/%m/%Y' ); - eval { - $start_date = DateTime->new( - year => $c->req->param('start_date_year'), - month => $c->req->param('start_date_month'), - day => $c->req->param('start_date_day'), - ); - }; + $start_date = $parser-> parse_datetime ( $c->req->param('start_date') ); - push @errors, _('Invalid start date') if $@; + push @errors, _('Invalid start date') unless defined $start_date; - eval { - $end_date = DateTime->new( - year => $c->req->param('end_date_year'), - month => $c->req->param('end_date_month'), - day => $c->req->param('end_date_day'), - ); - }; + $end_date = $parser-> parse_datetime ( $c->req->param('end_date') ) ; - push @errors, _('Invalid end date') if $@; + push @errors, _('Invalid end date') unless defined $end_date; $c->stash->{errors} = \@errors; $c->stash->{start_date} = $start_date; @@ -1148,6 +1188,7 @@ sub set_allowed_pages : Private { 'users' => [_('Users'), 5], 'flagged' => [_('Flagged'), 6], 'stats' => [_('Stats'), 6], + 'config' => [ undef, undef ], 'user_edit' => [undef, undef], 'body' => [undef, undef], 'body_edit' => [undef, undef], diff --git a/perllib/FixMyStreet/App/Controller/Contact.pm b/perllib/FixMyStreet/App/Controller/Contact.pm index 3fcb449d2..7ba18ed2d 100644 --- a/perllib/FixMyStreet/App/Controller/Contact.pm +++ b/perllib/FixMyStreet/App/Controller/Contact.pm @@ -149,7 +149,7 @@ sub prepare_params_for_email : Private { my $problem_url = $base_url . '/report/' . $c->stash->{update}->problem_id . '#update_' . $c->stash->{update}->id; - my $admin_url = " - $admin_url" . 'update_edit/' . $c->stash->{update}->id + my $admin_url = " - $admin_url" . '/update_edit/' . $c->stash->{update}->id if $admin_url; $c->stash->{message} .= sprintf( " \n\n[ Complaint about update %d on report %d - %s%s ]", @@ -161,7 +161,7 @@ sub prepare_params_for_email : Private { elsif ( $c->stash->{problem} ) { my $problem_url = $base_url . '/report/' . $c->stash->{problem}->id; - $admin_url = " - $admin_url" . 'report_edit/' . $c->stash->{problem}->id + $admin_url = " - $admin_url" . '/report_edit/' . $c->stash->{problem}->id if $admin_url; $c->stash->{message} .= sprintf( " \n\n[ Complaint about report %d - %s%s ]", diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index 028b9aadd..b47a1f54b 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -121,16 +121,22 @@ sub index : Path : Args(0) { if $c->stash->{category}; $c->stash->{where} = \%where; my $prob_where = { %where }; - $prob_where->{state} = $prob_where->{'problem.state'}; + $prob_where->{'me.state'} = $prob_where->{'problem.state'}; delete $prob_where->{'problem.state'}; $c->stash->{prob_where} = $prob_where; + my $dtf = $c->model('DB')->storage->datetime_parser; + my %counts; my $t = DateTime->today; - $counts{wtd} = $c->forward( 'updates_search', [ $t->subtract( days => $t->dow - 1 ) ] ); - $counts{week} = $c->forward( 'updates_search', [ DateTime->now->subtract( weeks => 1 ) ] ); - $counts{weeks} = $c->forward( 'updates_search', [ DateTime->now->subtract( weeks => 4 ) ] ); - $counts{ytd} = $c->forward( 'updates_search', [ DateTime->today->set( day => 1, month => 1 ) ] ); + $counts{wtd} = $c->forward( 'updates_search', + [ $dtf->format_datetime( $t->subtract( days => $t->dow - 1 ) ) ] ); + $counts{week} = $c->forward( 'updates_search', + [ $dtf->format_datetime( DateTime->now->subtract( weeks => 1 ) ) ] ); + $counts{weeks} = $c->forward( 'updates_search', + [ $dtf->format_datetime( DateTime->now->subtract( weeks => 4 ) ) ] ); + $counts{ytd} = $c->forward( 'updates_search', + [ $dtf->format_datetime( DateTime->today->set( day => 1, month => 1 ) ) ] ); $c->stash->{problems} = \%counts; @@ -138,17 +144,19 @@ sub index : Path : Args(0) { $c->stash->{q_state} = $c->req->param('state') || ''; if ( $c->stash->{q_state} eq 'fixed' ) { - $prob_where->{state} = [ FixMyStreet::DB::Result::Problem->fixed_states() ]; + $prob_where->{'me.state'} = [ FixMyStreet::DB::Result::Problem->fixed_states() ]; } elsif ( $c->stash->{q_state} ) { - $prob_where->{state} = $c->stash->{q_state}; - $prob_where->{state} = { IN => [ 'planned', 'action scheduled' ] } - if $prob_where->{state} eq 'action scheduled'; + $prob_where->{'me.state'} = $c->stash->{q_state}; + $prob_where->{'me.state'} = { IN => [ 'planned', 'action scheduled' ] } + if $prob_where->{'me.state'} eq 'action scheduled'; } my $params = { %$prob_where, - 'me.confirmed' => { '>=', DateTime->now->subtract( days => 30 ) }, + 'me.confirmed' => { '>=', $dtf->format_datetime( DateTime->now->subtract( days => 30 ) ) }, }; - my @problems = $c->cobrand->problems->search( $params )->all; + my $problems_rs = $c->cobrand->problems->search( $params ); + my @problems = $problems_rs->all; + my %problems; foreach (@problems) { if ($_->confirmed >= DateTime->now->subtract(days => 7)) { @@ -160,6 +168,102 @@ sub index : Path : Args(0) { } } $c->stash->{lists} = \%problems; + + if ( $c->req->params->{export} ) { + $self->export_as_csv($c, $problems_rs, $body); + } +} + +sub export_as_csv { + my ($self, $c, $problems_rs, $body) = @_; + require Text::CSV; + my $problems = $problems_rs->search( + {}, { prefetch => 'comments' }); + + my $filename = do { + my %where = ( + body => $body->id, + category => $c->stash->{category}, + state => $c->stash->{q_state}, + ward => $c->stash->{ward}, + ); + join '-', + $c->req->uri->host, + map { + my $value = $where{$_}; + (defined $value and length $value) ? ($_, $value) : () + } sort keys %where }; + + my $csv = Text::CSV->new(); + $csv->combine( + 'Report ID', + 'Title', + 'Detail', + 'User Name', + 'Category', + 'Created', + 'Confirmed', + 'Acknowledged', + 'Fixed', + 'Closed', + 'Status', + 'Latitude', 'Longitude', + 'Nearest Postcode', + 'Report URL', + ); + my @body = ($csv->string); + + my $fixed_states = FixMyStreet::DB::Result::Problem->fixed_states; + my $closed_states = FixMyStreet::DB::Result::Problem->closed_states; + + while ( my $report = $problems->next ) { + my $external_body; + my $body_name = ""; + if ( $external_body = $report->body($c) ) { + # seems to be a zurich specific thing + $body_name = $external_body->name if ref $external_body; + } + my $hashref = $report->as_hashref($c); + + $hashref->{user_name_display} = $report->anonymous? + '(anonymous)' : $report->user->name; + + for my $comment ($report->comments) { + my $problem_state = $comment->problem_state or next; + next if $problem_state eq 'confirmed'; + $hashref->{acknowledged_pp} //= $c->cobrand->prettify_dt( $comment->created ); + $hashref->{fixed_pp} //= $fixed_states->{ $problem_state } ? + $c->cobrand->prettify_dt( $comment->created ): undef; + if ($closed_states->{ $problem_state }) { + $hashref->{closed_pp} = $c->cobrand->prettify_dt( $comment->created ); + last; + } + } + + $csv->combine( + @{$hashref}{ + 'id', + 'title', + 'detail', + 'user_name_display', + 'category', + 'created_pp', + 'confirmed_pp', + 'acknowledged_pp', + 'fixed_pp', + 'closed_pp', + 'state', + 'latitude', 'longitude', + 'postcode', + }, + (join '', $c->cobrand->base_url_for_report($report), $report->url), + ); + + push @body, $csv->string; + } + $c->res->content_type('text/csv; charset=utf-8'); + $c->res->header('content-disposition' => "attachment; filename=${filename}.csv"); + $c->res->body( join "\n", @body ); } sub updates_search : Private { diff --git a/perllib/FixMyStreet/App/Controller/FakeMapit.pm b/perllib/FixMyStreet/App/Controller/FakeMapit.pm index bc46df712..253c75ba4 100755 --- a/perllib/FixMyStreet/App/Controller/FakeMapit.pm +++ b/perllib/FixMyStreet/App/Controller/FakeMapit.pm @@ -12,13 +12,13 @@ FixMyStreet::App::Controller::FakeMapit - Catalyst Controller A controller to fake mapit when we don't have it. If you set MAPIT_URL to .../fakemapit/ it should all just work, with a mapit that assumes the whole -world is one area, with ID 161 and name "Default Area". +world is one area, with ID 161 and name "Everywhere". =head1 METHODS =cut -my $area = { "name" => "Default Area", "type" => "ZZZ", "id" => 161 }; +my $area = { "name" => "Everywhere", "type" => "ZZZ", "id" => 161 }; sub output : Private { my ( $self, $c, $data ) = @_; diff --git a/perllib/FixMyStreet/App/Controller/My.pm b/perllib/FixMyStreet/App/Controller/My.pm index c00264315..bbef1f8d8 100644 --- a/perllib/FixMyStreet/App/Controller/My.pm +++ b/perllib/FixMyStreet/App/Controller/My.pm @@ -45,6 +45,7 @@ sub my : Path : Args(0) { } )->page( $p_page ); while ( my $problem = $rs->next ) { + $c->stash->{has_content}++; push @$pins, { latitude => $problem->latitude, longitude => $problem->longitude, @@ -64,7 +65,9 @@ sub my : Path : Args(0) { order_by => { -desc => 'confirmed' }, rows => 50 } )->page( $u_page ); + my @updates = $rs->all; + $c->stash->{has_content} += scalar @updates; $c->stash->{updates} = \@updates; $c->stash->{updates_pager} = $rs->pager; diff --git a/perllib/FixMyStreet/App/Controller/Photo.pm b/perllib/FixMyStreet/App/Controller/Photo.pm index 8b00d1533..09afabecf 100644 --- a/perllib/FixMyStreet/App/Controller/Photo.pm +++ b/perllib/FixMyStreet/App/Controller/Photo.pm @@ -30,17 +30,19 @@ Display a photo =cut -sub during :LocalRegex('^([0-9a-f]{40})\.temp\.jpeg$') { +sub during :LocalRegex('^([0-9a-f]{40})\.(temp|fulltemp)\.jpeg$') { my ( $self, $c ) = @_; - my ( $hash ) = @{ $c->req->captures }; + my ( $hash, $size ) = @{ $c->req->captures }; my $file = file( $c->config->{UPLOAD_DIR}, "$hash.jpeg" ); my $photo = $file->slurp; - if ( $c->cobrand->default_photo_resize ) { - $photo = _shrink( $photo, $c->cobrand->default_photo_resize ); - } else { - $photo = _shrink( $photo, '250x250' ); + if ( $size eq 'temp' ) { + if ( $c->cobrand->default_photo_resize ) { + $photo = _shrink( $photo, $c->cobrand->default_photo_resize ); + } else { + $photo = _shrink( $photo, '250x250' ); + } } $c->forward( 'output', [ $photo ] ); diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index 128ef2790..a419e9cc1 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -585,7 +585,7 @@ sub setup_categories_and_bodies : Private { my $first_area = ( values %$all_areas )[0]; my @bodies = $c->model('DB::Body')->search( - { 'body_areas.area_id' => [ keys %$all_areas ] }, + { 'body_areas.area_id' => [ keys %$all_areas ], deleted => 0 }, { join => 'body_areas' } )->all; my %bodies = map { $_->id => $_ } @bodies; @@ -666,6 +666,15 @@ sub setup_categories_and_bodies : Private { } } + if ($c->cobrand->can('hidden_categories')) { + my %hidden_categories = map { $_ => 1 } + $c->cobrand->hidden_categories; + + @category_options = grep { + !$hidden_categories{$_} + } @category_options; + } + # put results onto stash for display $c->stash->{bodies} = \%bodies; $c->stash->{all_body_names} = [ map { $_->name } values %bodies ]; @@ -957,6 +966,13 @@ sub check_for_errors : Private { delete $field_errors{name}; my $report = $c->stash->{report}; $report->title( Utils::cleanup_text( substr($report->detail, 0, 25) ) ); + + # We only want to validate the phone number web requests (where the + # service parameter is blank) because previous versions of the mobile + # apps don't validate the presence of a phone number. + if ( ! $c->req->param('phone') and ! $c->req->param('service') ) { + $field_errors{phone} = _("This information is required"); + } } # FIXME: need to check for required bromley fields here diff --git a/perllib/FixMyStreet/App/View/Web.pm b/perllib/FixMyStreet/App/View/Web.pm index 8d3775ddc..e4aafe951 100644 --- a/perllib/FixMyStreet/App/View/Web.pm +++ b/perllib/FixMyStreet/App/View/Web.pm @@ -19,7 +19,7 @@ __PACKAGE__->config( render_die => 1, expose_methods => [ 'loc', 'nget', 'tprintf', 'display_crosssell_advert', 'prettify_dt', - 'add_links', 'version', + 'add_links', 'version', 'decode', ], FILTERS => { escape_js => \&escape_js, @@ -181,5 +181,11 @@ sub version { return "$file?$version_hash{$file}"; } +sub decode { + my ( $self, $c, $text ) = @_; + utf8::decode($text) unless utf8::is_utf8($text); + return $text; +} + 1; diff --git a/perllib/FixMyStreet/Cobrand.pm b/perllib/FixMyStreet/Cobrand.pm index 881183463..ff7d7f943 100644 --- a/perllib/FixMyStreet/Cobrand.pm +++ b/perllib/FixMyStreet/Cobrand.pm @@ -8,6 +8,7 @@ use warnings; use FixMyStreet; use Carp; +use Moose; use Module::Pluggable sub_name => '_cobrands', @@ -38,7 +39,10 @@ Simply returns the config variable (so this function can be overridden in test s =cut sub _get_allowed_cobrands { - return FixMyStreet->config('ALLOWED_COBRANDS') || []; + my $allowed = FixMyStreet->config('ALLOWED_COBRANDS') || []; + # If the user has supplied a string, convert to an arrayref + $allowed = [ $allowed ] unless ref $allowed; + return $allowed; } =head2 available_cobrand_classes @@ -92,7 +96,14 @@ sub get_class_for_host { my $class = shift; my $host = shift; - foreach my $avail ( $class->available_cobrand_classes ) { + my @available = $class->available_cobrand_classes; + + # If only one entry, always use it + return class($available[0]) if 1 == @available; + + # If more than one entry, pick first whose regex (or + # name by default) matches hostname + foreach my $avail ( @available ) { return class($avail) if $host =~ /$avail->{host}/; } diff --git a/perllib/FixMyStreet/Cobrand/BellaVistaEnAccion.pm b/perllib/FixMyStreet/Cobrand/BellaVistaEnAccion.pm new file mode 100644 index 000000000..d96e7bc96 --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/BellaVistaEnAccion.pm @@ -0,0 +1,33 @@ +package FixMyStreet::Cobrand::BellaVistaEnAccion; +use base 'FixMyStreet::Cobrand::Default'; + +use strict; +use warnings; + +sub path_to_web_templates { + my $self = shift; + return [ + FixMyStreet->path_to( 'templates/web', $self->moniker )->stringify, + FixMyStreet->path_to( 'templates/web/fixmystreet' )->stringify + ]; +} + +sub country { + return 'CL'; +} + +sub example_places { + return ( 'Dominica, Recoleta', 'Pio Nono' ); +} + +sub languages { [ 'es-cl,Castellano,es_CL', 'en-gb,English,en_GB' ] } + +sub disambiguate_location { + return { + country => 'cl', + town => 'Santiago', + }; +} + +1; + diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index 38efb7a35..a39a98135 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -295,8 +295,11 @@ to null/0. sub uri { my ( $self, $uri ) = @_; - (my $map_class = $FixMyStreet::Map::map_class) =~ s/^FixMyStreet::Map:://; - return $uri unless $map_class =~ /OSM|FMS/; + { + no warnings 'once'; + (my $map_class = $FixMyStreet::Map::map_class) =~ s/^FixMyStreet::Map:://; + return $uri unless $map_class =~ /OSM|FMS/; + } $uri->query_param( zoom => 3 ) if $uri->query_param('lat') && !$uri->query_param('zoom'); diff --git a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm index 5e90db038..461018639 100644 --- a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm +++ b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm @@ -38,7 +38,7 @@ sub area_types { } sub admin_base_url { - return 'http://www.fiksgatami.no/admin/'; + return 'http://www.fiksgatami.no/admin'; } # If lat/lon are present in the URL, OpenLayers will use that to centre the map. diff --git a/perllib/FixMyStreet/Cobrand/FixMindelo.pm b/perllib/FixMyStreet/Cobrand/FixMindelo.pm index 59debf157..fd3a55c6c 100644 --- a/perllib/FixMyStreet/Cobrand/FixMindelo.pm +++ b/perllib/FixMyStreet/Cobrand/FixMindelo.pm @@ -4,6 +4,8 @@ use base 'FixMyStreet::Cobrand::Default'; use strict; use warnings; +sub site_title { return 'FixMindelo'; } + sub country { return 'CV'; } diff --git a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm index aa2be4a0f..c3a1f9d9d 100644 --- a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm +++ b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm @@ -9,7 +9,7 @@ sub restriction { } sub admin_base_url { - return 'https://secure.mysociety.org/admin/bci/'; + return 'https://secure.mysociety.org/admin/bci'; } sub title_list { diff --git a/perllib/FixMyStreet/Cobrand/Hart.pm b/perllib/FixMyStreet/Cobrand/Hart.pm new file mode 100644 index 000000000..cab834b69 --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/Hart.pm @@ -0,0 +1,72 @@ +package FixMyStreet::Cobrand::Hart; +use parent 'FixMyStreet::Cobrand::UKCouncils'; + +use strict; +use warnings; + +sub council_id { return 2333; } # http://mapit.mysociety.org/area/2333.html +sub council_area { return 'Hart'; } +sub council_name { return 'Hart Council'; } +sub council_url { return 'hart'; } +sub is_two_tier { return 1; } + +# Different to councils parent due to this being a two-tier council. If we get +# more, this can be genericised in the parent. +sub problems_clause { + return { bodies_str => { like => '%2333%' } }; +} + +sub path_to_web_templates { + my $self = shift; + return [ + FixMyStreet->path_to( 'templates/web', $self->moniker )->stringify, + FixMyStreet->path_to( 'templates/web/fixmystreet' )->stringify + ]; +} + +sub disambiguate_location { + my $self = shift; + my $string = shift; + + my $town = 'Hart, Hampshire'; + + return { + %{ $self->SUPER::disambiguate_location() }, + town => $town, + # these are taken from mapit http://mapit.mysociety.org/area/2333/geometry -- should be automated? + centre => '51.284839,-0.8974600', + span => '0.180311,0.239375', + bounds => [ 51.186005, -1.002295, 51.366316, -0.762920 ], + }; +} + +sub example_places { + return ( 'GU51 4JX', 'Primrose Drive' ); +} + +sub hidden_categories { + return ( + 'Graffiti on bridges/subways', + ); +} + +sub send_questionnaires { + return 0; +} + +sub ask_ever_reported { + return 0; +} + +sub contact_email { + my $self = shift; + return join( '@', 'info', 'hart.gov.uk' ); +} +sub contact_name { 'Hart District Council (do not reply)'; } + +sub default_map_zoom { 3 } + +sub reports_per_page { return 20; } + +1; + diff --git a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm index 173d5a09f..33611b219 100644 --- a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm +++ b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm @@ -53,6 +53,9 @@ sub example_places { # don't send questionnaires to people who used the OCC cobrand to report their problem sub send_questionnaires { return 0; } +# increase map zoom level so street names are visible +sub default_map_zoom { return 3; } + # let staff hide OCC reports sub users_can_hide { return 1; } diff --git a/perllib/FixMyStreet/Cobrand/UKCouncils.pm b/perllib/FixMyStreet/Cobrand/UKCouncils.pm index 5531ed048..ec3423f35 100644 --- a/perllib/FixMyStreet/Cobrand/UKCouncils.pm +++ b/perllib/FixMyStreet/Cobrand/UKCouncils.pm @@ -84,10 +84,7 @@ sub all_reports_single_body { sub reports_body_check { my ( $self, $c, $code ) = @_; - # First, the normal UK checks - $self->SUPER::find_closest( $c, $code ); - - # Now we want to make sure we're only on our page. + # We want to make sure we're only on our page. unless ( $self->council_name =~ /^\Q$code\E/ ) { $c->res->redirect( 'http://www.fixmystreet.com' . $c->req->uri->path_query, 301 ); $c->detach(); @@ -102,13 +99,19 @@ sub recent_photos { return $self->problems->recent_photos( $num, $lat, $lon, $dist ); } +# Returns true if the cobrand owns the problem. +sub owns_problem { + my ($self, $report) = @_; + my $bodies = $report->bodies; + my %areas = map { %{$_->areas} } values %$bodies; + return $areas{$self->council_id} ? 1 : undef; +} + # If we ever link to a county problem report, needs to be to main FixMyStreet sub base_url_for_report { my ( $self, $report ) = @_; if ( $self->is_two_tier ) { - my $bodies = $report->bodies; - my %areas = map { %{$_->areas} } values %$bodies; - if ( $areas{$self->council_id} ) { + if ( $self->owns_problem( $report ) ) { return $self->base_url; } else { return FixMyStreet->config('BASE_URL'); diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index ffdc1feab..0a05fe835 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -8,6 +8,48 @@ use RABX; use strict; use warnings; +=head1 NAME + +Zurich FixMyStreet cobrand + +=head1 DESCRIPTION + +This module provides the specific functionality for the Zurich FMS cobrand. + +=head1 DEVELOPMENT NOTES + +The admin for Zurich is different to the other cobrands. To access it you need +to be logged in as a user associated with an appropriate body. + +You can create the bodies needed to develop by running the 't/cobrand/zurich.t' +test script with the three C<$mech->delete...> lines at the end commented out. +This should leave you with the bodies and users correctly set up. + +The entries will be something like this (but with different ids). + + Bodies: + id | name | parent | endpoint + ----+---------------+--------+--------------------------- + 1 | Zurich | | + 2 | Division 1 | 1 | division@example.org + 3 | Subdivision A | 2 | subdivision@example.org + 4 | External Body | | external_body@example.org + + Users: + id | email | from_body + ----+------------------+----------- + 1 | super@example.org| 1 + 2 | dm1@example.org | 2 + 3 | sdm1@example.org | 3 + +The passwords for the users is 'secret'. + +Note: the password hashes are salted with the user's id so cannot be easily +changed. High ids have been used so that it should not conflict with anything +you already have, and the countres set so that they shouldn't in future. + +=cut + sub shorten_recency_if_new_greater_than_fixed { return 0; } @@ -53,6 +95,27 @@ sub prettify_dt { return Utils::prettify_dt( $dt, 'zurich' ); } +# problem already has a concept of is_fixed/is_closed, but Zurich has different +# workflow for this here. +# +# TODO: look at more elegant way of doing this, for example having ::DB::Problem +# consider cobrand specific state config? + +sub zurich_closed_states { + my $states = { + 'fixed - council' => 1, + 'closed' => 1, + 'hidden' => 1, + }; + + return wantarray ? keys %{ $states } : $states; +} + +sub problem_is_closed { + my ($self, $problem) = @_; + return exists $self->zurich_closed_states->{ $problem->state } ? 1 : 0; +} + sub problem_as_hashref { my $self = shift; my $problem = shift; @@ -174,19 +237,47 @@ sub overdue { my $w = $problem->created; return 0 unless $w; - if ( $problem->state eq 'unconfirmed' || $problem->state eq 'confirmed' ) { + # call with previous state + if ( $problem->state eq 'unconfirmed' ) { # One working day $w = add_days( $w, 1 ); return $w < DateTime->now() ? 1 : 0; - } elsif ( $problem->state eq 'in progress' || $problem->state eq 'planned' ) { + } elsif ( $problem->state eq 'confirmed' || $problem->state eq 'in progress' || $problem->state eq 'planned' ) { + # States which affect the subdiv_overdue statistic. TODO: this may no longer be required # Six working days from creation $w = add_days( $w, 6 ); return $w < DateTime->now() ? 1 : 0; + + # call with new state + } elsif ( $self->problem_is_closed($problem) ) { + # States which affect the closed_overdue statistic + # Five working days from moderation (so 6 from creation) + + $w = add_days( $w, 6 ); + return $w < DateTime->now() ? 1 : 0; } else { return 0; } } +sub get_or_check_overdue { + my ($self, $problem) = @_; + + # use the cached version is it exists (e.g. when called from template) + my $extra = $problem->extra; + if (exists $extra->{closed_overdue} and defined $extra->{closed_overdue}) { + return $extra->{closed_overdue} + } + return $self->overdue($problem); +} + +sub set_problem_state { + my ($self, $c, $problem, $new_state) = @_; + return if $new_state eq $problem->state; + $problem->state( $new_state ); + $c->forward( 'log_edit', [ $problem->id, 'problem', "state change to $new_state" ] ); +} + sub email_indent { ''; } # Specific administrative displays @@ -356,52 +447,87 @@ sub admin_report_edit { } - # Problem updates upon submission + # If super or sdm check that the token is correct before proceeding if ( ($type eq 'super' || $type eq 'dm') && $c->req->param('submit') ) { $c->forward('check_token'); + } + # All types of users can add internal notes + if ( ($type eq 'super' || $type eq 'dm' || $type eq 'sdm') && $c->req->param('submit') ) { + # If there is a new note add it as a comment to the problem (with is_internal_note set true in extra). + if ( my $new_internal_note = $c->req->params->{new_internal_note} ) { + $problem->add_to_comments( { + text => $new_internal_note, + user => $c->user->obj, + state => 'hidden', # seems best fit, should not be shown publicly + mark_fixed => 0, + anonymous => 1, + extra => { is_internal_note => 1 }, + } ); + } + } + + # Problem updates upon submission + if ( ($type eq 'super' || $type eq 'dm') && $c->req->param('submit') ) { # Predefine the hash so it's there for lookups - # XXX Note you need to shallow copy each time you set it, due to a bug? in FilterColumn. my $extra = $problem->extra || {}; - $extra->{internal_notes} = $c->req->param('internal_notes'); $extra->{publish_photo} = $c->req->params->{publish_photo} || 0; $extra->{third_personal} = $c->req->params->{third_personal} || 0; # Make sure we have a copy of the original detail field $extra->{original_detail} = $problem->detail if !$extra->{original_detail} && $c->req->params->{detail} && $problem->detail ne $c->req->params->{detail}; + # Some changes will be accompanied by an internal note, which if needed + # should be stored in this variable. + my $internal_note_text = ""; + # Workflow things my $redirect = 0; my $new_cat = $c->req->params->{category}; if ( $new_cat && $new_cat ne $problem->category ) { my $cat = $c->model('DB::Contact')->search( { category => $c->req->params->{category} } )->first; + my $old_cat = $problem->category; $problem->category( $new_cat ); $problem->external_body( undef ); $problem->bodies_str( $cat->body_id ); $problem->whensent( undef ); $extra->{changed_category} = 1; + $internal_note_text = "Weitergeleitet von $old_cat an $new_cat"; $redirect = 1 if $cat->body_id ne $body->id; } elsif ( my $subdiv = $c->req->params->{body_subdivision} ) { - $extra->{moderated_overdue} = $self->overdue( $problem ); - $problem->state( 'in progress' ); + $extra->{moderated_overdue} //= $self->overdue( $problem ); + $self->set_problem_state($c, $problem, 'in progress'); $problem->external_body( undef ); $problem->bodies_str( $subdiv ); $problem->whensent( undef ); $redirect = 1; } elsif ( my $external = $c->req->params->{body_external} ) { - $extra->{moderated_overdue} = $self->overdue( $problem ); - $problem->state( 'closed' ); + $extra->{moderated_overdue} //= $self->overdue( $problem ); + $self->set_problem_state($c, $problem, 'closed'); + $extra->{closed_overdue} //= $self->overdue( $problem ); $problem->external_body( $external ); $problem->whensent( undef ); _admin_send_email( $c, 'problem-external.txt', $problem ); $redirect = 1; } else { - $problem->state( $c->req->params->{state} ) if $c->req->params->{state}; - if ( $problem->state eq 'hidden' ) { - _admin_send_email( $c, 'problem-rejected.txt', $problem ); + if (my $state = $c->req->params->{state}) { + + if ($problem->state eq 'unconfirmed' and $state ne 'unconfirmed') { + # only set this for the first state change + $extra->{moderated_overdue} //= $self->overdue( $problem ); + } + + $self->set_problem_state($c, $problem, $state); + + if ($self->problem_is_closed($problem)) { + $extra->{closed_overdue} //= $self->overdue( $problem ); + } + if ( $state eq 'hidden' && $c->req->params->{send_rejected_email} ) { + _admin_send_email( $c, 'problem-rejected.txt', $problem ); + } } } - $problem->extra( { %$extra } ); + $problem->extra( $extra ); $problem->title( $c->req->param('title') ); $problem->detail( $c->req->param('detail') ); $problem->latitude( $c->req->param('latitude') ); @@ -410,9 +536,11 @@ sub admin_report_edit { # Final, public, Update from DM if (my $update = $c->req->param('status_update')) { $extra->{public_response} = $update; - $problem->extra( { %$extra } ); + $problem->extra( $extra ); if ($c->req->params->{publish_response}) { - $problem->state( 'fixed - council' ); + $self->set_problem_state($c, $problem, 'fixed - council'); + $extra->{closed_overdue} = $self->overdue( $problem ); + $problem->extra( { %$extra } ); _admin_send_email( $c, 'problem-closed.txt', $problem ); } } @@ -424,9 +552,22 @@ sub admin_report_edit { '<p><em>' . _('Updated!') . '</em></p>'; # do this here otherwise lastupdate and confirmed times - # do not display correctly + # do not display correctly (reloads problem from database, including + # fields modified by the database when saving) $problem->discard_changes; + # Create an internal note if required + if ($internal_note_text) { + $problem->add_to_comments( { + text => $internal_note_text, + user => $c->user->obj, + state => 'hidden', # seems best fit, should not be shown publicly + mark_fixed => 0, + anonymous => 1, + extra => { is_internal_note => 1 }, + } ); + } + if ( $redirect ) { $c->detach('index'); } @@ -447,7 +588,7 @@ sub admin_report_edit { $c->forward('check_token'); $problem->bodies_str( $body->parent->id ); - $problem->state( 'confirmed' ); + $self->set_problem_state($c, $problem, 'confirmed'); $problem->update; # log here $c->res->redirect( '/admin/summary' ); @@ -462,14 +603,6 @@ sub admin_report_edit { $db_update = 1; } - my $extra = $problem->extra || {}; - $extra->{internal_notes} ||= ''; - if ($c->req->param('internal_notes') && $c->req->param('internal_notes') ne $extra->{internal_notes}) { - $extra->{internal_notes} = $c->req->param('internal_notes'); - $problem->extra( { %$extra } ); - $db_update = 1; - } - $problem->update if $db_update; # Add new update from status_update @@ -491,10 +624,10 @@ sub admin_report_edit { if ($c->req->param('no_more_updates')) { my $extra = $problem->extra || {}; $extra->{subdiv_overdue} = $self->overdue( $problem ); - $problem->extra( { %$extra } ); + $problem->extra( $extra ); $problem->bodies_str( $body->parent->id ); $problem->whensent( undef ); - $problem->state( 'planned' ); + $self->set_problem_state($c, $problem, 'planned'); $problem->update; $c->res->redirect( '/admin/summary' ); } @@ -578,7 +711,7 @@ sub admin_stats { my %date_params; my $ym = $c->req->params->{ym}; - my ($m, $y) = $ym =~ /^(\d+)\.(\d+)$/; + my ($m, $y) = $ym ? ($ym =~ /^(\d+)\.(\d+)$/) : (); $c->stash->{ym} = $ym; if ($y && $m) { $c->stash->{start_date} = DateTime->new( year => $y, month => $m, day => 1 ); @@ -592,10 +725,31 @@ sub admin_stats { ); if ( $c->req->params->{export} ) { - my $problems = $c->model('DB::Problem')->search( { %params }, { columns => [ 'id', 'created', 'latitude', 'longitude', 'cobrand', 'category' ] } ); - my $body = "ID,Created,E,N,Category\n"; - while (my $report = $problems->next) { - $body .= join( ',', $report->id, $report->created, $report->local_coords, $report->category ) . "\n"; + my $problems = $c->model('DB::Problem')->search( + {%date_params}, + { + columns => [ + 'id', 'created', + 'latitude', 'longitude', + 'cobrand', 'category', + 'state', 'user_id', + 'external_body' + ] + } + ); + my $body = "ID,Created,E,N,Category,Status,UserID,External Body\n"; + while ( my $report = $problems->next ) { + my $external_body; + my $body_name = ""; + if ( $external_body = $report->body($c) ) { + $body_name = $external_body->name; + } + $body .= join( ',', + $report->id, $report->created, + $report->local_coords, $report->category, + $report->state, $report->user_id, + "\"$body_name\"" ) + . "\n"; } $c->res->content_type('text/csv; charset=utf-8'); $c->res->body($body); @@ -616,9 +770,11 @@ sub admin_stats { # Reports assigned to third party my $closed = $c->model('DB::Problem')->search( { state => 'closed', %date_params } )->count; # Reports moderated within 1 day - my $moderated = $c->model('DB::Problem')->search( { extra => { like => '%moderated_overdue,I1:0%' }, %params } )->count; - # Reports solved within 5 days + my $moderated = $c->model('DB::Problem')->search( { extra => { like => '%moderated_overdue,I1:0%' }, %date_params } )->count; + # Reports solved within 5 days (sent back from subdiv) my $subdiv_dealtwith = $c->model('DB::Problem')->search( { extra => { like => '%subdiv_overdue,I1:0%' }, %params } )->count; + # Reports solved within 5 days (marked as 'fixed - council', 'closed', or 'hidden' + my $fixed_in_time = $c->model('DB::Problem')->search( { extra => { like => '%closed_overdue,I1:0%' }, %date_params } )->count; # Reports per category my $per_category = $c->model('DB::Problem')->search( \%params, { select => [ 'category', { count => 'id' } ], @@ -649,7 +805,7 @@ sub admin_stats { reports_spam => $hidden, reports_assigned => $closed, reports_moderated => $moderated, - reports_dealtwith => $subdiv_dealtwith, + reports_dealtwith => $fixed_in_time, reports_category_changed => $changed, pictures_taken => $pictures_taken, pictures_published => $pictures_published, diff --git a/perllib/FixMyStreet/DB/RABXColumn.pm b/perllib/FixMyStreet/DB/RABXColumn.pm new file mode 100644 index 000000000..5f1583018 --- /dev/null +++ b/perllib/FixMyStreet/DB/RABXColumn.pm @@ -0,0 +1,98 @@ +package FixMyStreet::DB::RABXColumn; + +use strict; +use warnings; + +use IO::String; +use RABX; + +=head1 NAME + +FixMyStreet::DB::RABXColumn + +=head2 DESCRIPTION + +This is a helper component that will setup the RABX serialisation for some +fields. This is useful for when you want to persist some data structure such as +hashrefs etc. + +This code will also change the default FilterColumn behaviour so that whenever +your set a column, or specify a RABX'd column in an ->update the value is saved +to the database. The default behaviour is to check if the value is already set, +and for hashrefs this means that changes to the contents are missed as it is +still the same hashref. + +By putting all this code in one place there is also much less repetition. + +=cut + +# Store which columns are RABX cols. +# $RABX_COLUMNS{$class}{$col} = 1 +my %RABX_COLUMNS = (); + +sub _get_class_identifier { + my $class = ref $_[0] || $_[0]; + $class =~ s/.*?(\w+)$/$1/; + return $class; +} + +=head1 METHODS + +=head2 rabx_column + + # In one of your ::Result:: modules + __PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn"); + __PACKAGE__->rabx_column('data'); + +This sets up the filtering to and from the database, and also changes the +set_filtered_column behaviour to not trust the cache. + +=cut + +sub rabx_column { + my ($class, $col) = @_; + + # Apply the filtering for this column + $class->filter_column( + $col => { + filter_from_storage => sub { + my $self = shift; + my $ser = shift; + return undef unless defined $ser; + utf8::encode($ser) if utf8::is_utf8($ser); + my $h = new IO::String($ser); + return RABX::wire_rd($h); + }, + filter_to_storage => sub { + my $self = shift; + my $data = shift; + my $ser = ''; + my $h = new IO::String($ser); + RABX::wire_wr( $data, $h ); + return $ser; + }, + } + ); + + # store that this column is a RABX column. + $RABX_COLUMNS{ _get_class_identifier($class) }{$col} = 1; +} + + +sub set_filtered_column { + my ($self, $col, $val) = @_; + + my $class = ref $self; + + # because filtered objects may be expensive to marshall for storage there + # is a cache that attempts to detect if they have changed or not. For us + # this cache breaks things and our marshalling is cheap, so clear it when + # trying set a column. + delete $self->{_filtered_column}{$col} + if $RABX_COLUMNS{ _get_class_identifier($class) }{$col}; + + return $self->next::method($col, $val); +} + + +1; diff --git a/perllib/FixMyStreet/DB/Result/Alert.pm b/perllib/FixMyStreet/DB/Result/Alert.pm index fc84c8fd5..4ce72f873 100644 --- a/perllib/FixMyStreet/DB/Result/Alert.pm +++ b/perllib/FixMyStreet/DB/Result/Alert.pm @@ -48,7 +48,7 @@ __PACKAGE__->belongs_to( "alert_type", "FixMyStreet::DB::Result::AlertType", { ref => "alert_type" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, + { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); __PACKAGE__->has_many( "alerts_sent", @@ -60,12 +60,12 @@ __PACKAGE__->belongs_to( "user", "FixMyStreet::DB::Result::User", { id => "user_id" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, + { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); -# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-03-08 17:19:55 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vump36YxUO4FQi5Do6DwvA +# Created by DBIx::Class::Schema::Loader v0.07035 @ 2013-09-10 17:11:54 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:d9yIFiTGtbtFaULXZNKstQ # You can replace this text with custom code or comments, and it will be preserved on regeneration diff --git a/perllib/FixMyStreet/DB/Result/AlertSent.pm b/perllib/FixMyStreet/DB/Result/AlertSent.pm index a537c95cd..422e010a9 100644 --- a/perllib/FixMyStreet/DB/Result/AlertSent.pm +++ b/perllib/FixMyStreet/DB/Result/AlertSent.pm @@ -26,12 +26,12 @@ __PACKAGE__->belongs_to( "alert", "FixMyStreet::DB::Result::Alert", { id => "alert_id" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, + { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); -# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-03-08 17:19:55 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:oN+36hDWJuc0hqkCW9BHOw +# Created by DBIx::Class::Schema::Loader v0.07035 @ 2013-09-10 17:11:54 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:COwsprqRSNZS1IxJrPYgMQ # You can replace this text with custom code or comments, and it will be preserved on regeneration diff --git a/perllib/FixMyStreet/DB/Result/Body.pm b/perllib/FixMyStreet/DB/Result/Body.pm index bab16061b..be4adeca9 100644 --- a/perllib/FixMyStreet/DB/Result/Body.pm +++ b/perllib/FixMyStreet/DB/Result/Body.pm @@ -20,6 +20,8 @@ __PACKAGE__->add_columns( }, "name", { data_type => "text", is_nullable => 0 }, + "parent", + { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, "endpoint", { data_type => "text", is_nullable => 1 }, "jurisdiction", @@ -38,8 +40,8 @@ __PACKAGE__->add_columns( { data_type => "boolean", default_value => \"false", is_nullable => 0 }, "send_extended_statuses", { data_type => "boolean", default_value => \"false", is_nullable => 0 }, - "parent", - { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, + "deleted", + { data_type => "boolean", default_value => \"false", is_nullable => 0 }, "external_url", { data_type => "text", is_nullable => 0 }, ); @@ -61,10 +63,10 @@ __PACKAGE__->belongs_to( "FixMyStreet::DB::Result::User", { id => "comment_user_id" }, { - is_deferrable => 1, + is_deferrable => 0, join_type => "LEFT", - on_delete => "CASCADE", - on_update => "CASCADE", + on_delete => "NO ACTION", + on_update => "NO ACTION", }, ); __PACKAGE__->has_many( @@ -78,10 +80,10 @@ __PACKAGE__->belongs_to( "FixMyStreet::DB::Result::Body", { id => "parent" }, { - is_deferrable => 1, + is_deferrable => 0, join_type => "LEFT", - on_delete => "CASCADE", - on_update => "CASCADE", + on_delete => "NO ACTION", + on_update => "NO ACTION", }, ); __PACKAGE__->has_many( @@ -92,8 +94,8 @@ __PACKAGE__->has_many( ); -# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-12-19 12:47:10 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:DdtXjMWRpz20ZHjtY3oP2w +# Created by DBIx::Class::Schema::Loader v0.07035 @ 2013-09-10 18:11:23 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hTOxxiiHmC8nmQK/p8dXhQ sub url { my ( $self, $c ) = @_; diff --git a/perllib/FixMyStreet/DB/Result/BodyArea.pm b/perllib/FixMyStreet/DB/Result/BodyArea.pm index 844a3277d..4447777dc 100644 --- a/perllib/FixMyStreet/DB/Result/BodyArea.pm +++ b/perllib/FixMyStreet/DB/Result/BodyArea.pm @@ -21,12 +21,12 @@ __PACKAGE__->belongs_to( "body", "FixMyStreet::DB::Result::Body", { id => "body_id" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, + { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); -# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-12-19 12:47:10 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:aAr+Nadyu8IckZlK6+PTNg +# Created by DBIx::Class::Schema::Loader v0.07035 @ 2013-09-10 17:11:54 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:+hzie6kHleUBoEt199c/nQ __PACKAGE__->set_primary_key(__PACKAGE__->columns); diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm index c747f7fc1..e170a5655 100644 --- a/perllib/FixMyStreet/DB/Result/Comment.pm +++ b/perllib/FixMyStreet/DB/Result/Comment.pm @@ -54,6 +54,10 @@ __PACKAGE__->add_columns( { data_type => "boolean", default_value => \"false", is_nullable => 0 }, "problem_state", { data_type => "text", is_nullable => 1 }, + "external_id", + { data_type => "text", is_nullable => 1 }, + "extra", + { data_type => "text", is_nullable => 1 }, "send_fail_count", { data_type => "integer", default_value => 0, is_nullable => 0 }, "send_fail_reason", @@ -62,55 +66,32 @@ __PACKAGE__->add_columns( { data_type => "timestamp", is_nullable => 1 }, "whensent", { data_type => "timestamp", is_nullable => 1 }, - "external_id", - { data_type => "text", is_nullable => 1 }, - "extra", - { data_type => "text", is_nullable => 1 }, ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->belongs_to( "problem", "FixMyStreet::DB::Result::Problem", { id => "problem_id" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, + { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); __PACKAGE__->belongs_to( "user", "FixMyStreet::DB::Result::User", { id => "user_id" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, + { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); -# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-07-11 18:53:26 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tSejJzLxHD/fMWjpa10lfA - -__PACKAGE__->filter_column( - extra => { - filter_from_storage => sub { - my $self = shift; - my $ser = shift; - return undef unless defined $ser; - utf8::encode($ser) if utf8::is_utf8($ser); - my $h = new IO::String($ser); - return RABX::wire_rd($h); - }, - filter_to_storage => sub { - my $self = shift; - my $data = shift; - my $ser = ''; - my $h = new IO::String($ser); - RABX::wire_wr( $data, $h ); - return $ser; - }, - } -); +# Created by DBIx::Class::Schema::Loader v0.07035 @ 2013-09-10 17:11:54 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:D/+UWcF7JO/EkCiJaAHUOw + +__PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn"); +__PACKAGE__->rabx_column('extra'); use DateTime::TimeZone; use Image::Size; use Moose; use namespace::clean -except => [ 'meta' ]; -use RABX; with 'FixMyStreet::Roles::Abuser'; @@ -145,9 +126,10 @@ sub check_for_errors { $errors{update} = _('Please enter a message') unless $self->text =~ m/\S/; + # Bromley Council custom character limit if ( $self->text && $self->problem && $self->problem->bodies_str - && $self->problem->bodies_str eq '2482' && length($self->text) > 2000 ) { - $errors{update} = _('Updates are limited to 2000 characters in length. Please shorten your update'); + && $self->problem->bodies_str eq '2482' && length($self->text) > 1750 ) { + $errors{update} = sprintf( _('Updates are limited to %s characters in length. Please shorten your update'), 1750 ); } return \%errors; diff --git a/perllib/FixMyStreet/DB/Result/Contact.pm b/perllib/FixMyStreet/DB/Result/Contact.pm index 551bcd019..eca028c9b 100644 --- a/perllib/FixMyStreet/DB/Result/Contact.pm +++ b/perllib/FixMyStreet/DB/Result/Contact.pm @@ -53,32 +53,14 @@ __PACKAGE__->belongs_to( "body", "FixMyStreet::DB::Result::Body", { id => "body_id" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, + { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); -# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-12-13 12:34:33 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:imXq3EtrC0FrQwj+E2xfBw +# Created by DBIx::Class::Schema::Loader v0.07035 @ 2013-09-10 17:11:54 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hq/BFHDEu4OUI4MSy3OyHg -__PACKAGE__->filter_column( - extra => { - filter_from_storage => sub { - my $self = shift; - my $ser = shift; - return undef unless defined $ser; - utf8::encode($ser) if utf8::is_utf8($ser); - my $h = new IO::String($ser); - return RABX::wire_rd($h); - }, - filter_to_storage => sub { - my $self = shift; - my $data = shift; - my $ser = ''; - my $h = new IO::String($ser); - RABX::wire_wr( $data, $h ); - return $ser; - }, - } -); +__PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn"); +__PACKAGE__->rabx_column('extra'); 1; diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index ec15600b6..a06a339bf 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -120,12 +120,12 @@ __PACKAGE__->belongs_to( "user", "FixMyStreet::DB::Result::User", { id => "user_id" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, + { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); -# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-12-13 15:13:48 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:H2P3Og37G569nQdQA1IWaA +# Created by DBIx::Class::Schema::Loader v0.07035 @ 2013-09-10 17:11:54 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:U/4BT8EGfcCLKA/7LX+qyQ # Add fake relationship to stored procedure table __PACKAGE__->has_one( @@ -135,54 +135,15 @@ __PACKAGE__->has_one( { cascade_copy => 0, cascade_delete => 0 }, ); -__PACKAGE__->filter_column( - extra => { - filter_from_storage => sub { - my $self = shift; - my $ser = shift; - return undef unless defined $ser; - utf8::encode($ser) if utf8::is_utf8($ser); - my $h = new IO::String($ser); - return RABX::wire_rd($h); - }, - filter_to_storage => sub { - my $self = shift; - my $data = shift; - my $ser = ''; - my $h = new IO::String($ser); - RABX::wire_wr( $data, $h ); - return $ser; - }, - } -); - -__PACKAGE__->filter_column( - geocode => { - filter_from_storage => sub { - my $self = shift; - my $ser = shift; - return undef unless defined $ser; - utf8::encode($ser) if utf8::is_utf8($ser); - my $h = new IO::String($ser); - return RABX::wire_rd($h); - }, - filter_to_storage => sub { - my $self = shift; - my $data = shift; - my $ser = ''; - my $h = new IO::String($ser); - RABX::wire_wr( $data, $h ); - return $ser; - }, - } -); +__PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn"); +__PACKAGE__->rabx_column('extra'); +__PACKAGE__->rabx_column('geocode'); use DateTime::TimeZone; use Image::Size; use Moose; use namespace::clean -except => [ 'meta' ]; use Utils; -use RABX; with 'FixMyStreet::Roles::Abuser'; @@ -427,9 +388,16 @@ sub check_for_errors { $self->category(undef); } - if ( $self->bodies_str && $self->detail && - $self->bodies_str eq '2482' && length($self->detail) > 2000 ) { - $errors{detail} = _('Reports are limited to 2000 characters in length. Please shorten your report'); + if ( $self->bodies_str && $self->detail ) { + # Custom character limit: + # Bromley Council + if ( $self->bodies_str eq '2482' && length($self->detail) > 1750 ) { + $errors{detail} = sprintf( _('Reports are limited to %s characters in length. Please shorten your report'), 1750 ); + } + # Oxfordshire + if ( $self->bodies_str eq '2237' && length($self->detail) > 1700 ) { + $errors{detail} = sprintf( _('Reports are limited to %s characters in length. Please shorten your report'), 1700 ); + } } return \%errors; @@ -578,11 +546,11 @@ sub meta_line { and $problem->category && $problem->category ne _('Other') ) { $meta = - sprintf( _('Reported by %s in the %s category anonymously at %s'), + sprintf( _('Reported via %s in the %s category anonymously at %s'), $problem->service, $problem->category, $date_time ); } elsif ( $problem->service ) { - $meta = sprintf( _('Reported by %s anonymously at %s'), + $meta = sprintf( _('Reported via %s anonymously at %s'), $problem->service, $date_time ); } elsif ( $problem->category and $problem->category ne _('Other') ) { @@ -598,13 +566,13 @@ sub meta_line { and $problem->category && $problem->category ne _('Other') ) { $meta = sprintf( - _('Reported by %s in the %s category by %s at %s'), + _('Reported via %s in the %s category by %s at %s'), $problem->service, $problem->category, $problem->name, $date_time ); } elsif ( $problem->service ) { - $meta = sprintf( _('Reported by %s by %s at %s'), + $meta = sprintf( _('Reported via %s by %s at %s'), $problem->service, $problem->name, $date_time ); } elsif ( $problem->category and $problem->category ne _('Other') ) { diff --git a/perllib/FixMyStreet/DB/Result/Questionnaire.pm b/perllib/FixMyStreet/DB/Result/Questionnaire.pm index fcaa17d99..7f9c79d9a 100644 --- a/perllib/FixMyStreet/DB/Result/Questionnaire.pm +++ b/perllib/FixMyStreet/DB/Result/Questionnaire.pm @@ -36,12 +36,12 @@ __PACKAGE__->belongs_to( "problem", "FixMyStreet::DB::Result::Problem", { id => "problem_id" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, + { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); -# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-03-08 17:19:55 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:NGlSRjoBpDoIvK3EueqN6Q +# Created by DBIx::Class::Schema::Loader v0.07035 @ 2013-09-10 17:11:54 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:oL1Hk4/bNG14CY74GA75SA use DateTime::TimeZone; use Moose; diff --git a/perllib/FixMyStreet/DB/Result/Token.pm b/perllib/FixMyStreet/DB/Result/Token.pm index 028300842..5525fe7a5 100644 --- a/perllib/FixMyStreet/DB/Result/Token.pm +++ b/perllib/FixMyStreet/DB/Result/Token.pm @@ -34,8 +34,6 @@ __PACKAGE__->set_primary_key("scope", "token"); # use mySociety::DBHandle qw(dbh); use mySociety::AuthToken; -use IO::String; -use RABX; =head1 NAME @@ -54,26 +52,9 @@ ms_current_timestamp. =cut -__PACKAGE__->filter_column( - data => { - filter_from_storage => sub { - my $self = shift; - my $ser = shift; - return undef unless defined $ser; - utf8::encode($ser) if utf8::is_utf8($ser); - my $h = new IO::String($ser); - return RABX::wire_rd($h); - }, - filter_to_storage => sub { - my $self = shift; - my $data = shift; - my $ser = ''; - my $h = new IO::String($ser); - RABX::wire_wr( $data, $h ); - return $ser; - }, - } -); +__PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn"); +__PACKAGE__->rabx_column('data'); + sub new { my ( $class, $attrs ) = @_; diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index 481b654c9..523382670 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -58,10 +58,10 @@ __PACKAGE__->belongs_to( "FixMyStreet::DB::Result::Body", { id => "from_body" }, { - is_deferrable => 1, + is_deferrable => 0, join_type => "LEFT", - on_delete => "CASCADE", - on_update => "CASCADE", + on_delete => "NO ACTION", + on_update => "NO ACTION", }, ); __PACKAGE__->has_many( @@ -72,8 +72,8 @@ __PACKAGE__->has_many( ); -# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-12-14 09:23:59 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:aw374WQraL5ysOvUmUIU3w +# Created by DBIx::Class::Schema::Loader v0.07035 @ 2013-09-10 17:11:54 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:jRAtXRLRNozCmthAg9p0dA __PACKAGE__->add_columns( "password" => { diff --git a/perllib/FixMyStreet/DB/ResultSet/AlertType.pm b/perllib/FixMyStreet/DB/ResultSet/AlertType.pm index a2784950a..cc4fc67fc 100644 --- a/perllib/FixMyStreet/DB/ResultSet/AlertType.pm +++ b/perllib/FixMyStreet/DB/ResultSet/AlertType.pm @@ -58,6 +58,7 @@ sub email_alerts ($) { while (my $row = $query->fetchrow_hashref) { my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($row->{alert_cobrand})->new(); + $cobrand->set_lang_and_domain( $row->{alert_lang}, 1, FixMyStreet->path_to('locale')->stringify ); # 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 @@ -204,7 +205,7 @@ sub _send_aggregated_alert_email(%) { my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($data{cobrand})->new(); - $cobrand->set_lang_and_domain( $data{lang}, 1 ); + $cobrand->set_lang_and_domain( $data{lang}, 1, FixMyStreet->path_to('locale')->stringify ); if (!$data{alert_email}) { my $user = FixMyStreet::App->model('DB::User')->find( { diff --git a/perllib/FixMyStreet/DB/ResultSet/Problem.pm b/perllib/FixMyStreet/DB/ResultSet/Problem.pm index 97d457297..5499af474 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Problem.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Problem.pm @@ -216,6 +216,11 @@ sub categories_summary { return \%categories; } +sub get_admin_url { + my ($rs, $cobrand, $row) = @_; + return $cobrand->admin_base_url . '/report_edit/' . $row->id; +} + sub send_reports { my ( $rs, $site_override ) = @_; @@ -259,10 +264,14 @@ sub send_reports { } $cobrand->set_lang_and_domain($row->lang, 1); - if ( $row->is_from_abuser ) { + if ( $row->is_from_abuser) { $row->update( { state => 'hidden' } ); debug_print("hiding because its sender is flagged as an abuser", $row->id) if $debug_mode; next; + } elsif ( $row->title =~ /app store test/i ) { + $row->update( { state => 'hidden' } ); + debug_print("hiding because it is an app store test message", $row->id) if $debug_mode; + next; } # Template variables for the email @@ -274,7 +283,7 @@ sub send_reports { $h{query} = $row->postcode; $h{url} = $email_base_url . $row->url; - $h{admin_url} = $cobrand->admin_base_url . 'report_edit/' . $row->id; + $h{admin_url} = $rs->get_admin_url($cobrand, $row); $h{phone_line} = $h{phone} ? _('Phone:') . " $h{phone}\n\n" : ''; if ($row->photo) { $h{has_photo} = _("This web page also contains a photo of the problem, provided by the user.") . "\n\n"; @@ -331,7 +340,10 @@ sub send_reports { # XXX Only copes with at most one missing body my ($bodies, $missing) = $row->bodies_str =~ /^([\d,]+)(?:\|(\d+))?/; my @bodies = split(/,/, $bodies); - $bodies = FixMyStreet::App->model("DB::Body")->search({ id => \@bodies }); + $bodies = FixMyStreet::App->model("DB::Body")->search( + { id => \@bodies }, + { order_by => 'name' }, + ); $missing = FixMyStreet::App->model("DB::Body")->find($missing) if $missing; my @dear; diff --git a/perllib/FixMyStreet/Map/GoogleOL.pm b/perllib/FixMyStreet/Map/GoogleOL.pm new file mode 100644 index 000000000..64baf8d36 --- /dev/null +++ b/perllib/FixMyStreet/Map/GoogleOL.pm @@ -0,0 +1,22 @@ +#!/usr/bin/perl +# +# FixMyStreet:Map::GoogleOL +# Google maps on FixMyStreet, using OpenLayers. +# +# Copyright (c) 2013 UK Citizens Online Democracy. All rights reserved. +# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/ + +package FixMyStreet::Map::GoogleOL; +use parent 'FixMyStreet::Map::OSM'; + +use strict; + +sub map_type { + return '""'; +} + +sub map_template { + return 'google-ol'; +} + +1; diff --git a/perllib/FixMyStreet/SendReport/Email.pm b/perllib/FixMyStreet/SendReport/Email.pm index 9006e2f11..e8151f175 100644 --- a/perllib/FixMyStreet/SendReport/Email.pm +++ b/perllib/FixMyStreet/SendReport/Email.pm @@ -33,14 +33,24 @@ sub build_recipient_list { $self->unconfirmed_notes->{$body_email}{$row->category} = $note; } + my $body_name = $body->name; # see something uses council areas but doesn't send to councils so just use a # generic name here to minimise confusion if ( $row->cobrand eq 'seesomething' ) { - push @{ $self->to }, [ $body_email, 'See Something, Say Something' ]; + $body_name = 'See Something, Say Something'; + } + + my @emails; + # allow multiple emails per contact + if ( $body_email =~ /,/ ) { + @emails = split(/,/, $body_email); } else { - push @{ $self->to }, [ $body_email, $body->name ]; + @emails = ( $body_email ); + } + for my $email ( @emails ) { + push @{ $self->to }, [ $email, $body_name ]; + $recips{$email} = 1; } - $recips{$body_email} = 1; } return () unless $all_confirmed; @@ -74,7 +84,7 @@ sub send { # on a staging server send emails to ourselves rather than the bodies if (mySociety::Config::get('STAGING_SITE') && !mySociety::Config::get('SEND_REPORTS_ON_STAGING') && !FixMyStreet->test_mode) { - @recips = ( mySociety::Config::get('CONTACT_EMAIL') ); + @recips = ( $row->user->email ); } unless ( @recips ) { diff --git a/perllib/FixMyStreet/TestMech.pm b/perllib/FixMyStreet/TestMech.pm index e91c6a1d6..be8f004a5 100644 --- a/perllib/FixMyStreet/TestMech.pm +++ b/perllib/FixMyStreet/TestMech.pm @@ -87,8 +87,8 @@ sub log_in_ok { my $user = $mech->create_user_ok($email); - # store the old password and then change it - my $old_password = $user->password; + # remember the old password and then change it to a known one + my $old_password = $user->password || ''; $user->update( { password => 'secret' } ); # log in @@ -99,7 +99,19 @@ sub log_in_ok { $mech->logged_in_ok; # restore the password (if there was one) - $user->update( { password => $old_password } ) if $old_password; + if ($old_password) { + + # Use store_column and then make_column_dirty to bypass the filters that + # would hash the password, otherwise the password required ito log in + # would be the hash of the previous one. + $user->store_column("password", $old_password); + $user->make_column_dirty("password"); + $user->update(); + + # Belt and braces, check that the password has been correctly saved. + die "password not correctly restored after log_in_ok" + if $user->password ne $old_password; + } return $user; } @@ -296,7 +308,7 @@ sub extract_location { $meta = $mech->extract_problem_meta; -Returns the problem meta information ( submitted by, at etc ) from a +Returns the problem meta information ( submitted by, at etc ) from a problem report page =cut diff --git a/perllib/Open311.pm b/perllib/Open311.pm index 6c811d445..c8289a442 100644 --- a/perllib/Open311.pm +++ b/perllib/Open311.pm @@ -290,6 +290,7 @@ sub _populate_service_request_update_params { my $name = $comment->name || $comment->user->name; my ( $firstname, $lastname ) = ( $name =~ /(\w+)\.?\s+(.+)/ ); + $lastname ||= '-'; # fall back to problem state as it's probably correct my $state = $comment->problem_state || $comment->problem->state; diff --git a/perllib/Open311/GetServiceRequestUpdates.pm b/perllib/Open311/GetServiceRequestUpdates.pm index ae1f06a50..f7b758137 100644 --- a/perllib/Open311/GetServiceRequestUpdates.pm +++ b/perllib/Open311/GetServiceRequestUpdates.pm @@ -127,7 +127,9 @@ sub update_comments { # actually changing the state of the problem if ( FixMyStreet::DB::Result::Problem->council_states()->{$state} && $p->state ne $state && !( $p->is_fixed && FixMyStreet::DB::Result::Problem->fixed_states()->{$state} ) ) { - $p->state($state); + if ($p->is_visible) { + $p->state($state); + } $comment->problem_state($state); } } @@ -137,17 +139,19 @@ sub update_comments { $comment->insert(); if ( $self->suppress_alerts ) { - my $alert = FixMyStreet::App->model('DB::Alert')->find( { + my @alerts = FixMyStreet::App->model('DB::Alert')->search( { alert_type => 'new_updates', parameter => $p->id, confirmed => 1, user_id => $p->user->id, } ); - my $alerts_sent = FixMyStreet::App->model('DB::AlertSent')->find_or_create( { - alert_id => $alert->id, - parameter => $comment->id, - } ); + for my $alert (@alerts) { + my $alerts_sent = FixMyStreet::App->model('DB::AlertSent')->find_or_create( { + alert_id => $alert->id, + parameter => $comment->id, + } ); + } } } } |