aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Somerville <matthew-github@dracos.co.uk>2018-03-14 10:26:25 +0000
committerMatthew Somerville <matthew-github@dracos.co.uk>2018-03-14 10:26:25 +0000
commita3cbfda1d64e416a21f7aaa9f32705b050bf5911 (patch)
tree455d5ee5e595414145d9f27a8931eabca1d09c1a
parent2361c341a2d946470ce445c4ffd2efc57c348c4f (diff)
parent1511fd51adef54ec93759ef256ca9f3ce3646adb (diff)
Merge branch 'issues/commercial/987-zurich-csv-updates'
-rwxr-xr-xbin/fixmystreet.com/fixture103
-rwxr-xr-xbin/setup-contacts204
-rwxr-xr-xbin/zurich/fixture119
-rwxr-xr-xbin/zurich/overdue-alert4
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm18
-rw-r--r--perllib/FixMyStreet/App/Controller/Dashboard.pm7
-rw-r--r--perllib/FixMyStreet/App/Controller/Open311.pm2
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/New.pm1
-rw-r--r--perllib/FixMyStreet/Cobrand/Default.pm44
-rw-r--r--perllib/FixMyStreet/Cobrand/Zurich.pm573
-rw-r--r--perllib/FixMyStreet/DB/Factories.pm133
-rw-r--r--perllib/FixMyStreet/DB/Result/Problem.pm14
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/Problem.pm4
-rw-r--r--perllib/FixMyStreet/Script/Reports.pm2
-rw-r--r--perllib/FixMyStreet/SendReport/Zurich.pm8
-rw-r--r--t/app/controller/report_display.t100
-rw-r--r--t/cobrand/zurich.t138
-rw-r--r--templates/web/base/admin/bodies.html2
-rw-r--r--templates/web/base/admin/report-category.html4
-rw-r--r--templates/web/base/report/_inspect.html2
-rw-r--r--templates/web/zurich/admin/body.html30
-rw-r--r--templates/web/zurich/admin/contact-form.html25
-rw-r--r--templates/web/zurich/admin/header.html14
-rw-r--r--templates/web/zurich/admin/index-dm.html6
-rw-r--r--templates/web/zurich/admin/index-sdm.html2
-rw-r--r--templates/web/zurich/admin/index.html3
-rw-r--r--templates/web/zurich/admin/problem_row.html5
-rw-r--r--templates/web/zurich/admin/report_edit-sdm.html16
-rw-r--r--templates/web/zurich/admin/report_edit.html35
-rw-r--r--templates/web/zurich/admin/stats/index.html39
-rw-r--r--templates/web/zurich/report/_item.html4
-rw-r--r--templates/web/zurich/report/_main.html2
-rw-r--r--templates/web/zurich/report/updates.html6
-rw-r--r--web/cobrands/zurich/base.scss6
-rw-r--r--web/cobrands/zurich/js.js2
-rw-r--r--web/cobrands/zurich/layout.scss8
36 files changed, 756 insertions, 929 deletions
diff --git a/bin/fixmystreet.com/fixture b/bin/fixmystreet.com/fixture
index aceb75bd3..fc04e531d 100755
--- a/bin/fixmystreet.com/fixture
+++ b/bin/fixmystreet.com/fixture
@@ -16,12 +16,9 @@ BEGIN {
}
use List::Util qw(shuffle);
-use Path::Tiny;
use FixMyStreet;
use FixMyStreet::Cobrand;
use FixMyStreet::DB::Factories;
-use FixMyStreet::App::Model::PhotoSet;
-use DateTime::Format::Pg;
use Getopt::Long::Descriptive;
my ($opt, $usage) = describe_options(
@@ -35,32 +32,7 @@ my ($opt, $usage) = describe_options(
);
print($usage->text), exit if $opt->help;
-my $db = FixMyStreet::DB->schema->storage;
-$db->txn_begin;
-END {
- if ($db) {
- $opt->commit ? $db->txn_commit : $db->txn_rollback;
- }
-}
-if (!$opt->commit) {
- say "NOT COMMITTING TO DATABASE";
-}
-
-if ($opt->empty) {
- $db->dbh->do(q{
-DO
-$func$
-BEGIN
- EXECUTE
- (SELECT 'TRUNCATE TABLE ' || string_agg(quote_ident(tablename), ', ') || ' RESTART IDENTITY CASCADE '
- FROM pg_tables WHERE schemaname='public');
-END
-$func$;
-}) or die $!;
- $db->dbh->do( scalar FixMyStreet->path_to('db/fixture.sql')->slurp ) or die $!;
- $db->dbh->do( scalar FixMyStreet->path_to('db/generate_secret.sql')->slurp ) or die $!;
- say "Emptied database";
-}
+FixMyStreet::DB::Factories->setup($opt);
# Body + categories
my $categories = ['Potholes', 'Street lighting', 'Graffiti', 'Other'];
@@ -119,50 +91,6 @@ foreach (
# Problems
-my %titles = (
- 'Potholes' => ['Deep pothole', 'Small pothole', 'Pothole in cycle lane', 'Pothole on busy pavement', 'Large pothole', 'Sinking manhole'],
- 'Street lighting' => ['Faulty light', 'Street light not working', 'Lights out in tunnel', 'Light not coming on', 'Light not going off'],
- 'Graffiti' => ['Graffiti', 'Graffiti', 'Offensive graffiti', 'Graffiti on the bridge', 'Remove graffiti'],
- 'Other' => ['Loose drain cover', 'Flytipping on country lane', 'Vehicle blocking footpath', 'Hedge encroaching pavement', 'Full litter bins'],
-);
-my %photos = (
- 'Potholes' => [ '33717571655_46dfc6f65f_z.jpg', '37855543925_9dbbbecf41_z.jpg', '19119222668_a3c866d7c8_z.jpg', '12049724866_404b066875_z.jpg', '3705226606_eac71cf195_z.jpg', '6304445383_bd216ca892_z.jpg' ],
- 'Street lighting' => ['38110448864_fd71227247_z.jpg', '27050321819_ac123400eb_z.jpg', '35732107202_b790c61f63_z.jpg', '31889115854_01cdf38b0d_z.jpg', undef ],
- 'Graffiti' => ['12205918375_f37f0b27a9_z.jpg', '8895442578_376a9b0be0_z.jpg', '22998854352_17555b7536_z.jpg', '22593395257_3d48f23bfa_z.jpg', '20515339175_f4ed9fc1d9_z.jpg' ],
- 'Other' => ['14347396807_20737504f7_z.jpg', '14792525771_167bc20e3d_z.jpg', undef, '36296226976_a83a118ff8_z.jpg', '23222004240_273977b2b2_z.jpg'],
-);
-my %descriptions = (
- 'Potholes' => [
- '6” deep pothole in the very centre of the Bristol road; cars are swerving to avoid it. Please treat this as a matter of urgency.',
- 'It’s small but it’s a trip hazard. Right where people cross over to get into the school or church. About 3” across but will become larger if not attended to.',
- 'Just went over my handlebars as I didn’t see this pothole on Banbury road, just before the traffic lights. Dread to think what might have happened if the traffic had been busier.',
- 'I work in the cafe at 34 Clarington Avenue and we’ve had four people come in having tripped over in the last seven days. The pothole’s right outside the key-cutting shop, just near the alleyway.',
- 'This has been here, next to the side of the road, for a month',
- 'A manhole on the junction of Etherington Road is sinking into the road surface. Not only is it an accident waiting to happen but it’s making a terrible noise every time a car passes over it.',
- ],
- 'Street lighting' => [
- 'I saw a workman attempting to fix this streetlight over a week ago, and ever since then it’s come on in the daytime and gone off as soon as it gets dark. Come and sort it out please!',
- 'Every Tuesday night I have to walk across the carpark outside the station at around 9pm. Not a problem in summer but now the nights are drawing in I feel very unsafe. Please get the streetlight by the exit fixed as I’m sure I can’t be the only woman feeling vulnerable.',
- 'My toddler is too scared to go in now, as soon as you’re more than a few paces in it’s absolutely pitch black with no hope of seeing any puddles or worse on the floor. I think this needs seeing to as a priority. Thank you.',
- 'I think the lights in the multi storey carpark are motion sensitive but I’ve actually never seen them come on. Maybe the bulb needs replacing??',
- 'This streetlight is right outside my bedroom window. It is on 24 hours a day, even in blazing sunlight. Apart from the fact that it’s a waste of electricity, it makes my bedroom feel like an interrogation chamber. Please come and fix it.',
- ],
- 'Graffiti' => [
- 'Someone has scrawled a really offensive piece of graffiti (are they called ‘tags’??) on the side of the town hall. You might want to see about getting it cleaned off. Wouldn’t want my own children to see that, I’m sure others feel the same.',
- 'Can’t see the timetable at the bus shelter cos some idiot’s covered it all in red spray paint. Honestly. Kids of today.',
- 'Not gonna write down what it depicts cos I suspect that’d get caught in your profanity filter lol. But please do come and paint over this monstrosity before it causes an accident.',
- 'That same guy that’s graffitied all over town has gone and done the same on the passenger bridge over the tracks, you can see it as you come into the station. Ugly bit of garbage graffiti. Bit of a poor first impression for the town eh.',
- 'What’s the procedure for requesting a bit of graffiti be removed? There’s been a huge scrawl on the wall outside the club for months. Nice sentiment maybe but really brings the tone of the area down.',
- ],
- 'Other' => [
- 'Surprised me so much when I crossed the road I nearly took a tumble! Glad I didn’t fall in, this really needs securing now.',
- 'Some unmentionable has driven down Larker’s Lane and left a huge heap of old rubbish on the verge. Talk about ruining the view! Such a beautiful spot and these lowlifes come and dump their junk. Probably trying to avoid paying the tip.',
- 'Well someone on foot can just about squeeze through but good luck if you’ve got a pushchair or god forbid a wheelchair. Think someone’s abandoned this car; it hasn’t moved in weeks.',
- 'Awful trying to walk past after a rain shower, well any time really.',
- 'I think these need seeing to more frequently, they’re always full to overflowing by midday.',
- ],
-);
-
my ($location, $lat, $lon);
if ($opt->coords) {
$location = $opt->coords;
@@ -185,42 +113,21 @@ foreach (FixMyStreet::Cobrand->available_cobrand_classes) {
my $user = $users{'user@example.org'};
my $num = 20;
say "Created $num problems around '$location' in cobrand '$cobrand'";
-my $inaccurate_km = 0.01;
my $confirmed = DateTime->today->subtract(days => 1)->add(hours => 8);
my $problems = [];
for (1..$num) {
$confirmed->add(seconds => rand(7000));
my $category = $categories->[int(rand(@$categories))];
- my $titles = $titles{$category};
- my $descs = $descriptions{$category};
- my $rand = int(rand(@$titles));
-
- my $photo;
- if (my $file = $photos{$category}->[$rand]) {
- my $files = [ $file ];
- if ($category eq 'Graffiti') {
- push @$files, $photos{$category}->[int(rand(@$titles))];
- }
- $files = [ map { path(FixMyStreet->path_to("t/images/$_"))->slurp_raw } @$files ];
- my $photoset = FixMyStreet::App::Model::PhotoSet->new({
- data_items => $files,
- });
- $photo = $photoset->data;
- }
-
- push @$problems, FixMyStreet::DB::Factory::Problem->create({
+ push @$problems, FixMyStreet::DB::Factory::Problem->create_problem({
body => $body,
areas => ',' . $opt->area_id . ',',
user => $user,
postcode => $location,
- latitude => $lat + rand(2 * $inaccurate_km) - $inaccurate_km,
- longitude => $lon + rand(3 * $inaccurate_km) - 1.5 * $inaccurate_km,
+ latitude => $lat,
+ longitude => $lon,
category => $category,
cobrand => $cobrand,
- title => $titles->[$rand],
- detail => $descs->[$rand],
- photo_id => $photo,
- confirmed => DateTime::Format::Pg->format_datetime($confirmed),
+ confirmed => $confirmed,
});
}
diff --git a/bin/setup-contacts b/bin/setup-contacts
deleted file mode 100755
index d562ae71d..000000000
--- a/bin/setup-contacts
+++ /dev/null
@@ -1,204 +0,0 @@
-#!/usr/bin/env perl
-
-use strict;
-use warnings;
-require 5.8.0;
-use feature 'say';
-
-BEGIN {
- use File::Basename qw(dirname);
- use File::Spec;
- my $d = dirname(File::Spec->rel2abs($0));
- require "$d/../setenv.pl";
-}
-
-use FixMyStreet::App;
-
-my $moniker = $ARGV[0];
-
-my $c = FixMyStreet::App->new();
-my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($moniker)->new({ c => $c });
-$c->stash->{cobrand} = $cobrand;
-
-die "Not a staging site, bailing out" unless $c->config->{STAGING_SITE}; # TODO, allow override via --force
-say "Applying contacts for $cobrand";
-
-
-ensure_bodies();
-setup_contacts();
-
-=head2 setup_contacts, ensure_bodies
-
-routines to update extra data for contacts. These can be called by
-a script:
-
- bin/setup-contacts zurich
-
-=cut
-
-sub ensure_bodies {
- my @bodies = $cobrand->body_details_data;
-
- my $bodies_rs = $c->model('DB::Body');
-
- for my $body (@bodies) {
- # following should work (having added Unique name/parent constraint, but doesn't)
- # $bodies_rs->find_or_create( $body, { $parent ? ( key => 'body_name_parent_key' ) : () } );
- # let's keep it simple and just allow unique names
- next if $bodies_rs->search({ name => $body->{name} })->count;
- if (my $area_id = delete $body->{area_id}) {
- $body->{body_areas} = [ { area_id => $area_id } ];
- }
- my $parent = $body->{parent};
- if ($parent and ! ref $parent) {
- $body->{parent} = { name => $parent };
- }
- $bodies_rs->find_or_create( $body );
- }
-}
-
-sub setup_contacts {
- die "Not a staging site, bailing out" unless $c->config->{STAGING_SITE}; # TODO, allow override via --force
-
- my @contact_details = $cobrand->contact_details_data;
-
- for my $detail (@contact_details) {
- update_contact( $detail, $description );
- }
-}
-
-sub update_contact {
- my ($contact_data, $description) = @_;
-
- my $contact_rs = $c->model('DB::Contact');
-
- my $category = $contact_data->{category} or die "No category provided";
- $description ||= "Update contact";
-
- my $contact = ensure_contact($contact_data, $description)
- or return; # e.g. nothing returned if deleted
-
- if (my $fields = $contact_data->{fields}) {
-
- my @fields = map { get_field_extra($_) } @$fields;
- my $note = sprintf 'Fields edited by automated script%s', $description ? " ($description)" : '';
- $contact->set_extra_fields(@fields);
- $contact->set_inflated_columns({
- confirmed => 1,
- deleted => 0,
- editor => 'automated script',
- whenedited => \'NOW()',
- note => "Updated fields $description",
- });
- $contact->update;
- }
-}
-
-sub ensure_contact {
- my ($contact_data, $description) = @_;
-
- my $category = $contact_data->{category} or die "No category provided";
- $description ||= "Ensure contact exists $category";
-
- my $email = temp_email_to_update(); # will only be set if newly created
-
- my $body = get_body_for_contact($contact_data) or die "No body found for $category";
-
- my $contact_rs = $c->model('DB::Contact');
-
- my $category_details = $contact_data->{category_details} || {};
-
- if (my $old_name = delete $contact_data->{rename_from}) {
- if (my $old_category = $contact_rs->find({
- category => $old_name,
- , body => $body,
- })) {
- $old_category->update({
- category => $category,
- whenedited => \'NOW()',
- note => "Renamed $description",
- %{ $category_details || {} },
- });
- return $old_category;
- }
- }
-
- if ($contact_data->{delete}) {
- my $contact = $contact_rs->search({
- body_id => $body->id,
- category => $category,
- deleted => 0
- });
- if ($contact->count) {
- print sprintf "Deleting: %s\n", $category;
- $contact->update({
- deleted => 1,
- editor => 'automated script',
- whenedited => \'NOW()',
- note => "Deleted by script $description",
- });
- }
- return;
- }
-
- return $contact_rs->find_or_create(
- {
- body => $body,
- category => $category,
-
- confirmed => 1,
- deleted => 0,
- email => $email,
- editor => 'automated script',
- note => 'created by automated script',
- send_method => '',
- whenedited => \'NOW()',
- %{ $category_details || {} },
- },
- {
- key => 'contacts_body_id_category_idx'
- }
- );
-}
-
-sub get_field_extra {
- my ($field) = @_;
-
- my %default = (
- variable => 'true',
- order => '1',
- required => 'no',
- datatype => 'string',
- datatype_description => 'a string',
- );
-
- if (($field->{datatype} || '') eq 'boolean') {
- %default = (
- %default,
- datatype => 'singlevaluelist',
- datatype_description => 'Yes or No',
- values => { value => [
- { key => ['No'], name => ['No'] },
- { key => ['Yes'], name => ['Yes'] },
- ] },
- );
- }
-
- return { %default, %$field };
-}
-
-sub temp_email_to_update { 'test@example.com' }
-
-sub get_body_for_contact {
- my ($contact_data) = @_;
- if (my $body_name = $contact_data->{body_name}) {
- return $c->model('DB::Body')->find({ name => $body_name });
- }
- if ($cobrand->can('contact_details_data_body_default')) {
- return $cobrand->contact_details_data_body_default;
- }
- return;
- # TODO: for UK Councils use
- # $c->model('DB::Body')->find(id => $cobrand->council_id);
- # # NB: (or better that's the area in BodyAreas)
-}
diff --git a/bin/zurich/fixture b/bin/zurich/fixture
new file mode 100755
index 000000000..e387c4fab
--- /dev/null
+++ b/bin/zurich/fixture
@@ -0,0 +1,119 @@
+#!/usr/bin/env perl
+#
+# This script will create a test body and its categories, covering the area
+# provided, and users associated with that body, which should help testing
+# of report interactions.
+
+use strict;
+use warnings;
+use v5.14;
+use utf8;
+
+BEGIN {
+ use File::Basename qw(dirname);
+ use File::Spec;
+ my $d = dirname(File::Spec->rel2abs($0));
+ require "$d/../../setenv.pl";
+}
+
+use FixMyStreet;
+use FixMyStreet::Cobrand::Zurich;
+use FixMyStreet::DB::Factories;
+use Getopt::Long::Descriptive;
+
+my ($opt, $usage) = describe_options(
+ '%c %o',
+ [ 'empty', "Empty all tables of the database first" ],
+ [ 'commit', "Actually commit changes to the database" ],
+ [ 'help', "print usage message and exit", { shortcircuit => 1 } ],
+);
+print($usage->text), exit if $opt->help;
+
+FixMyStreet::DB::Factories->setup($opt);
+
+my $cobrand = FixMyStreet::Cobrand::Zurich->new();
+$cobrand->db_state_migration;
+
+# Body + categories
+my $body = FixMyStreet::DB::Factory::Body->find_or_create({
+ name => 'Zürich',
+ body_areas => [],
+ contacts => [],
+});
+say "Created body " . $body->name;
+
+my $categories = ['Potholes', 'Street lighting', 'Graffiti', 'Other'];
+my $div = FixMyStreet::DB::Factory::Body->find_or_create({
+ name => 'Division 1',
+ parent => $body->id,
+ endpoint => 'division@example.org',
+ categories => $categories,
+ area_id => 423017,
+});
+say "Created body " . $div->name;
+
+my $contact = $div->contacts->first;
+$contact->set_extra_fields(
+ map { $_->{variable} = 'true'; $_->{datatype} = 'string'; $_ }
+ { code => 'strasse', description => 'Strasse', required => 'yes', },
+ { code => 'haus_nr', description => 'Haus-Nr.', },
+ { code => 'mast_nr', description => 'Mast-Nr.', }
+);
+$contact->update;
+
+my $subdiv = FixMyStreet::DB::Factory::Body->find_or_create({
+ name => 'Subdivision A',
+ parent => $div->id,
+ endpoint => 'subdivision@example.org',
+ body_areas => [],
+ contacts => [],
+});
+say "Created body " . $subdiv->name;
+
+my $ext = FixMyStreet::DB::Factory::Body->find_or_create({
+ name => 'External Body',
+ endpoint => 'external_body@example.org',
+ body_areas => [],
+ contacts => [],
+});
+say "Created body " . $ext->name;
+
+# Users
+say "Created users, all with password 'password':";
+my %users;
+foreach (
+ { name => 'Super', email => 'super@example.org', email_verified => 1, body => $body },
+ { name => 'DM', email_verified => 1, email => 'dm@example.org', body => $div },
+ { name => 'SDM', email_verified => 1, email => 'sdm@example.org', body => $subdiv },
+ { name => 'Wizard of Oz', email_verified => 1, email => 'admin@example.org', body => $body, is_superuser => 't' },
+ { name => "Norma User", email_verified => 1, email => 'user@example.org' },
+) {
+ $users{$_->{email}} = FixMyStreet::DB::Factory::User->find_or_create($_);
+ my $su = $_->{is_superuser} ? " (superuser)" : "";
+ say "* $_->{email}$su";
+}
+
+# Problems
+
+my $lat = 47.381416;
+my $lon = 8.531369;
+
+my $user = $users{'user@example.org'};
+my $num = 20;
+say "Created $num problems around '$lat,$lon' in cobrand '" . $cobrand->moniker . "'";
+my $confirmed = DateTime->today->subtract(days => 1)->add(hours => 8);
+my $problems = [];
+for (1..$num) {
+ $confirmed->add(seconds => rand(7000));
+ my $category = $categories->[int(rand(@$categories))];
+ push @$problems, FixMyStreet::DB::Factory::Problem->create_problem({
+ body => $div,
+ user => $user,
+ postcode => "$lat,$lon",
+ latitude => $lat,
+ longitude => $lon,
+ category => $category,
+ cobrand => $cobrand->moniker,
+ confirmed => $confirmed,
+ });
+}
diff --git a/bin/zurich/overdue-alert b/bin/zurich/overdue-alert
index f4fd0f4b7..7689c172f 100755
--- a/bin/zurich/overdue-alert
+++ b/bin/zurich/overdue-alert
@@ -32,9 +32,9 @@ exit if FixMyStreet::Cobrand::Zurich::is_public_holiday($now) or FixMyStreet::Co
my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker('zurich')->new();
my %bodies = map { $_->id => $_ } FixMyStreet::DB->resultset("Body")->all;
-loop_through( 'alert-moderation-overdue.txt', 0, 1, [ 'unconfirmed' ] );
+loop_through( 'alert-moderation-overdue.txt', 0, 1, [ 'submitted' ] );
loop_through( 'alert-overdue.txt', 1, 6, 'in progress' );
-loop_through( 'alert-overdue.txt', 0, 6, ['confirmed', 'planned'] );
+loop_through( 'alert-overdue.txt', 0, 6, ['confirmed', 'feedback pending'] );
sub loop_through {
my ( $template, $include_parent, $days, $states ) = @_;
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm
index 253840082..5e69d5bf3 100644
--- a/perllib/FixMyStreet/App/Controller/Admin.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin.pm
@@ -340,6 +340,7 @@ sub update_contacts : Private {
}
$c->forward('update_extra_fields', [ $contact ]);
+ $c->forward('contact_cobrand_extra_fields', [ $contact ]);
if ( %errors ) {
$c->stash->{updated} = _('Please correct the errors below');
@@ -1008,11 +1009,18 @@ sub report_edit_location : Private {
my ($lat, $lon) = map { Utils::truncate_coordinate($_) } $problem->latitude, $problem->longitude;
if ( $c->stash->{latitude} != $lat || $c->stash->{longitude} != $lon ) {
+ # The two actions below change the stash, setting things up for e.g. a
+ # new report. But here we're only doing it in order to check the found
+ # bodies match; we don't want to overwrite the existing report data if
+ # this lookup is bad. So let's save the stash and restore it after the
+ # comparison.
+ my $safe_stash = { %{$c->stash} };
$c->forward('/council/load_and_check_areas', []);
$c->forward('/report/new/setup_categories_and_bodies');
my %allowed_bodies = map { $_ => 1 } @{$problem->bodies_str_ids};
my @new_bodies = @{$c->stash->{bodies_to_list}};
my $bodies_match = grep { exists( $allowed_bodies{$_} ) } @new_bodies;
+ $c->stash($safe_stash);
return unless $bodies_match;
$problem->latitude($c->stash->{latitude});
$problem->longitude($c->stash->{longitude});
@@ -1035,7 +1043,6 @@ sub categories_for_point : Private {
# Remove the "Pick a category" option
shift @{$c->stash->{category_options}} if @{$c->stash->{category_options}};
- $c->stash->{category_options_copy} = $c->stash->{category_options};
$c->stash->{categories_hash} = { map { $_->{name} => 1 } @{$c->stash->{category_options}} };
}
@@ -1610,6 +1617,15 @@ sub user_edit : Path('user_edit') : Args(1) {
return 1;
}
+sub contact_cobrand_extra_fields : Private {
+ my ( $self, $c, $contact ) = @_;
+
+ my $extra_fields = $c->cobrand->call_hook('contact_extra_fields');
+ foreach ( @$extra_fields ) {
+ $contact->set_extra_metadata( $_ => $c->get_param("extra[$_]") );
+ }
+}
+
sub user_cobrand_extra_fields : Private {
my ( $self, $c ) = @_;
diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm
index 661579f95..7cdf150aa 100644
--- a/perllib/FixMyStreet/App/Controller/Dashboard.pm
+++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm
@@ -345,6 +345,8 @@ Generates a CSV output, given a 'csv' stash hashref containing:
* columns: an arrayref of the columns (looked up in the row's as_hashref, plus
the following: user_name_display, acknowledged, fixed, closed, wards,
local_coords_x, local_coords_y, url).
+* extra_data: If present, a function that is passed the report and returns a
+hashref of extra data to include that can be used by 'columns'.
=cut
@@ -398,6 +400,11 @@ sub generate_csv : Private {
$report->local_coords;
$hashref->{url} = join '', $c->cobrand->base_url_for_report($report), $report->url;
+ if (my $fn = $c->stash->{csv}->{extra_data}) {
+ my $extra = $fn->($report);
+ $hashref = { %$hashref, %$extra };
+ }
+
$csv->combine(
@{$hashref}{
@{$c->stash->{csv}->{columns}}
diff --git a/perllib/FixMyStreet/App/Controller/Open311.pm b/perllib/FixMyStreet/App/Controller/Open311.pm
index c7e4e5bee..50f082e39 100644
--- a/perllib/FixMyStreet/App/Controller/Open311.pm
+++ b/perllib/FixMyStreet/App/Controller/Open311.pm
@@ -307,6 +307,7 @@ sub get_requests : Private {
# Only provide access to the published reports
my $states = FixMyStreet::DB::Result::Problem->visible_states();
delete $states->{unconfirmed};
+ delete $states->{submitted};
my $criteria = {
state => [ keys %$states ]
};
@@ -409,6 +410,7 @@ sub get_request : Private {
my $states = FixMyStreet::DB::Result::Problem->visible_states();
delete $states->{unconfirmed};
+ delete $states->{submitted};
my $criteria = {
state => [ keys %$states ],
id => $id,
diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm
index 888110429..f7334c115 100644
--- a/perllib/FixMyStreet/App/Controller/Report/New.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/New.pm
@@ -967,6 +967,7 @@ sub process_report : Private {
# set defaults that make sense
$report->state('unconfirmed');
+ $report->state('submitted') if $c->cobrand->moniker eq 'zurich';
# save the cobrand and language related information
$report->cobrand( $c->cobrand->moniker );
diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm
index c6ca5c56b..1883550e5 100644
--- a/perllib/FixMyStreet/Cobrand/Default.pm
+++ b/perllib/FixMyStreet/Cobrand/Default.pm
@@ -1159,50 +1159,6 @@ sub jurisdiction_id_example {
return $self->moniker;
}
-=item body_details_data
-
-Returns a list of bodies to create with ensure_body. These
-are mostly just passed to ->find_or_create, but there is some
-pre-processing so that you can enter:
-
- area_id => 123,
- parent => 'Big Town',
-
-instead of
-
- body_areas => [ { area_id => 123 } ],
- parent => { name => 'Big Town' },
-
-For example:
-
- return (
- {
- name => 'Big Town',
- },
- {
- name => 'Small town',
- parent => 'Big Town',
- area_id => 1234,
- },
-
-
-=cut
-
-sub body_details_data {
- return ();
-}
-
-=item contact_details_data
-
-Returns a list of contact_data to create with setup_contacts.
-See Zurich for an example.
-
-=cut
-
-sub contact_details_data {
- return ()
-}
-
=item lookup_by_ref_regex
Returns a regex to match postcode form input against to determine if a lookup
diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm
index 06244ccd4..7068d3b03 100644
--- a/perllib/FixMyStreet/Cobrand/Zurich.pm
+++ b/perllib/FixMyStreet/Cobrand/Zurich.pm
@@ -10,7 +10,6 @@ use DateTime::Format::Pg;
use strict;
use warnings;
-use feature 'say';
use utf8;
=head1 NAME
@@ -56,8 +55,7 @@ you already have, and the countres set so that they shouldn't in future.
=cut
sub setup_states {
- FixMyStreet::DB::Result::Problem->visible_states_add('unconfirmed');
- FixMyStreet::DB::Result::Problem->visible_states_remove('investigating');
+ FixMyStreet::DB::Result::Problem->visible_states_remove('not contactable');
}
sub shorten_recency_if_new_greater_than_fixed {
@@ -67,7 +65,7 @@ sub shorten_recency_if_new_greater_than_fixed {
sub pin_colour {
my ( $self, $p, $context ) = @_;
return 'green' if $p->is_fixed || $p->is_closed;
- return 'red' if $p->state eq 'unconfirmed' || $p->state eq 'confirmed';
+ return 'red' if $p->state eq 'submitted' || $p->state eq 'confirmed';
return 'yellow';
}
@@ -98,35 +96,11 @@ 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, # extern
- 'hidden' => 1,
- 'investigating' => 1, # wish
- 'unable to fix' => 1, # jurisdiction unknown
- 'partial' => 1, # not contactable
- };
-
- return wantarray ? keys %{ $states } : $states;
-}
-
-sub problem_is_closed {
- my ($self, $problem) = @_;
- return exists $self->zurich_closed_states->{ $problem->state } ? 1 : 0;
-}
-
sub zurich_public_response_states {
my $states = {
'fixed - council' => 1,
- 'closed' => 1, # extern
- 'unable to fix' => 1, # jurisdiction unknown
+ 'external' => 1,
+ 'wish' => 1,
};
return wantarray ? keys %{ $states } : $states;
@@ -134,9 +108,9 @@ sub zurich_public_response_states {
sub zurich_user_response_states {
my $states = {
+ 'jurisdiction unknown' => 1,
'hidden' => 1,
- 'investigating' => 1, # wish
- 'partial' => 1, # not contactable
+ 'not contactable' => 1,
};
return wantarray ? keys %{ $states } : $states;
@@ -160,43 +134,33 @@ sub problem_as_hashref {
my $hashref = $problem->as_hashref( $ctx );
- if ( $problem->state eq 'unconfirmed' ) {
- for my $var ( qw( photo detail state state_t is_fixed meta ) ) {
+ if ( $problem->state eq 'submitted' ) {
+ for my $var ( qw( photo is_fixed meta ) ) {
delete $hashref->{ $var };
}
$hashref->{detail} = _('This report is awaiting moderation.');
$hashref->{title} = _('This report is awaiting moderation.');
- $hashref->{state} = 'submitted';
- $hashref->{state_t} = _('Submitted');
$hashref->{banner_id} = 'closed';
} else {
+ if ( $problem->state eq 'confirmed' || $problem->state eq 'external' ) {
+ $hashref->{banner_id} = 'closed';
+ } elsif ( $problem->is_fixed || $problem->is_closed ) {
+ $hashref->{banner_id} = 'fixed';
+ } else {
+ $hashref->{banner_id} = 'progress';
+ }
+
if ( $problem->state eq 'confirmed' ) {
$hashref->{state} = 'open';
$hashref->{state_t} = _('Open');
- $hashref->{banner_id} = 'closed';
- } elsif ( $problem->state eq 'closed' ) {
- $hashref->{state} = 'extern'; # is this correct?
- $hashref->{banner_id} = 'closed';
- $hashref->{state_t} = _('Extern');
- } elsif ( $problem->state eq 'unable to fix' ) {
- $hashref->{state} = 'jurisdiction unknown'; # is this correct?
- $hashref->{state_t} = _('Jurisdiction Unknown');
- $hashref->{banner_id} = 'fixed'; # green
- } elsif ( $problem->state eq 'partial' ) {
- $hashref->{state} = 'not contactable'; # is this correct?
- $hashref->{state_t} = _('Not contactable');
- # no banner_id as hidden
- } elsif ( $problem->state eq 'investigating' ) {
- $hashref->{state} = 'wish'; # is this correct?
- $hashref->{state_t} = _('Wish');
+ } elsif ( $problem->state eq 'wish' ) {
+ $hashref->{state_t} = _('Closed');
} elsif ( $problem->is_fixed ) {
$hashref->{state} = 'closed';
- $hashref->{banner_id} = 'fixed';
$hashref->{state_t} = _('Closed');
- } elsif ( $problem->state eq 'in progress' || $problem->state eq 'planned' ) {
+ } elsif ( $problem->state eq 'feedback pending' ) {
$hashref->{state} = 'in progress';
- $hashref->{state_t} = _('In progress');
- $hashref->{banner_id} = 'progress';
+ $hashref->{state_t} = FixMyStreet::DB->resultset("State")->display('in progress');
}
}
@@ -210,13 +174,13 @@ sub updates_as_hashref {
my $hashref = {};
- if ( $problem->state eq 'fixed - council' || $problem->state eq 'closed' ) {
+ if (problem_has_public_response($problem)) {
$hashref->{update_pp} = $self->prettify_dt( $problem->lastupdate );
- if ( $problem->state eq 'fixed - council' ) {
+ if ( $problem->state ne 'external' ) {
$hashref->{details} = FixMyStreet::App::View::Web::add_links(
$problem->get_extra_metadata('public_response') || '' );
- } elsif ( $problem->state eq 'closed' ) {
+ } else {
$hashref->{details} = sprintf( _('Assigned to %s'), $problem->body($ctx)->name );
}
}
@@ -229,6 +193,7 @@ sub updates_as_hashref {
# boolean whether that indexed photo can be shown.
sub allow_photo_display {
my ( $self, $r, $num ) = @_;
+ return unless $r;
my $publish_photo;
if (blessed $r) {
$publish_photo = $r->get_extra_metadata('publish_photo');
@@ -250,10 +215,6 @@ sub allow_photo_display {
return $i + 1;
}
-sub show_unconfirmed_reports {
- 1;
-}
-
sub get_body_sender {
my ( $self, $body, $category ) = @_;
return { method => 'Zurich' };
@@ -324,25 +285,23 @@ sub overdue {
return 0 unless $w;
# call with previous state
- if ( $problem->state eq 'unconfirmed' ) {
+ if ( $problem->state eq 'submitted' ) {
# One working day
$w = add_days( $w, 1 );
return $w < DateTime->now() ? 1 : 0;
- } elsif ( $problem->state eq 'confirmed' || $problem->state eq 'in progress' || $problem->state eq 'planned' ) {
+ } elsif ( $problem->state eq 'confirmed' || $problem->state eq 'in progress' || $problem->state eq 'feedback pending' ) {
# 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) ) {
+ } else {
# 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;
}
}
@@ -430,6 +389,13 @@ sub admin_pages {
'users' => [_('Users'), 3],
'user_edit' => [undef, undef],
};
+
+ # There are some pages that only super users can see
+ if ($self->{c}->user->is_superuser) {
+ $pages->{states} = [ _('States'), 8 ];
+ $pages->{config} = [ _('Configuration'), 9];
+ };
+
return $pages if $type eq 'super';
}
@@ -471,14 +437,14 @@ sub admin {
$order .= ' desc' if $dir;
# XXX No multiples or missing bodies
- $c->stash->{unconfirmed} = $c->cobrand->problems->search({
- state => [ 'unconfirmed', 'confirmed' ],
+ $c->stash->{submitted} = $c->cobrand->problems->search({
+ state => [ 'submitted', 'confirmed' ],
bodies_str => $c->stash->{body}->id,
}, {
order_by => $order,
});
$c->stash->{approval} = $c->cobrand->problems->search({
- state => 'planned',
+ state => 'feedback pending',
bodies_str => $c->stash->{body}->id,
}, {
order_by => $order,
@@ -486,7 +452,7 @@ sub admin {
my $page = $c->get_param('p') || 1;
$c->stash->{other} = $c->cobrand->problems->search({
- state => { -not_in => [ 'unconfirmed', 'confirmed', 'planned' ] },
+ state => { -not_in => [ 'submitted', 'confirmed', 'feedback pending' ] },
bodies_str => \@all,
}, {
order_by => $order,
@@ -512,7 +478,7 @@ sub admin {
order_by => $order
} );
$c->stash->{reports_unpublished} = $c->cobrand->problems->search( {
- state => 'planned',
+ state => 'feedback pending',
bodies_str => $body->parent->id,
}, {
order_by => $order
@@ -529,6 +495,15 @@ sub admin {
}
}
+sub category_options {
+ my ($self, $c) = @_;
+ my @categories = $c->model('DB::Contact')->not_deleted->all;
+ $c->stash->{category_options} = [ map { {
+ name => $_->category, value => $_->category,
+ abbreviation => $_->get_extra_metadata('abbreviation'),
+ } } @categories ];
+}
+
sub admin_report_edit {
my $self = shift;
my $c = $self->{c};
@@ -539,6 +514,8 @@ sub admin_report_edit {
if ($type ne 'super') {
my %allowed_bodies = map { $_->id => 1 } ( $body->bodies->all, $body );
+ # SDMs can see parent reports but not edit them
+ $allowed_bodies{$body->parent->id} = 1 if $type eq 'sdm';
$c->detach( '/page_error_404_not_found' )
unless $allowed_bodies{$problem->bodies_str};
}
@@ -550,8 +527,7 @@ sub admin_report_edit {
$c->stash->{bodies} = \@bodies;
# Can change category to any other
- my @categories = $c->model('DB::Contact')->not_deleted->all;
- $c->stash->{category_options} = [ map { { name => $_->category, value => $_->category } } @categories ];
+ $self->category_options($c);
} elsif ($type eq 'dm') {
@@ -565,8 +541,7 @@ sub admin_report_edit {
$c->stash->{bodies} = \@bodies;
# Can change category to any other
- my @categories = $c->model('DB::Contact')->not_deleted->all;
- $c->stash->{category_options} = [ map { { name => $_->category, value => $_->category } } @categories ];
+ $self->category_options($c);
}
@@ -636,8 +611,7 @@ sub admin_report_edit {
my $state = $c->get_param('state') || '';
my $oldstate = $problem->state;
- my $closure_states = $self->zurich_closed_states;
- delete $closure_states->{'fixed - council'}; # may not be needed?
+ my $closure_states = { map { $_ => 1 } FixMyStreet::DB::Result::Problem->closed_states(), FixMyStreet::DB::Result::Problem->hidden_states() };
my $old_closure_state = $problem->get_extra_metadata('closure_status') || '';
@@ -662,19 +636,19 @@ sub admin_report_edit {
$self->update_admin_log($c, $problem, "Changed category from $old_cat to $new_cat");
$redirect = 1 if $cat->body_id ne $body->id;
} elsif ( $oldstate ne $state and $closure_states->{$state} and
- $oldstate ne 'planned' || $old_closure_state ne $state)
+ $oldstate ne 'feedback pending' || $old_closure_state ne $state)
{
# for these states
- # - closed (Extern)
- # - investigating (Wish)
+ # - external
+ # - wish
# - hidden
- # - partial (Not contactable)
- # - unable to fix (Jurisdiction unknown)
- # we divert to planned (Rueckmeldung ausstehend) and set closure_status to the requested state
+ # - not contactable
+ # - jurisdiction unknown
+ # we divert to feedback pending (Rueckmeldung ausstehend) and set closure_status to the requested state
# From here, the DM can reply to the user, triggering the setting of problem to correct state
$problem->set_extra_metadata( closure_status => $state );
- $self->set_problem_state($c, $problem, 'planned');
- $state = 'planned';
+ $self->set_problem_state($c, $problem, 'feedback pending');
+ $state = 'feedback pending';
$problem->set_extra_metadata_if_undefined( moderated_overdue => $self->overdue( $problem ) );
} elsif ( my $subdiv = $c->get_param('body_subdivision') ) {
@@ -687,18 +661,18 @@ sub admin_report_edit {
} else {
if ($state) {
- if ($oldstate eq 'unconfirmed' and $state ne 'unconfirmed') {
+ if ($oldstate eq 'submitted' and $state ne 'submitted') {
# only set this for the first state change
$problem->set_extra_metadata_if_undefined( moderated_overdue => $self->overdue( $problem ) );
}
$self->set_problem_state($c, $problem, $state)
unless $closure_states->{$state};
- # we'll defer to 'planned' clause below to change the state
+ # we'll defer to 'feedback pending' clause below to change the state
}
}
- if ($problem->state eq 'planned') {
+ if ($problem->state eq 'feedback pending') {
# Rueckmeldung ausstehend
# override $state from the metadata set above
$state = $problem->get_extra_metadata('closure_status') || '';
@@ -711,7 +685,7 @@ sub admin_report_edit {
$moderated++;
$closed++;
}
- elsif ($state =~/^(closed|investigating)$/) { # Extern | Wish
+ elsif ($state =~/^(external|wish)$/) {
$moderated++;
# Nested if instead of `and` because in these cases, we *don't*
# want to close unless we have body_external (so we don't want
@@ -724,7 +698,7 @@ sub admin_report_edit {
if ($problem->external_body && $c->get_param('publish_response')) {
$problem->whensent( undef );
$self->set_problem_state($c, $problem, $state);
- my $template = ($state eq 'investigating') ? 'problem-wish.txt' : 'problem-external.txt';
+ my $template = ($state eq 'wish') ? 'problem-wish.txt' : 'problem-external.txt';
_admin_send_email( $c, $template, $problem );
$redirect = 0;
$closed++;
@@ -770,7 +744,7 @@ sub admin_report_edit {
# send external_message if provided and state is *now* Wish|Extern
# e.g. was already, or was set in the Rueckmeldung ausstehend clause above.
if ( my $external_message = $c->get_param('external_message')
- and $problem->state =~ /^(closed|investigating)$/)
+ and $problem->state =~ /^(external|wish)$/)
{
my $external = $problem->external_body;
my $external_body = $c->model('DB::Body')->find($external)
@@ -782,7 +756,7 @@ sub admin_report_edit {
$problem->add_to_comments( {
text => (
sprintf '(%s %s) %s',
- $state eq 'closed' ?
+ $state eq 'external' ?
_('Forwarded to external body') :
_('Forwarded wish to external body'),
$external_body->name,
@@ -841,10 +815,13 @@ sub admin_report_edit {
if ($type eq 'sdm') {
+ my $editable = $type eq 'sdm' && $body->id eq $problem->bodies_str;
+ $c->stash->{sdm_disabled} = $editable ? '' : 'disabled';
+
# Has cut-down edit template for adding update and sending back up only
$c->stash->{template} = 'admin/report_edit-sdm.html';
- if ($c->get_param('send_back') or $c->get_param('not_contactable')) {
+ if ($editable && $c->get_param('send_back') or $c->get_param('not_contactable')) {
# SDM can send back a report either to be assigned to a different
# subdivision, or because the customer was not contactable.
# We handle these in the same way but with different statuses.
@@ -856,8 +833,8 @@ sub admin_report_edit {
$problem->bodies_str( $body->parent->id );
if ($not_contactable) {
# we can't directly set state, but mark the closure_status for DM to confirm.
- $self->set_problem_state($c, $problem, 'planned');
- $problem->set_extra_metadata( closure_status => 'partial');
+ $self->set_problem_state($c, $problem, 'feedback pending');
+ $problem->set_extra_metadata( closure_status => 'not contactable');
}
else {
$self->set_problem_state($c, $problem, 'confirmed');
@@ -870,7 +847,7 @@ sub admin_report_edit {
# Make sure the problem's time_spent is updated
$self->update_admin_log($c, $problem);
$c->res->redirect( '/admin/summary' );
- } elsif ($c->get_param('submit')) {
+ } elsif ($editable && $c->get_param('submit')) {
$c->forward('/auth/check_csrf_token');
my $db_update = 0;
@@ -904,7 +881,7 @@ sub admin_report_edit {
$problem->set_extra_metadata( subdiv_overdue => $self->overdue( $problem ) );
$problem->bodies_str( $body->parent->id );
$problem->whensent( undef );
- $self->set_problem_state($c, $problem, 'planned');
+ $self->set_problem_state($c, $problem, 'feedback pending');
$problem->update;
$c->res->redirect( '/admin/summary' );
}
@@ -932,61 +909,52 @@ sub stash_states {
my @states = (
{
# Erfasst
- state => 'unconfirmed',
- trans => _('Submitted'),
- unconfirmed => 1,
+ state => 'submitted',
+ submitted => 1,
hidden => 1,
},
{
# Aufgenommen
state => 'confirmed',
- trans => _('Open'),
- unconfirmed => 1,
+ submitted => 1,
},
{
# Unsichtbar (hidden)
state => 'hidden',
- trans => _('Hidden'),
- unconfirmed => 1,
+ submitted => 1,
hidden => 1,
},
{
# Extern
- state => 'closed',
- trans => _('Extern'),
+ state => 'external',
},
{
# Zustaendigkeit unbekannt
- state => 'unable to fix',
- trans => _('Jurisdiction unknown'),
+ state => 'jurisdiction unknown',
},
{
- # Wunsch (hidden)
- state => 'investigating',
- trans => _('Wish'),
+ # Wunsch
+ state => 'wish',
},
{
# Nicht kontaktierbar (hidden)
- state => 'partial',
- trans => _('Not contactable'),
+ state => 'not contactable',
},
);
- my %state_trans = map { $_->{state} => $_->{trans} } @states;
my $state = $problem->state;
# Rueckmeldung ausstehend may also indicate the status it's working towards.
push @states, do {
- if ($state eq 'planned' and my $closure_status = $problem->get_extra_metadata('closure_status')) {
+ if ($state eq 'feedback pending' and my $closure_status = $problem->get_extra_metadata('closure_status')) {
{
state => $closure_status,
- trans => sprintf '%s (%s)', _('Planned'), $state_trans{$closure_status},
+ trans => sprintf 'Rückmeldung ausstehend (%s)', FixMyStreet::DB->resultset("State")->display($closure_status),
};
}
else {
{
- state => 'planned',
- trans => _('Planned'),
+ state => 'feedback pending',
};
}
};
@@ -994,25 +962,22 @@ sub stash_states {
if ($state eq 'in progress') {
push @states, {
state => 'in progress',
- trans => _('In progress'),
};
}
elsif ($state eq 'fixed - council') {
push @states, {
state => 'fixed - council',
- trans => _('Closed'),
};
}
- elsif ($state =~/^(hidden|unconfirmed)$/) {
+ elsif ($state =~/^(hidden|submitted)$/) {
@states = grep { $_->{$state} } @states;
}
$c->stash->{states} = \@states;
- $c->stash->{states_trans} = { map { $_->{state} => $_->{trans} } @states }; # [% states_trans.${problem.state} %]
# stash details about the public response
$c->stash->{default_public_response} = "\nFreundliche Grüsse\n\nIhre Stadt Zürich\n";
$c->stash->{show_publish_response} =
- ($problem->state eq 'planned');
+ ($problem->state eq 'feedback pending');
}
=head2 _admin_send_email
@@ -1044,7 +1009,7 @@ sub _admin_send_email {
sub munge_sendreport_params {
my ($self, $row, $h, $params) = @_;
- if ($row->state =~ /^(closed|investigating)$/) {
+ if ($row->state =~ /^(external|wish)$/) {
# we attach images to reports sent to external bodies
my $photoset = $row->get_photoset();
my $num = $photoset->num_images
@@ -1114,138 +1079,34 @@ sub admin_stats {
my $self = shift;
my $c = $self->{c};
- my %date_params;
+ my %optional_params;
my $ym = $c->get_param('ym');
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 );
$c->stash->{end_date} = $c->stash->{start_date} + DateTime::Duration->new( months => 1 );
- $date_params{created} = {
+ $optional_params{created} = {
'>=', DateTime::Format::Pg->format_datetime($c->stash->{start_date}),
'<', DateTime::Format::Pg->format_datetime($c->stash->{end_date}),
};
}
+ my $cat = $c->stash->{category} = $c->get_param('category');
+ $optional_params{category} = $cat if $cat;
+
my %params = (
- %date_params,
+ %optional_params,
state => [ FixMyStreet::DB::Result::Problem->visible_states() ],
);
if ( $c->get_param('export') ) {
- my $problems = $c->model('DB::Problem')->search(
- {%date_params},
- {
- join => 'admin_log_entries',
- distinct => 1,
- columns => [
- 'id', 'created',
- 'latitude', 'longitude',
- 'cobrand', 'category',
- 'state', 'user_id',
- 'external_body',
- 'title', 'detail',
- 'photo',
- 'whensent', 'lastupdate',
- 'service',
- 'extra',
- { sum_time_spent => { sum => 'admin_log_entries.time_spent' } },
- ]
- }
- );
- my @fields = (
- 'Report ID',
- 'Created',
- 'Sent to Agency',
- 'Last Updated',
- 'E',
- 'N',
- 'Category',
- 'Status',
- 'Closure Status',
- 'UserID',
- 'External Body',
- 'Time Spent',
- 'Title',
- 'Detail',
- 'Media URL',
- 'Interface Used',
- 'Council Response',
- 'Strasse',
- 'Mast-Nr.',
- 'Haus-Nr.',
- 'Hydranten-Nr.',
- );
-
- my $body = "";
- require Text::CSV;
- my $csv = Text::CSV->new({ binary => 1 });
-
- if ($csv->combine(@fields)) {
- $body .= $csv->string . "\n";
- }
- else {
- $body .= sprintf "{{error emitting CSV line: %s}}\n", $csv->error_diag;
- }
-
- while ( my $report = $problems->next ) {
- my $external_body;
- my $body_name = "";
- if ( $external_body = $report->body($c) ) {
- $body_name = $external_body->name || '[Unknown body]';
- }
-
- my $detail = $report->detail;
- my $public_response = $report->get_extra_metadata('public_response') || '';
- my $metas = $report->get_extra_fields();
- my %extras;
- foreach my $field (@$metas) {
- $extras{$field->{name}} = $field->{value};
- }
-
- # replace newlines with HTML <br/> element
- $detail =~ s{\r?\n}{ <br/> }g;
- $public_response =~ s{\r?\n}{ <br/> }g if $public_response;
-
- # Assemble photo URL, if report has a photo
- my $photo_to_display = $c->cobrand->allow_photo_display($report);
- my $media_url = (@{$report->photos} && $photo_to_display)
- ? $c->cobrand->base_url . $report->photos->[$photo_to_display-1]->{url}
- : '';
-
- my @columns = (
- $report->id,
- $report->created,
- $report->whensent,
- $report->lastupdate,
- $report->local_coords, $report->category,
- $report->state,
- $report->get_extra_metadata('closure_status') || '',
- $report->user_id,
- $body_name,
- $report->get_column('sum_time_spent') || 0,
- $report->title,
- $detail,
- $media_url,
- $report->service || 'Web interface',
- $public_response,
- $extras{'strasse'} || '',
- $extras{'mast_nr'} || '',
- $extras{'haus_nr'} || '',
- $extras{'hydranten_nr'} || ''
- );
- if ($csv->combine(@columns)) {
- $body .= $csv->string . "\n";
- }
- else {
- $body .= sprintf "{{error emitting CSV line: %s}}\n", $csv->error_diag;
- }
- }
- $c->res->content_type('text/csv; charset=utf-8');
- $c->res->header('Content-Disposition' => 'attachment; filename=stats.csv');
- $c->res->body($body);
+ return $self->export_as_csv($c, \%optional_params);
}
+ # Can change category to any other
+ $self->category_options($c);
+
# Total reports (non-hidden)
my $total = $c->model('DB::Problem')->search( \%params )->count;
# Device for apps (iOS/Android)
@@ -1255,17 +1116,17 @@ sub admin_stats {
group_by => [ 'service' ],
});
# Reports solved
- my $solved = $c->model('DB::Problem')->search( { state => 'fixed - council', %date_params } )->count;
+ my $solved = $c->model('DB::Problem')->search( { state => 'fixed - council', %optional_params } )->count;
# Reports marked as spam
- my $hidden = $c->model('DB::Problem')->search( { state => 'hidden', %date_params } )->count;
+ my $hidden = $c->model('DB::Problem')->search( { state => 'hidden', %optional_params } )->count;
# Reports assigned to third party
- my $closed = $c->model('DB::Problem')->search( { state => 'closed', %date_params } )->count;
+ my $external = $c->model('DB::Problem')->search( { state => 'external', %optional_params } )->count;
# Reports moderated within 1 day
- my $moderated = $c->model('DB::Problem')->search( { extra => { like => '%moderated_overdue,I1:0%' }, %date_params } )->count;
+ my $moderated = $c->model('DB::Problem')->search( { extra => { like => '%moderated_overdue,I1:0%' }, %optional_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 solved within 5 days (marked as 'fixed - council', 'external', or 'hidden'
+ my $fixed_in_time = $c->model('DB::Problem')->search( { extra => { like => '%closed_overdue,I1:0%' }, %optional_params } )->count;
# Reports per category
my $per_category = $c->model('DB::Problem')->search( \%params, {
select => [ 'category', { count => 'id' } ],
@@ -1294,7 +1155,7 @@ sub admin_stats {
reports_total => $total,
reports_solved => $solved,
reports_spam => $hidden,
- reports_assigned => $closed,
+ reports_assigned => $external,
reports_moderated => $moderated,
reports_dealtwith => $fixed_in_time,
reports_category_changed => $changed,
@@ -1309,6 +1170,96 @@ sub admin_stats {
return 1;
}
+sub export_as_csv {
+ my ($self, $c, $params) = @_;
+ my $csv = $c->stash->{csv} = {
+ problems => $c->model('DB::Problem')->search_rs(
+ $params,
+ {
+ join => 'admin_log_entries',
+ distinct => 1,
+ columns => [
+ 'id', 'created',
+ 'latitude', 'longitude',
+ 'cobrand', 'category',
+ 'state', 'user_id',
+ 'external_body',
+ 'title', 'detail',
+ 'photo',
+ 'whensent', 'lastupdate',
+ 'service',
+ 'extra',
+ { sum_time_spent => { sum => 'admin_log_entries.time_spent' } },
+ ]
+ }
+ ),
+ headers => [
+ 'Report ID', 'Created', 'Sent to Agency', 'Last Updated',
+ 'E', 'N', 'Category', 'Status', 'Closure Status',
+ 'UserID', 'User email', 'User phone', 'User name',
+ 'External Body', 'Time Spent', 'Title', 'Detail',
+ 'Media URL', 'Interface Used', 'Council Response',
+ 'Strasse', 'Mast-Nr.', 'Haus-Nr.', 'Hydranten-Nr.',
+ ],
+ columns => [
+ 'id', 'created', 'whensent',' lastupdate', 'local_coords_x',
+ 'local_coords_y', 'category', 'state', 'closure_status',
+ 'user_id', 'user_email', 'user_phone', 'user_name',
+ 'body_name', 'sum_time_spent', 'title', 'detail',
+ 'media_url', 'service', 'public_response',
+ 'strasse', 'mast_nr',' haus_nr', 'hydranten_nr',
+ ],
+ extra_data => sub {
+ my $report = shift;
+
+ my $body_name = "";
+ if ( my $external_body = $report->body($c) ) {
+ $body_name = $external_body->name || '[Unknown body]';
+ }
+
+ my $detail = $report->detail;
+ my $public_response = $report->get_extra_metadata('public_response') || '';
+ my $metas = $report->get_extra_fields();
+ my %extras;
+ foreach my $field (@$metas) {
+ $extras{$field->{name}} = $field->{value};
+ }
+
+ # replace newlines with HTML <br/> element
+ $detail =~ s{\r?\n}{ <br/> }g;
+ $public_response =~ s{\r?\n}{ <br/> }g if $public_response;
+
+ # Assemble photo URL, if report has a photo
+ my $photo_to_display = $c->cobrand->allow_photo_display($report);
+ my $media_url = (@{$report->photos} && $photo_to_display)
+ ? $c->cobrand->base_url . $report->photos->[$photo_to_display-1]->{url}
+ : '';
+
+ return {
+ whensent => $report->whensent,
+ lastupdate => $report->lastupdate,
+ user_id => $report->user_id,
+ user_email => $report->user->email || '',
+ user_phone => $report->user->phone || '',
+ user_name => $report->name,
+ closure_status => $report->get_extra_metadata('closure_status') || '',
+ body_name => $body_name,
+ sum_time_spent => $report->get_column('sum_time_spent') || 0,
+ detail => $detail,
+ media_url => $media_url,
+ service => $report->service || 'Web interface',
+ public_response => $public_response,
+ strasse => $extras{'strasse'} || '',
+ mast_nr => $extras{'mast_nr'} || '',
+ haus_nr => $extras{'haus_nr'} || '',
+ hydranten_nr => $extras{'hydranten_nr'} || ''
+ };
+ },
+ filename => 'stats',
+ };
+ $c->forward('/dashboard/generate_csv');
+}
+
sub problem_confirm_email_extras {
my ($self, $report) = @_;
my $confirmed_reports = $report->user->problems->search({
@@ -1318,102 +1269,44 @@ sub problem_confirm_email_extras {
$self->{c}->stash->{email_confirmed} = $confirmed_reports;
}
-sub body_details_data {
- return (
- {
- name => 'Stadt Zurich'
- },
- {
- name => 'Elektrizitäwerk Stadt Zürich',
- parent => 'Stadt Zurich',
- area_id => 423017,
- },
- {
- name => 'ERZ Entsorgung + Recycling Zürich',
- parent => 'Stadt Zurich',
- area_id => 423017,
- },
- {
- name => 'Fachstelle Graffiti',
- parent => 'Stadt Zurich',
- area_id => 423017,
- },
- {
- name => 'Grün Stadt Zürich',
- parent => 'Stadt Zurich',
- area_id => 423017,
- },
- {
- name => 'Tiefbauamt Stadt Zürich',
- parent => 'Stadt Zurich',
- area_id => 423017,
- },
- {
- name => 'Dienstabteilung Verkehr',
- parent => 'Stadt Zurich',
- area_id => 423017,
- },
- );
-}
+sub reports_per_page { return 20; }
-sub contact_details_data {
- return (
- {
- category => 'Beleuchtung/Uhren',
- body_name => 'Elektrizitätswerk Stadt Zürich',
- fields => [
- {
- code => 'strasse',
- description => 'Strasse',
- datatype => 'string',
- required => 'yes',
- },
- {
- code => 'haus_nr',
- description => 'Haus-Nr.',
- datatype => 'string',
- },
- {
- code => 'mast_nr',
- description => 'Mast-Nr.',
- datatype => 'string',
- }
- ],
- },
- {
- category => 'Brunnen/Hydranten',
- # body_name ???
- fields => [
- {
- code => 'hydranten_nr',
- description => 'Hydranten-Nr.',
- datatype => 'string',
- },
- ],
- },
- {
- category => "Grünflächen/Spielplätze",
- body_name => 'Grün Stadt Zürich',
- rename_from => "Tiere/Grünflächen",
- },
- {
- category => 'Spielplatz/Sitzbank',
- body_name => 'Grün Stadt Zürich',
- delete => 1,
- },
+sub singleton_bodies_str { 1 }
+
+sub contact_extra_fields { [ 'abbreviation' ] };
+
+sub db_state_migration {
+ my $rs = FixMyStreet::DB->resultset('State');
+
+ # Create new states needed
+ $rs->create({ label => 'submitted', type => 'open', name => 'Erfasst' });
+ $rs->create({ label => 'feedback pending', type => 'open', name => 'Rückmeldung ausstehend' });
+ $rs->create({ label => 'wish', type => 'closed', name => 'Wunsch' });
+ $rs->create({ label => 'external', type => 'closed', name => 'Extern' });
+ $rs->create({ label => 'jurisdiction unknown', type => 'closed', name => 'Zuständigkeit unbekannt' });
+ $rs->create({ label => 'not contactable', type => 'closed', name => 'Nicht kontaktierbar' });
+
+ # And update used current ones to have correct name
+ $rs->find({ label => 'in progress' })->update({ name => 'In Bearbeitung' });
+ $rs->find({ label => 'fixed' })->update({ name => 'Beantwortet' });
+
+ # Move reports to correct new state
+ my %state_move = (
+ unconfirmed => 'submitted',
+ closed => 'external',
+ investigating => 'wish',
+ 'unable to fix' => 'jurisdiction unknown',
+ planned => 'feedback pending',
+ partial => 'not contactable',
);
-}
+ foreach (keys %state_move) {
+ FixMyStreet::DB->resultset('Problem')->search({ state => $_ })->update({ state => $state_move{$_} });
+ }
-sub contact_details_data_body_default {
- my ($self) = @_;
- # temporary measure to assign un-bodied contacts to parent
- # (this isn't at all how things will be setup in live, but is
- # handy during dev.)
- return $self->{c}->model('DB::Body')->find({ name => 'Stadt Zurich' });
+ # Delete unused standard states from the database
+ for ('action scheduled', 'duplicate', 'not responsible', 'internal referral', 'planned', 'investigating', 'unable to fix') {
+ $rs->find({ label => $_ })->delete;
+ }
}
-sub reports_per_page { return 20; }
-
-sub singleton_bodies_str { 1 }
-
1;
diff --git a/perllib/FixMyStreet/DB/Factories.pm b/perllib/FixMyStreet/DB/Factories.pm
index 0e99608e1..66148ad61 100644
--- a/perllib/FixMyStreet/DB/Factories.pm
+++ b/perllib/FixMyStreet/DB/Factories.pm
@@ -1,5 +1,46 @@
+use strict;
+use warnings;
+use v5.14;
+
use FixMyStreet::DB;
+package FixMyStreet::DB::Factories;
+
+my $db;
+my $opt;
+
+END {
+ if ($db) {
+ $opt->commit ? $db->txn_commit : $db->txn_rollback;
+ }
+}
+sub setup {
+ my $cls = shift;
+
+ $opt = shift;
+ $db = FixMyStreet::DB->schema->storage;
+ $db->txn_begin;
+ if (!$opt->commit) {
+ say "NOT COMMITTING TO DATABASE";
+ }
+
+ if ($opt->empty) {
+ $db->dbh->do(q{
+DO
+$func$
+BEGIN
+ EXECUTE
+ (SELECT 'TRUNCATE TABLE ' || string_agg(quote_ident(tablename), ', ') || ' RESTART IDENTITY CASCADE '
+ FROM pg_tables WHERE schemaname='public');
+END
+$func$;
+}) or die $!;
+ $db->dbh->do( scalar FixMyStreet->path_to('db/fixture.sql')->slurp ) or die $!;
+ $db->dbh->do( scalar FixMyStreet->path_to('db/generate_secret.sql')->slurp ) or die $!;
+ say "Emptied database";
+ }
+}
+
package FixMyStreet::DB::Factory::Base;
use parent "DBIx::Class::Factory";
@@ -19,6 +60,10 @@ sub find_or_create {
package FixMyStreet::DB::Factory::Problem;
use parent "DBIx::Class::Factory";
+use Path::Tiny;
+use DateTime::Format::Pg;
+use FixMyStreet;
+use FixMyStreet::App::Model::PhotoSet;
__PACKAGE__->resultset(FixMyStreet::DB->resultset("Problem"));
@@ -43,6 +88,94 @@ __PACKAGE__->fields({
category => 'Other',
});
+sub data {
+ my $self = shift;
+
+ my %titles = (
+ 'Potholes' => ['Deep pothole', 'Small pothole', 'Pothole in cycle lane', 'Pothole on busy pavement', 'Large pothole', 'Sinking manhole'],
+ 'Street lighting' => ['Faulty light', 'Street light not working', 'Lights out in tunnel', 'Light not coming on', 'Light not going off'],
+ 'Graffiti' => ['Graffiti', 'Graffiti', 'Offensive graffiti', 'Graffiti on the bridge', 'Remove graffiti'],
+ 'Other' => ['Loose drain cover', 'Flytipping on country lane', 'Vehicle blocking footpath', 'Hedge encroaching pavement', 'Full litter bins'],
+ );
+ my %photos = (
+ 'Potholes' => [ '33717571655_46dfc6f65f_z.jpg', '37855543925_9dbbbecf41_z.jpg', '19119222668_a3c866d7c8_z.jpg', '12049724866_404b066875_z.jpg', '3705226606_eac71cf195_z.jpg', '6304445383_bd216ca892_z.jpg' ],
+ 'Street lighting' => ['38110448864_fd71227247_z.jpg', '27050321819_ac123400eb_z.jpg', '35732107202_b790c61f63_z.jpg', '31889115854_01cdf38b0d_z.jpg', undef ],
+ 'Graffiti' => ['12205918375_f37f0b27a9_z.jpg', '8895442578_376a9b0be0_z.jpg', '22998854352_17555b7536_z.jpg', '22593395257_3d48f23bfa_z.jpg', '20515339175_f4ed9fc1d9_z.jpg' ],
+ 'Other' => ['14347396807_20737504f7_z.jpg', '14792525771_167bc20e3d_z.jpg', undef, '36296226976_a83a118ff8_z.jpg', '23222004240_273977b2b2_z.jpg'],
+ );
+ my %descriptions = (
+ 'Potholes' => [
+ '6” deep pothole in the very centre of the Bristol road; cars are swerving to avoid it. Please treat this as a matter of urgency.',
+ 'It’s small but it’s a trip hazard. Right where people cross over to get into the school or church. About 3” across but will become larger if not attended to.',
+ 'Just went over my handlebars as I didn’t see this pothole on Banbury road, just before the traffic lights. Dread to think what might have happened if the traffic had been busier.',
+ 'I work in the cafe at 34 Clarington Avenue and we’ve had four people come in having tripped over in the last seven days. The pothole’s right outside the key-cutting shop, just near the alleyway.',
+ 'This has been here, next to the side of the road, for a month',
+ 'A manhole on the junction of Etherington Road is sinking into the road surface. Not only is it an accident waiting to happen but it’s making a terrible noise every time a car passes over it.',
+ ],
+ 'Street lighting' => [
+ 'I saw a workman attempting to fix this streetlight over a week ago, and ever since then it’s come on in the daytime and gone off as soon as it gets dark. Come and sort it out please!',
+ 'Every Tuesday night I have to walk across the carpark outside the station at around 9pm. Not a problem in summer but now the nights are drawing in I feel very unsafe. Please get the streetlight by the exit fixed as I’m sure I can’t be the only woman feeling vulnerable.',
+ 'My toddler is too scared to go in now, as soon as you’re more than a few paces in it’s absolutely pitch black with no hope of seeing any puddles or worse on the floor. I think this needs seeing to as a priority. Thank you.',
+ 'I think the lights in the multi storey carpark are motion sensitive but I’ve actually never seen them come on. Maybe the bulb needs replacing??',
+ 'This streetlight is right outside my bedroom window. It is on 24 hours a day, even in blazing sunlight. Apart from the fact that it’s a waste of electricity, it makes my bedroom feel like an interrogation chamber. Please come and fix it.',
+ ],
+ 'Graffiti' => [
+ 'Someone has scrawled a really offensive piece of graffiti (are they called ‘tags’??) on the side of the town hall. You might want to see about getting it cleaned off. Wouldn’t want my own children to see that, I’m sure others feel the same.',
+ 'Can’t see the timetable at the bus shelter cos some idiot’s covered it all in red spray paint. Honestly. Kids of today.',
+ 'Not gonna write down what it depicts cos I suspect that’d get caught in your profanity filter lol. But please do come and paint over this monstrosity before it causes an accident.',
+ 'That same guy that’s graffitied all over town has gone and done the same on the passenger bridge over the tracks, you can see it as you come into the station. Ugly bit of garbage graffiti. Bit of a poor first impression for the town eh.',
+ 'What’s the procedure for requesting a bit of graffiti be removed? There’s been a huge scrawl on the wall outside the club for months. Nice sentiment maybe but really brings the tone of the area down.',
+ ],
+ 'Other' => [
+ 'Surprised me so much when I crossed the road I nearly took a tumble! Glad I didn’t fall in, this really needs securing now.',
+ 'Some unmentionable has driven down Larker’s Lane and left a huge heap of old rubbish on the verge. Talk about ruining the view! Such a beautiful spot and these lowlifes come and dump their junk. Probably trying to avoid paying the tip.',
+ 'Well someone on foot can just about squeeze through but good luck if you’ve got a pushchair or god forbid a wheelchair. Think someone’s abandoned this car; it hasn’t moved in weeks.',
+ 'Awful trying to walk past after a rain shower, well any time really.',
+ 'I think these need seeing to more frequently, they’re always full to overflowing by midday.',
+ ],
+ );
+
+ return {
+ titles => \%titles,
+ descriptions => \%descriptions,
+ photos => \%photos,
+ };
+}
+
+sub create_problem {
+ my $self = shift;
+ my $params = shift;
+
+ my $data = $self->data;
+ my $category = $params->{category};
+ my $inaccurate_km = 0.01;
+
+ my $titles = $data->{titles}{$category};
+ my $descs = $data->{descriptions}{$category};
+ my $rand = int(rand(@$titles));
+
+ my $photo;
+ if (my $file = $data->{photos}{$category}->[$rand]) {
+ my $files = [ $file ];
+ if ($category eq 'Graffiti') {
+ push @$files, $data->{photos}{$category}->[int(rand(@$titles))];
+ }
+ $files = [ map { path(FixMyStreet->path_to("t/images/$_"))->slurp_raw } @$files ];
+ my $photoset = FixMyStreet::App::Model::PhotoSet->new({
+ data_items => $files,
+ });
+ $photo = $photoset->data;
+ }
+
+ $params->{latitude} += rand(2 * $inaccurate_km) - $inaccurate_km;
+ $params->{longitude} += rand(3 * $inaccurate_km) - 1.5 * $inaccurate_km,
+ $params->{title} = $titles->[$rand];
+ $params->{detail} = $descs->[$rand];
+ $params->{photo_id} = $photo;
+ $params->{confirmed} = DateTime::Format::Pg->format_datetime($params->{confirmed});
+ return $self->create($params);
+}
+
#######################
package FixMyStreet::DB::Factory::Body;
diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm
index c73f7efca..2deeb3084 100644
--- a/perllib/FixMyStreet/DB/Result/Problem.pm
+++ b/perllib/FixMyStreet/DB/Result/Problem.pm
@@ -341,7 +341,7 @@ around service => sub {
sub title_safe {
my $self = shift;
- return _('Awaiting moderation') if $self->cobrand eq 'zurich' && $self->state eq 'unconfirmed';
+ return _('Awaiting moderation') if $self->cobrand eq 'zurich' && $self->state eq 'submitted';
return $self->title;
}
@@ -509,6 +509,18 @@ sub tokenised_url {
return "/M/". $token->token;
}
+=head2 is_hidden
+
+Returns 1 if the problem is in an hidden state otherwise 0.
+
+=cut
+
+sub is_hidden {
+ my $self = shift;
+
+ return exists $self->hidden_states->{ $self->state } ? 1 : 0;
+}
+
=head2 is_open
Returns 1 if the problem is in a open state otherwise 0.
diff --git a/perllib/FixMyStreet/DB/ResultSet/Problem.pm b/perllib/FixMyStreet/DB/ResultSet/Problem.pm
index 458efa179..ebc6e6c1b 100644
--- a/perllib/FixMyStreet/DB/ResultSet/Problem.pm
+++ b/perllib/FixMyStreet/DB/ResultSet/Problem.pm
@@ -118,8 +118,8 @@ sub _recent {
my $key = $photos ? 'recent_photos' : 'recent';
$key .= ":$site_key:$num";
- # unconfirmed might be returned for e.g. Zurich, but would mean in moderation, so no photo
- my @states = grep { $_ ne 'unconfirmed' } FixMyStreet::DB::Result::Problem->visible_states();
+ # submitted might be returned for e.g. Zurich, but would mean in moderation, so no photo
+ my @states = grep { $_ ne 'submitted' } FixMyStreet::DB::Result::Problem->visible_states();
my $query = {
non_public => 0,
state => \@states,
diff --git a/perllib/FixMyStreet/Script/Reports.pm b/perllib/FixMyStreet/Script/Reports.pm
index be3c4c69a..aa6b64752 100644
--- a/perllib/FixMyStreet/Script/Reports.pm
+++ b/perllib/FixMyStreet/Script/Reports.pm
@@ -29,7 +29,7 @@ sub send(;$) {
my $site = $site_override || CronFns::site($base_url);
my $states = [ FixMyStreet::DB::Result::Problem::open_states() ];
- $states = [ 'unconfirmed', 'confirmed', 'in progress', 'planned', 'closed', 'investigating' ] if $site eq 'zurich';
+ $states = [ 'submitted', 'confirmed', 'in progress', 'feedback pending', 'external', 'wish' ] if $site eq 'zurich';
my $unsent = $rs->search( {
state => $states,
whensent => undef,
diff --git a/perllib/FixMyStreet/SendReport/Zurich.pm b/perllib/FixMyStreet/SendReport/Zurich.pm
index b38981d94..59adfd688 100644
--- a/perllib/FixMyStreet/SendReport/Zurich.pm
+++ b/perllib/FixMyStreet/SendReport/Zurich.pm
@@ -44,15 +44,15 @@ sub get_template {
my ( $self, $row ) = @_;
my $template;
- if ( $row->state eq 'unconfirmed' || $row->state eq 'confirmed' ) {
+ if ( $row->state eq 'submitted' || $row->state eq 'confirmed' ) {
$template = 'submit.txt';
} elsif ( $row->state eq 'in progress' ) {
$template = 'submit-in-progress.txt';
- } elsif ( $row->state eq 'planned' ) {
+ } elsif ( $row->state eq 'feedback pending' ) {
$template = 'submit-feedback-pending.txt';
- } elsif ( $row->state eq 'investigating' ) {
+ } elsif ( $row->state eq 'wish' ) {
$template = 'submit-external-wish.txt';
- } elsif ( $row->state eq 'closed' ) {
+ } elsif ( $row->state eq 'external' ) {
$template = 'submit-external.txt';
if ( $row->extra->{third_personal} ) {
$template = 'submit-external-personal.txt';
diff --git a/t/app/controller/report_display.t b/t/app/controller/report_display.t
index f0913fbd2..605371ed7 100644
--- a/t/app/controller/report_display.t
+++ b/t/app/controller/report_display.t
@@ -419,106 +419,6 @@ for my $test (
};
}
-subtest "Zurich unconfirmeds are 200" => sub {
- FixMyStreet::override_config {
- ALLOWED_COBRANDS => [ 'zurich' ],
- MAP_TYPE => 'Zurich,OSM',
- }, sub {
- $mech->host( 'zurich.example.com' );
- ok $report->update( { state => 'unconfirmed' } ), 'unconfirm report';
- $mech->get_ok("/report/$report_id");
- $mech->content_contains( '&Uuml;berpr&uuml;fung ausstehend' );
- ok $report->update( { state => 'confirmed' } ), 'confirm report again';
- $mech->host( 'www.fixmystreet.com' );
- };
-};
-
-subtest "Zurich banners are displayed correctly" => sub {
- FixMyStreet::override_config {
- ALLOWED_COBRANDS => [ 'zurich' ],
- MAP_TYPE => 'Zurich,OSM',
- }, sub {
- $mech->host( 'zurich.example.com' );
-
- for my $test (
- {
- description => 'new report',
- state => 'unconfirmed',
- banner_id => 'closed',
- banner_text => 'Erfasst'
- },
- {
- description => 'confirmed report',
- state => 'confirmed',
- banner_id => 'closed',
- banner_text => 'Aufgenommen',
- },
- {
- description => 'fixed report',
- state => 'fixed - council',
- banner_id => 'fixed',
- banner_text => 'Beantwortet',
- },
- {
- description => 'closed report',
- state => 'closed',
- banner_id => 'closed',
- banner_text => _('Extern'),
- },
- {
- description => 'in progress report',
- state => 'in progress',
- banner_id => 'progress',
- banner_text => 'In Bearbeitung',
- },
- {
- description => 'planned report',
- state => 'planned',
- banner_id => 'progress',
- banner_text => 'In Bearbeitung',
- },
- {
- description => 'planned report',
- state => 'planned',
- banner_id => 'progress',
- banner_text => 'In Bearbeitung',
- },
- {
- description => 'jurisdiction unknown',
- state => 'unable to fix',
- banner_id => 'fixed',
- # We can't use _('Jurisdiction Unknown') here because
- # TestMech::extract_problem_banner decodes the HTML entities before
- # the string is passed back.
- banner_text => 'Zust\x{e4}ndigkeit unbekannt',
- },
- ) {
- subtest "banner for $test->{description}" => sub {
- $report->state( $test->{state} );
- $report->update;
-
- $mech->get_ok("/report/$report_id");
- is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
- my $banner = $mech->extract_problem_banner;
- if ( $banner->{text} ) {
- $banner->{text} =~ s/^ //g;
- $banner->{text} =~ s/ $//g;
- }
-
- is $banner->{id}, $test->{banner_id}, 'banner id';
- if ($test->{banner_text}) {
- like_string( $banner->{text}, qr/$test->{banner_text}/i, 'banner text is ' . $test->{banner_text} );
- } else {
- is $banner->{text}, $test->{banner_text}, 'banner text';
- }
-
- };
- }
-
- $mech->host( 'www.fixmystreet.com' );
- };
-};
-
my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council');
my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire);
diff --git a/t/cobrand/zurich.t b/t/cobrand/zurich.t
index 4ae9a0cde..f5c6b1145 100644
--- a/t/cobrand/zurich.t
+++ b/t/cobrand/zurich.t
@@ -23,6 +23,7 @@ my $mech = FixMyStreet::TestMech->new;
use FixMyStreet;
my $cobrand = FixMyStreet::Cobrand::Zurich->new();
+$cobrand->db_state_migration;
my $sample_file = path(__FILE__)->parent->parent->child("app/controller/sample.jpg");
ok $sample_file->exists, "sample file $sample_file exists";
@@ -48,7 +49,7 @@ sub reset_report_state {
$report->unset_extra_metadata('closed_overdue');
$report->unset_extra_metadata('closure_status');
$report->whensent(undef);
- $report->state('unconfirmed');
+ $report->state('submitted');
$report->created($created) if $created;
$report->update;
}
@@ -109,7 +110,7 @@ subtest "set up superuser" => sub {
};
my @reports = $mech->create_problems_for_body( 1, $division->id, 'Test', {
- state => 'unconfirmed',
+ state => 'submitted',
confirmed => undef,
cobrand => 'zurich',
areas => ',423017,',
@@ -125,6 +126,80 @@ FixMyStreet::override_config {
$mech->content_contains('&Uuml;berpr&uuml;fung ausstehend')
or die $mech->content;
+subtest "Banners are displayed correctly" => sub {
+ FixMyStreet::override_config {
+ ALLOWED_COBRANDS => [ 'zurich' ],
+ MAP_TYPE => 'Zurich,OSM',
+ }, sub {
+ for my $test (
+ {
+ description => 'new report',
+ state => 'submitted',
+ banner_id => 'closed',
+ banner_text => 'Erfasst'
+ },
+ {
+ description => 'confirmed report',
+ state => 'confirmed',
+ banner_id => 'closed',
+ banner_text => 'Aufgenommen',
+ },
+ {
+ description => 'fixed report',
+ state => 'fixed - council',
+ banner_id => 'fixed',
+ banner_text => 'Beantwortet',
+ },
+ {
+ description => 'closed report',
+ state => 'external',
+ banner_id => 'closed',
+ banner_text => 'Extern',
+ },
+ {
+ description => 'in progress report',
+ state => 'in progress',
+ banner_id => 'progress',
+ banner_text => 'In Bearbeitung',
+ },
+ {
+ description => 'planned report',
+ state => 'feedback pending',
+ banner_id => 'progress',
+ banner_text => 'In Bearbeitung',
+ },
+ {
+ description => 'jurisdiction unknown',
+ state => 'jurisdiction unknown',
+ banner_id => 'fixed',
+ banner_text => 'Zust\x{e4}ndigkeit unbekannt',
+ },
+ ) {
+ subtest "banner for $test->{description}" => sub {
+ $report->state( $test->{state} );
+ $report->update;
+
+ $mech->get_ok("/report/" . $report->id);
+ is $mech->uri->path, "/report/" . $report->id, "at /report/" . $report->id;
+ my $banner = $mech->extract_problem_banner;
+ if ( $banner->{text} ) {
+ $banner->{text} =~ s/^ //g;
+ $banner->{text} =~ s/ $//g;
+ }
+
+ is $banner->{id}, $test->{banner_id}, 'banner id';
+ if ($test->{banner_text}) {
+ like_string( $banner->{text}, qr/$test->{banner_text}/i, 'banner text is ' . $test->{banner_text} );
+ } else {
+ is $banner->{text}, $test->{banner_text}, 'banner text';
+ }
+
+ };
+ }
+ $report->update({ state => 'submitted' });
+ };
+};
+
# Check logging in to deal with this report
FixMyStreet::override_config {
ALLOWED_COBRANDS => [ 'zurich' ],
@@ -158,7 +233,7 @@ subtest "changing of categories" => sub {
);
}
- # full Categories dropdown is hidden for unconfirmed reports
+ # full Categories dropdown is hidden for submitted reports
$report->update({ state => 'confirmed' });
# put report into known category
@@ -274,7 +349,7 @@ subtest "report_edit" => sub {
$report->discard_changes;
is ( $report->get_extra_metadata('moderated_overdue'), 0, 'Still marked moderated_overdue' );
is ( $report->get_extra_metadata('closed_overdue'), undef, "Marking hidden doesn't set closed_overdue..." );
- is ( $report->state, 'planned', 'Marking hidden actually sets state to planned');
+ is ( $report->state, 'feedback pending', 'Marking hidden actually sets state to feedback pending');
is ( $report->get_extra_metadata('closure_status'), 'hidden', 'Marking hidden sets closure_status to hidden');
is get_moderated_count(), 1, 'Check still counted moderated'
or diag $report->get_column('extra');
@@ -411,6 +486,12 @@ subtest 'SDM' => sub {
$mech->submit_form_ok( { button => 'no_more_updates' } );
is $mech->uri->path, '/admin/summary', "redirected now finished with report.";
+ # Can still view the edit page but can't change anything
+ $mech->get_ok( '/admin/report_edit/' . $report->id );
+ $mech->content_contains('<input disabled');
+ $mech->submit_form_ok( { with_fields => { status_update => 'This is a disallowed update.' } } );
+ $mech->content_lacks('This is a disallowed update');
+
$mech->get_ok( '/report/' . $report->id );
$mech->content_contains('In Bearbeitung');
$mech->content_contains('Test Test');
@@ -423,7 +504,7 @@ subtest 'SDM' => sub {
$mech->clear_emails_ok;
$report->discard_changes;
- is $report->state, 'planned', 'Report now in planned state';
+ is $report->state, 'feedback pending', 'Report now in feedback pending state';
subtest 'send_back' => sub {
FixMyStreet::override_config {
@@ -448,8 +529,8 @@ subtest 'SDM' => sub {
$mech->get_ok( '/admin/report_edit/' . $report->id );
$mech->submit_form_ok( { button => 'not_contactable', form_number => 2 } );
$report->discard_changes;
- is $report->state, 'planned', 'Report sent back to Rueckmeldung ausstehend state';
- is $report->get_extra_metadata('closure_status'), 'partial', 'Report sent back to partial (not_contactable) state';
+ is $report->state, 'feedback pending', 'Report sent back to Rueckmeldung ausstehend state';
+ is $report->get_extra_metadata('closure_status'), 'not contactable', 'Report sent back to not_contactable state';
is $report->bodies_str, $division->id, 'Report sent back to division';
};
};
@@ -465,7 +546,7 @@ FixMyStreet::override_config {
};
reset_report_state($report);
-$report->update({ state => 'planned' });
+$report->update({ state => 'feedback pending' });
$mech->content_contains( 'report_edit/' . $report->id );
$mech->content_contains( DateTime->now->strftime("%d.%m.%Y") );
@@ -500,9 +581,9 @@ like $email->header('From'), qr/do-not-reply\@example.org/, 'from line looks cor
like $email->body, qr/FINAL UPDATE/, 'body looks correct';
$mech->clear_emails_ok;
-# Assign planned (via confirmed), don't confirm email
+# Assign feedback pending (via confirmed), don't confirm email
@reports = $mech->create_problems_for_body( 1, $division->id, 'Second', {
- state => 'unconfirmed',
+ state => 'submitted',
confirmed => undef,
cobrand => 'zurich',
areas => ',423017,',
@@ -516,7 +597,7 @@ FixMyStreet::override_config {
$mech->get_ok( '/admin/report_edit/' . $report->id );
$mech->submit_form_ok( { with_fields => { state => 'confirmed' } } );
$mech->get_ok( '/admin/report_edit/' . $report->id );
- $mech->submit_form_ok( { with_fields => { state => 'planned' } } );
+ $mech->submit_form_ok( { with_fields => { state => 'feedback pending' } } );
$mech->get_ok( '/report/' . $report->id );
};
$mech->content_contains('In Bearbeitung');
@@ -543,7 +624,7 @@ $mech->email_count_is(0);
# Report assigned to third party
@reports = $mech->create_problems_for_body( 1, $division->id, 'Third', {
- state => 'unconfirmed',
+ state => 'submitted',
confirmed => undef,
cobrand => 'zurich',
areas => ',423017,',
@@ -558,8 +639,8 @@ subtest "external report triggers email" => sub {
}, sub {
# required to see body_external field
- $report->state('planned');
- $report->set_extra_metadata('closure_status' => 'closed');
+ $report->state('feedback pending');
+ $report->set_extra_metadata('closure_status' => 'external');
# Set the public_response manually here because the default one will have line breaks that get escaped as HTML, causing the comparison to fail.
$report->set_extra_metadata('public_response' => 'Freundliche Gruesse Ihre Stadt Zuerich');
$report->update;
@@ -575,7 +656,7 @@ subtest "external report triggers email" => sub {
$report->discard_changes;
$mech->get_ok( '/report/' . $report->id );
};
- is ($report->state, 'closed', 'Report was closed correctly');
+ is ($report->state, 'external', 'Report was closed correctly');
$mech->content_contains('Extern')
or die $mech->content;
$mech->content_contains('Third Test');
@@ -596,8 +677,8 @@ subtest "external report triggers email" => sub {
}, sub {
$mech->get_ok( '/admin' );
# required to see body_external field
- $report->state('planned');
- $report->set_extra_metadata('closure_status' => 'closed');
+ $report->state('feedback pending');
+ $report->set_extra_metadata('closure_status' => 'external');
$report->set_extra_metadata('public_response' => 'Freundliche Gruesse Ihre Stadt Zuerich');
$report->update;
@@ -632,10 +713,10 @@ subtest "external report triggers email" => sub {
}, sub {
# set as wish
$report->discard_changes;
- $report->state('planned');
- $report->set_extra_metadata('closure_status' => 'investigating');
+ $report->state('feedback pending');
+ $report->set_extra_metadata('closure_status' => 'wish');
$report->update;
- is ($report->state, 'planned', 'Sanity check') or die;
+ is ($report->state, 'feedback pending', 'Sanity check') or die;
$mech->get_ok( '/admin/report_edit/' . $report->id );
@@ -646,6 +727,9 @@ subtest "external report triggers email" => sub {
body_external => $external_body->id,
external_message => $EXTERNAL_MESSAGE,
} });
+ # Wishes publicly viewable
+ $mech->get_ok( '/report/' . $report->id );
+ $mech->content_contains('Freundliche Gruesse Ihre Stadt Zuerich');
};
send_reports_for_zurich();
$email = $mech->get_email;
@@ -665,12 +749,12 @@ subtest "external report triggers email" => sub {
}, sub {
# set as extern
reset_report_state($report);
- $report->state('planned');
- $report->set_extra_metadata('closure_status' => 'closed');
+ $report->state('feedback pending');
+ $report->set_extra_metadata('closure_status' => 'external');
$report->set_extra_metadata('email_confirmed' => 1);
$report->unset_extra_metadata('public_response');
$report->update;
- is ($report->state, 'planned', 'Sanity check') or die;
+ is ($report->state, 'feedback pending', 'Sanity check') or die;
$mech->get_ok( '/admin/report_edit/' . $report->id );
@@ -872,7 +956,7 @@ subtest "test admin_log" => sub {
# XXX: following is dependent on all of test up till now, rewrite to explicitly
# test which things need to be logged!
is scalar @entries, 4, 'State changes logged';
- is $entries[-1]->action, 'state change to closed', 'State change logged as expected';
+ is $entries[-1]->action, 'state change to external', 'State change logged as expected';
};
subtest 'email images to external partners' => sub {
@@ -892,7 +976,7 @@ subtest 'email images to external partners' => sub {
# The below email comparison must not have an external message.
$report->unset_extra_metadata('external_message');
$report->update({
- state => 'closed',
+ state => 'external',
photo => $fileid,
external_body => $external_body->id,
});
@@ -944,9 +1028,9 @@ subtest 'Status update shown as appropriate' => sub {
}, sub {
# ALL closed states must hide the public_response edit, and public ones
# must show the answer in blue.
- for (['planned', 1, 0, 0],
+ for (['feedback pending', 1, 0, 0],
['fixed - council', 0, 1, 0],
- ['closed', 0, 1, 0],
+ ['external', 0, 1, 0],
['hidden', 0, 0, 1])
{
my ($state, $update, $public, $user_response) = @$_;
diff --git a/templates/web/base/admin/bodies.html b/templates/web/base/admin/bodies.html
index 9bd85940b..eab0f4c49 100644
--- a/templates/web/base/admin/bodies.html
+++ b/templates/web/base/admin/bodies.html
@@ -20,7 +20,7 @@
</p>
[% END %]
- <table cellspacing="0" cellpadding="2" border="1">
+ <table cellspacing="0" cellpadding="2" border="1" id="admin_bodies">
<tr>
<th>[% loc('Name') %]</th>
[% IF c.cobrand.moniker == 'zurich' %]
diff --git a/templates/web/base/admin/report-category.html b/templates/web/base/admin/report-category.html
index a2290089b..2d9ffcdb1 100644
--- a/templates/web/base/admin/report-category.html
+++ b/templates/web/base/admin/report-category.html
@@ -4,9 +4,9 @@
<option selected value="[% problem.category | html %]">[% (problem.category_display OR '-') | html %]</option>
</optgroup>
[% END %]
- [% IF category_options_copy.size %]
+ [% IF category_options.size %]
<optgroup label="[% loc('Available categories') %]">
- [% FOREACH cat IN category_options_copy %]
+ [% FOREACH cat IN category_options %]
<option value="[% cat.name | html %]"[% ' selected' IF problem.category == cat.name %]>[% cat.value | html %]</option>
[% END %]
</optgroup>
diff --git a/templates/web/base/report/_inspect.html b/templates/web/base/report/_inspect.html
index 111e71283..6923dbb1a 100644
--- a/templates/web/base/report/_inspect.html
+++ b/templates/web/base/report/_inspect.html
@@ -58,7 +58,7 @@
[% INCLUDE 'admin/report-category.html' %]
</p>
- [% FOREACH category IN category_options_copy %]
+ [% FOREACH category IN category_options %]
[% cat_name = category.name;
cat_prefix = cat_name | lower | replace('[^a-z]', '');
cat_prefix = "category_" _ cat_prefix _ "_" %]
diff --git a/templates/web/zurich/admin/body.html b/templates/web/zurich/admin/body.html
index 468f7adff..11be6eef7 100644
--- a/templates/web/zurich/admin/body.html
+++ b/templates/web/zurich/admin/body.html
@@ -16,14 +16,16 @@
<th>[% loc('Last editor') %]</th>
<th>[% loc('Note') %]</th>
<th>[% loc('When edited') %]</th>
+ <th>Kürzel</th>
</tr>
[% WHILE ( cat = contacts.next ) %]
- <tr[% IF cat.deleted %] class="is-deleted"[% END %]>
+ <tr[% IF cat.state == 'deleted' %] class="is-deleted"[% END %]>
<td><a href="[% c.uri_for( 'body', body_id, cat.category ) %]">[% cat.category_display %]</a></td>
<td>[% cat.email | html %]</td>
<td>[% cat.editor %]</td>
<td>[% cat.note | html %]</td>
<td>[% PROCESS format_date this_date=cat.whenedited %]</td>
+ <td>[% cat.get_extra_metadata('abbreviation') %]</td>
</tr>
[% END %]
</table>
@@ -37,31 +39,7 @@
</div>
[% END %]
- <form method="post" action="[% c.uri_for('body', body_id ) %]" enctype="application/x-www-form-urlencoded" accept-charset="utf-8">
-
- <p>
- <strong>[% loc('Category:') %] </strong>
- <input type="text" class="form-control" name="category" size="30" value="[% contact.category | html %]">
- </p>
-
- <p>
- <strong>[% loc('Email:') %] </strong>
- <input type="text" class="form-control" name="email" size="30" value="[% contact.email | html %]">
- </p>
-
- <input type="hidden" name="confirmed" value="1" id="confirmed">
-
- <p>
- <strong>[% loc('Note:') %] </strong>
- <textarea class="form-control" name="note" rows="3" cols="40"></textarea>
- </p>
-
- <p>
- <input type="hidden" name="posted" value="new" >
- <input type="hidden" name="token" value="[% csrf_token %]" >
- <input type="submit" class="btn" name="Create category" value="[% errors ? loc('Save changes') : loc('Create category') %]">
- </p>
- </form>
+ [% INCLUDE 'admin/contact-form.html' %]
[% END %]
diff --git a/templates/web/zurich/admin/contact-form.html b/templates/web/zurich/admin/contact-form.html
index 236b169d0..064eba3f0 100644
--- a/templates/web/zurich/admin/contact-form.html
+++ b/templates/web/zurich/admin/contact-form.html
@@ -1,11 +1,24 @@
<form method="post" action="[% c.uri_for('body', body_id ) %]" enctype="application/x-www-form-urlencoded" accept-charset="utf-8" id="category_edit">
- <p><strong>[% loc('Category:') %] </strong>[% contact.category_display | html %]
+
+ [% IF contact.in_storage %]
+ <h1>[% contact.category_display | html %]</h1>
<input type="hidden" name="category" value="[% contact.category | html %]" >
- <input type="hidden" name="token" value="[% csrf_token %]" >
+ [% ELSE %]
+ <p>
+ <strong>[% loc('Category:') %] </strong>
+ <input type="text" class="form-control" name="category" size="30" value="[% contact.category | html %]" required>
+ </p>
+ [% END %]
+
+ <p>
+ <strong>Kürzel</strong>
+ <input type="text" class="form-control" name="extra[abbreviation]" id="abbreviation" size="30" value="[% contact.get_extra_metadata('abbreviation') | html %]">
+ </p>
<p><strong>[% loc('Email:') %] </strong>
<input type="text" class="form-control" name="email" value="[% contact.email | html %]" size="30">
+ [% IF contact.in_storage %]
<p>
<label for="state">[% loc('State') %]</label>
<select name="state" id="state">
@@ -15,9 +28,15 @@
<input type="checkbox" name="photo_required" value="1" id="photo_required"[% ' checked' IF contact.get_extra_metadata('photo_required') %]>
<label class="inline" for="photo_required">[% loc('Photo required') %]</label>
</p>
+ [% ELSE %]
+ <input type="hidden" name="confirmed" value="1" id="confirmed">
+ [% END %]
<p><strong>[% loc('Note:') %] </strong><textarea class="form-control" name="note" rows="3" cols="40"></textarea>
<input type="hidden" name="posted" value="new">
- <p><input type="submit" class="btn" name="Save changes" value="[% loc('Save changes') %]">
+ <input type="hidden" name="token" value="[% csrf_token %]" >
+ <p><input type="submit" class="btn" name="Create category" value="[% contact.in_storage ? loc('Save changes') : loc('Create category') %]">
+ </p>
+
</form>
diff --git a/templates/web/zurich/admin/header.html b/templates/web/zurich/admin/header.html
index 40847f190..91ac24faa 100644
--- a/templates/web/zurich/admin/header.html
+++ b/templates/web/zurich/admin/header.html
@@ -1,20 +1,6 @@
[%
SET bodyclass = bodyclass || 'fullwidthpage';
INCLUDE 'header.html' admin = 1, bodyclass = bodyclass _ ' admin';
-
- states = {
- 'unconfirmed' = loc('Submitted'),
- 'confirmed' = loc('Open'),
- 'in progress' = loc('In progress'),
- 'planned' = loc('Planned'),
- 'fixed - council' = loc('Closed'),
- 'hidden' = loc('Hidden'),
- 'closed' = loc('Extern'),
- 'partial' = loc('Not contactable'),
- 'investigating' = loc('Wish'),
- 'unable to fix' = loc('Jurisdiction unknown'),
- 'fixed - council' = loc('Closed'),
- }
%]
<style type="text/css">
.adminhidden { color: #666666; }
diff --git a/templates/web/zurich/admin/index-dm.html b/templates/web/zurich/admin/index-dm.html
index e0b62d5d2..4d77cf264 100644
--- a/templates/web/zurich/admin/index-dm.html
+++ b/templates/web/zurich/admin/index-dm.html
@@ -4,10 +4,10 @@
[% status_message %]
<h2 id="submitted">[% loc('Submitted') %]</h2>
-[% INCLUDE list, problems = unconfirmed.all, hash = 'submitted' %]
+[% INCLUDE list, problems = submitted.all, hash = 'submitted' %]
-<h2 id="planned">[% loc('Planned') %]</h2>
-[% INCLUDE list, problems = approval.all, hash = 'planned' %]
+<h2 id="feedback_pending">Rückmeldung ausstehend</h2>
+[% INCLUDE list, problems = approval.all, hash = 'feedback_pending' %]
<h2 id="alle">[% loc('All reports') %]</h2>
[% INCLUDE list, problems = other.all, include_subdiv = 1, hash = 'alle' %]
diff --git a/templates/web/zurich/admin/index-sdm.html b/templates/web/zurich/admin/index-sdm.html
index 707bb2d9d..68a2fcf62 100644
--- a/templates/web/zurich/admin/index-sdm.html
+++ b/templates/web/zurich/admin/index-sdm.html
@@ -5,7 +5,7 @@
[% INCLUDE list, problems = reports_new.all, hash = 'new' %]
<h2 id="wait">[% loc('Reports awaiting approval') %]</h2>
-[% INCLUDE list, problems = reports_unpublished.all, no_edit = 1, hash = 'wait' %]
+[% INCLUDE list, problems = reports_unpublished.all, hash = 'wait' %]
<h2 id="alle">[% loc('Reports published') %]</h2>
[% INCLUDE list, problems = reports_published.all, no_edit = 1, hash = 'alle' %]
diff --git a/templates/web/zurich/admin/index.html b/templates/web/zurich/admin/index.html
index fb3609bb3..62cd1a52c 100644
--- a/templates/web/zurich/admin/index.html
+++ b/templates/web/zurich/admin/index.html
@@ -9,8 +9,7 @@
<h2>[% loc('Problem breakdown by state') %]</h2>
<ul>
[% FOREACH state IN problems.keys.sort %]
- [% NEXT IF NOT states.$state %]
- <li>[% problems.$state %] [% states.$state %]</li>
+ <li>[% problems.$state %] [% prettify_state(state) %]</li>
[% END %]
</ul>
diff --git a/templates/web/zurich/admin/problem_row.html b/templates/web/zurich/admin/problem_row.html
index a83e22b27..973d9f651 100644
--- a/templates/web/zurich/admin/problem_row.html
+++ b/templates/web/zurich/admin/problem_row.html
@@ -18,9 +18,10 @@
<td>[% PROCESS value_or_nbsp value=problem.category_display %]</td>
<td>[% PROCESS format_date this_date=problem.created %]</td>
<td>[% PROCESS format_date this_date=problem.lastupdate %]</td>
- <td> [% states.${problem.state} %][% IF problem.state == 'planned';
+ <td>[% prettify_state(problem.state) %]
+ [% IF problem.state == 'feedback pending';
SET cs=problem.get_extra_metadata('closure_status');
- IF cs %] ([% states.$cs %]) [% END; END %]</td>
+ IF cs %] ([% prettify_state(cs) %]) [% END; END %]</td>
[% IF include_subdiv %]
<td>
diff --git a/templates/web/zurich/admin/report_edit-sdm.html b/templates/web/zurich/admin/report_edit-sdm.html
index 0319fc565..d07629d01 100644
--- a/templates/web/zurich/admin/report_edit-sdm.html
+++ b/templates/web/zurich/admin/report_edit-sdm.html
@@ -73,7 +73,7 @@
<dd>[% problem.category_display | html %]</dd>
<dt class="print-only">[% loc('State:') %] <!-- Status --></dt>
- <dd class="print-only">[% states.${problem.state} %]</dd>
+ <dd class="print-only">[% prettify_state(problem.state) %]</dd>
<dt>[% loc('Time spent (in minutes):') %]</dt>
<dd>[% problem.get_time_spent %]</dd>
@@ -101,28 +101,28 @@
<div class="admin-report-edit admin-report-edit--interact">
-<p align="right" class="screen-only"><input type="submit" class="btn" name="send_back" value="[% loc('Not for my subdivision') %]"></p>
-<p align="right" class="screen-only"><input type="submit" class="btn" name="not_contactable" value="[% loc('Customer not contactable') %]"></p>
+<p align="right" class="screen-only"><input [% sdm_disabled %] type="submit" class="btn" name="send_back" value="[% loc('Not for my subdivision') %]"></p>
+<p align="right" class="screen-only"><input [% sdm_disabled %] type="submit" class="btn" name="not_contactable" value="[% loc('Customer not contactable') %]"></p>
<ul class="no-bullets screen-only">
<li>
<label for="new_internal_note">[% loc('New internal note:') %]</label>
- <textarea class="form-control" name='new_internal_note' id='new_internal_note' cols=60 rows=4></textarea>
+ <textarea [% sdm_disabled %] class="form-control" name='new_internal_note' id='new_internal_note' cols=60 rows=4></textarea>
</li>
<li>
<label for="status_update">[% loc('New note to DM:') %]</label>
- <textarea class="form-control" name='status_update' id='status_update' cols=60 rows=4></textarea>
+ <textarea [% sdm_disabled %] class="form-control" name='status_update' id='status_update' cols=60 rows=4></textarea>
</li>
</ul>
<p class="screen-only">
<label for="time_spent">[% loc('Time spent (in minutes):') %]</label>
- <input type="text" class="form-control" name="time_spent" id="form_time_spent" style="width: 4em" value="0">
+ <input [% sdm_disabled %] type="text" class="form-control" name="time_spent" id="form_time_spent" style="width: 4em" value="0">
</p>
<p class="clearfix screen-only">
- <input style="float:left" type="submit" class="btn" name="Submit changes" value="[% loc('Submit changes') %]" >
- <input style="float:right" type="submit" class="btn" name="no_more_updates" value="[% loc('No further updates') %]">
+ <input [% sdm_disabled %] style="float:left" type="submit" class="btn" name="Submit changes" value="[% loc('Submit changes') %]" >
+ <input [% sdm_disabled %] style="float:right" type="submit" class="btn" name="no_more_updates" value="[% loc('No further updates') %]">
</p>
[% INCLUDE 'admin/list_updates.html' %]
diff --git a/templates/web/zurich/admin/report_edit.html b/templates/web/zurich/admin/report_edit.html
index fd03fb044..ad14b2d45 100644
--- a/templates/web/zurich/admin/report_edit.html
+++ b/templates/web/zurich/admin/report_edit.html
@@ -27,7 +27,7 @@
<dd class="screen-only">&raquo; <a href="http://webgis.intra.stzh.ch/stapo/GoogleStreetView.asp?lat=[% problem.latitude %]&amp;lon=[% problem.longitude %]" target="_blank">[% loc('Street View') %]</a></dd>
- [% IF c.cobrand.problem_is_closed(problem) %]
+ [% IF problem.is_fixed || problem.is_closed || problem.is_hidden %]
<dt><span class="mock-label">[% loc('Details:') %]</span></dt>
<dd>[% problem.detail | html %]</dd>
[% IF problem.extra.original_detail %]
@@ -101,7 +101,7 @@
<dd>[% problem.category_display | html %]</dd>
<dt class="print-only">[% loc('State:') %] <!-- Status --></dt>
- <dd class="print-only">[% states_trans.${problem.state} %]</dd>
+ <dd class="print-only">[% prettify_state(problem.state) %]</dd>
<dt>[% loc('Time spent (in minutes):') %]</dt>
<dd>[% problem.get_time_spent %]</dd>
@@ -154,7 +154,8 @@
<select class="form-control" name="state" id="state" data-pstate="[% pstate %]">
<option value="">--</option>
[% FOREACH s IN states %]
- <option [% 'selected ' IF s.state == pstate %] value="[% s.state %]">[% s.trans %]</option>
+ <option [% 'selected ' IF s.state == pstate %] value="[% s.state %]">
+ [% IF s.trans; s.trans; ELSE; prettify_state(s.state); END %]</option>
[% END %]
</select>
</dd>
@@ -170,7 +171,8 @@
<select class="form-control" name="category" id="category">
<option value="">--</option>
[% FOREACH cat IN category_options %]
- <option value="[% cat.name %]">[% cat.value %]</option>
+ <option value="[% cat.name %]">[% cat.value ~%]
+ [% ' (' _ cat.abbreviation _ ')' IF cat.abbreviation %]</option>
[% END %]
</select>
</div>
@@ -195,15 +197,15 @@
</ul>
-[% IF problem.state == 'planned' %]
+[% IF problem.state == 'feedback pending' %]
[%# 3rd party messages sent for Extern/Wunsch states %]
[% SWITCH pstate %]
- [% CASE ['closed','investigating'] %]
+ [% CASE ['external','wish'] %]
<ul class="no-bullets screen-only">
<li class="assignation" id="assignation__external">
<span class="error hidden">[% loc('Please select a body.') %]</span>
<label for="body_external">
- [% IF pstate == 'closed' %]
+ [% IF pstate == 'external' %]
[% loc('Assign to external body:') %]
[% ELSE %]
[% loc('Assign to competent body:') %]
@@ -218,7 +220,7 @@
</select>
</li>
<li>
- [% IF pstate == 'closed' %]
+ [% IF pstate == 'external' %]
<input type="checkbox" name="third_personal" id="third_personal" value="1"[% ' checked' IF problem.extra.third_personal %]>
<label for="third_personal" class="inline">[% loc('Include reporter personal details') %]</label>
[% END %]
@@ -234,12 +236,11 @@
# (e.g. various pstates) %]
<ul class="no-bullets screen-only">
<li id="status_update_container"><label for="status_update">
- [% SWITCH pstate %]
- [% CASE ['hidden', 'investigating', 'partial'] %][%# Hidden/Wish/Not contactable %]
- [% loc('Reply to user:') %]
- [% CASE DEFAULT %]
- [% loc('Public response:') %]
- [% END %]
+ [% IF c.cobrand.zurich_user_response_states.$pstate %]
+ [% loc('Reply to user:') %]
+ [% ELSE %]
+ [% loc('Public response:') %]
+ [% END %]
</label>
[% INCLUDE 'admin/response_templates_select.html' for='status_update' %]
<textarea class="form-control" name='status_update' id='status_update' cols=60 rows=5>
@@ -252,7 +253,7 @@
<p align="right" class="screen-only">
[% IF show_publish_response %]
[%# While we call this 'publish_response', the response will not actually
- # be "published" for these cases: Wish / Hidden / Not contactable (for these,
+ # be "published" for these cases: Hidden / Not contactable (for these,
# only a private email will be sent to the user. However, in all cases,
# this is the end of processing, so we mark this with the same text used
# for 'No further updates %]
@@ -272,9 +273,9 @@
[% END %]
[% SWITCH problem.state %]
- [% CASE ['closed','investigating'] %]
+ [% CASE ['external','wish'] %]
<h2>
- [% IF problem.state == 'closed' %]
+ [% IF problem.state == 'external' %]
[% loc('Message to external body:') %]
[% ELSE %]
[% loc('Message to competent body:') %]
diff --git a/templates/web/zurich/admin/stats/index.html b/templates/web/zurich/admin/stats/index.html
index ce8e238f7..9f2906d0a 100644
--- a/templates/web/zurich/admin/stats/index.html
+++ b/templates/web/zurich/admin/stats/index.html
@@ -2,14 +2,12 @@
[% PROCESS 'admin/report_blocks.html' %]
[% USE date %]
-<p style="float:right"><a href="[% c.uri_with( { export=1 } ) %]">[% loc('All Reports as CSV') %]</a></p>
+<form method="get" action="[% c.uri_for('/admin/stats') %]">
+<div class="filters">
-[% IF start_date AND end_date %]
-<p><strong>[% tprintf( loc( 'All reports between %s and %s' ), start_date.ymd, end_date.ymd ) | html %]</strong></p>
-[% END %]
-
-<form method="get" action="[% c.uri_for('stats') %]">
-<p><select class="form-control" name="ym">
+<p>
+ <label for="ym">[% loc('Month') %]</label>
+ <select class="form-control" name="ym" id="ym">
<option value="">[% loc('All reports') %]</option>
[% FOR y IN [ 2013 .. date.format(date.now, '%Y') ];
SET max = 12;
@@ -21,11 +19,34 @@
<option[% ' selected' IF v == ym %]>[% v %]</option>
[% END %]
[% END %]
-</select>
+ </select>
+</p>
+
+<p>
+ <label for="category">[% loc('Category') %]</label>
+ <select class="form-control" name="category" id="category">
+ <option value="">--</option>
+ [% FOREACH cat IN category_options %]
+ <option value="[% cat.name %]"[% ' selected' IF cat.name == category %]>[% cat.value ~%]
+ [% ' (' _ cat.abbreviation _ ')' IF cat.abbreviation %]</option>
+ [% END %]
+ </select>
+</p>
+
+<p class="no-label">
+ <input type="submit" class="btn" value="[% loc('Go') %]">
+</p>
-<input type="submit" class="btn" value="[% loc('Go') %]">
+</div>
</form>
+<ul class="dashboard-options-tabs">
+ [% IF start_date AND end_date %]
+ <li><strong>[% tprintf( loc( 'All reports between %s and %s' ), start_date.ymd, end_date.ymd ) | html %]</strong></li>
+ [% END %]
+ <li class="pull-right"><a href="[% c.uri_with( { export=1 } ) %]">[% loc('All Reports as CSV') %]</a></li>
+</ul>
+
<ul>
<li>[% loc('Total') %]: [% reports_total || 0 %]
<li>[% loc('Closed') %]: [% reports_solved || 0 %]
diff --git a/templates/web/zurich/report/_item.html b/templates/web/zurich/report/_item.html
index ccae84271..f20b2021d 100644
--- a/templates/web/zurich/report/_item.html
+++ b/templates/web/zurich/report/_item.html
@@ -1,11 +1,11 @@
<li class="item-list__item item-list--reports__item">
<a href="[% c.uri_for('/report', problem.id ) %]">
[% photo_to_display = c.cobrand.allow_photo_display(problem) %]
- [% IF problem.state != 'unconfirmed' AND problem.photo AND photo_to_display %]
+ [% IF problem.state != 'submitted' AND problem.photo AND photo_to_display %]
[% photo_idx = photo_to_display - 1 ~%]
<img class="img" height="60" width="90" src="[% problem.photos.${photo_idx}.url_fp %]" alt="">
[% END %]
- [% IF problem.state != 'unconfirmed' %]
+ [% IF problem.state != 'submitted' %]
<h3 class="item-list__heading">[% problem.title | html %]</h3>
[% ELSE %]
<h3 class="item-list__heading"><em>[% loc('Awaiting moderation') %]</em></h3>
diff --git a/templates/web/zurich/report/_main.html b/templates/web/zurich/report/_main.html
index 912e8b0c9..c9225b4aa 100644
--- a/templates/web/zurich/report/_main.html
+++ b/templates/web/zurich/report/_main.html
@@ -5,7 +5,7 @@
[%- IF !problem.used_map %]<br>[% loc('there is no pin shown as the user did not use the map') %][% END %]
</p>
- [% IF problem.state != 'unconfirmed' %]
+ [% IF problem.state != 'submitted' %]
[% INCLUDE 'report/photo.html' object=problem %]
[% problem.detail | add_links | html_para %]
[% ELSE %]
diff --git a/templates/web/zurich/report/updates.html b/templates/web/zurich/report/updates.html
index 4489fea34..8a09d0bc7 100644
--- a/templates/web/zurich/report/updates.html
+++ b/templates/web/zurich/report/updates.html
@@ -1,15 +1,11 @@
-[% IF problem.state == 'fixed - council' OR problem.state == 'closed' %]
+[% IF c.cobrand.problem_has_public_response(problem) %]
<h4 class="static-with-rule">[% loc('Updates') %]</h4>
<ul class="item-list item-list--updates">
<li class="item-list__item item-list__item--updates">
<div class="item-list__update-wrap">
<div class="item-list__update-text">
<p class="meta-2">[% prettify_dt( problem.lastupdate, 'zurich' ) %]</p>
- [%# XXX following should honour zurich_closed_states instead? %]
- [% IF problem.state == 'fixed - council'
- || ( problem.external_body AND problem.state == 'closed' ) %]
[% problem.extra.public_response | add_links | html_para %]
- [% END %]
</div>
</div>
</li>
diff --git a/web/cobrands/zurich/base.scss b/web/cobrands/zurich/base.scss
index 101118a02..f526a8f1e 100644
--- a/web/cobrands/zurich/base.scss
+++ b/web/cobrands/zurich/base.scss
@@ -224,9 +224,9 @@ h4.static-with-rule {
margin-right: 0.2em;
}
- table tr.is-deleted {
- background-color: transparent;
- }
+}
+table#admin_bodies tr.is-deleted {
+ background-color: transparent;
}
.admin-label--inline {
diff --git a/web/cobrands/zurich/js.js b/web/cobrands/zurich/js.js
index d2909bb6d..8b30436d5 100644
--- a/web/cobrands/zurich/js.js
+++ b/web/cobrands/zurich/js.js
@@ -77,7 +77,7 @@ $(function() {
$('#assignation__category').show();
$('#assignation__subdivision').show();
}
- if ((state === 'closed') || (state === 'investigating')) {
+ if ((state === 'external') || (state === 'wish')) {
$('#assignation__external').show();
} else {
$('#assignation__external').hide();
diff --git a/web/cobrands/zurich/layout.scss b/web/cobrands/zurich/layout.scss
index d49f400e6..31469926f 100644
--- a/web/cobrands/zurich/layout.scss
+++ b/web/cobrands/zurich/layout.scss
@@ -283,6 +283,10 @@ body.mappage.admin {
}
}
+.admin-report-edit--interact {
+ margin-top: -1.25em; // To counteract the label's default top margin
+}
+
.admin {
.content {
margin: 2em 0 1em;
@@ -300,10 +304,6 @@ body.mappage.admin {
font-weight: bold;
padding-right: 0.333em;
}
- :first-child > label {
- // avoid empty space above first label in a container
- margin-top: 0;
- }
textarea {
min-height: 0;
}