aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHakim Cassimally <hakim@mysociety.org>2015-02-19 16:41:22 +0000
committerDave Arter <davea@mysociety.org>2015-10-06 09:09:23 +0100
commit735536dc5e269f2094d122e49f2c648928db4acb (patch)
tree5779c470d6f326828f0f587f76bfc0841c03144f
parentd004b2df0c85615eab6924c35f478b45bdf84b2d (diff)
[Zurich] Response templates for report admin.
This provides a ResponseTemplate model that an admin/staff user can choose from when responding to problems. For mysociety/FixMyStreet-Commercial#671.
-rwxr-xr-xbin/update-schema1
-rw-r--r--db/downgrade_0037---0036.sql1
-rw-r--r--db/schema.sql9
-rw-r--r--db/schema_0037-response-templates.sql8
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm79
-rw-r--r--perllib/FixMyStreet/App/View/Web.pm3
-rw-r--r--perllib/FixMyStreet/Cobrand/Zurich.pm4
-rw-r--r--perllib/FixMyStreet/DB/Result/Body.pm22
-rw-r--r--perllib/FixMyStreet/DB/Result/Problem.pm23
-rw-r--r--perllib/FixMyStreet/DB/Result/ResponseTemplate.pm49
-rw-r--r--templates/web/zurich/admin/body-form.html1
-rw-r--r--templates/web/zurich/admin/header.html1
-rw-r--r--templates/web/zurich/admin/report_edit.html2
-rw-r--r--templates/web/zurich/admin/response_templates_select.html25
-rw-r--r--templates/web/zurich/admin/template_edit.html38
-rw-r--r--templates/web/zurich/admin/templates.html28
-rw-r--r--templates/web/zurich/header.html5
-rw-r--r--web/cobrands/zurich/layout.scss12
18 files changed, 300 insertions, 11 deletions
diff --git a/bin/update-schema b/bin/update-schema
index d40df1689..d98fe6e9c 100755
--- a/bin/update-schema
+++ b/bin/update-schema
@@ -195,6 +195,7 @@ else {
# By querying the database schema, we can see where we're currently at
# (assuming schema change files are never half-applied, which should be the case)
sub get_db_version {
+ return '0037' if table_exists('response_templates');
return '0036' if constraint_contains('problem_cobrand_check', 'a-z0-9_');
return '0035' if column_exists('problem', 'bodies_missing');
return '0034' if ! function_exists('ms_current_timestamp');
diff --git a/db/downgrade_0037---0036.sql b/db/downgrade_0037---0036.sql
new file mode 100644
index 000000000..39f73b308
--- /dev/null
+++ b/db/downgrade_0037---0036.sql
@@ -0,0 +1 @@
+drop table response_templates;
diff --git a/db/schema.sql b/db/schema.sql
index 2db89bddb..e87a2aafe 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -457,3 +457,12 @@ create table user_body_permissions (
),
unique(user_id, body_id, permission_type)
);
+
+create table response_templates (
+ id serial not null primary key,
+ body_id int references body(id) not null,
+ title text not null,
+ text text not null,
+ created timestamp not null default current_timestamp,
+ unique(body_id, title)
+);
diff --git a/db/schema_0037-response-templates.sql b/db/schema_0037-response-templates.sql
new file mode 100644
index 000000000..4534e1e84
--- /dev/null
+++ b/db/schema_0037-response-templates.sql
@@ -0,0 +1,8 @@
+create table response_templates (
+ id serial not null primary key,
+ body_id int references body(id) not null,
+ title text not null,
+ text text not null,
+ created timestamp not null default current_timestamp,
+ unique(body_id, title)
+);
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm
index 7ead7db16..be705110b 100644
--- a/perllib/FixMyStreet/App/Controller/Admin.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin.pm
@@ -819,6 +819,85 @@ sub report_edit : Path('report_edit') : Args(1) {
return 1;
}
+sub templates : Path('templates') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->detach( '/page_error_404_not_found' )
+ unless $c->cobrand->moniker eq 'zurich';
+
+ my $user = $c->user;
+
+ $self->templates_for_body($c, $user->from_body );
+}
+
+sub templates_view : Path('templates') : Args(1) {
+ my ($self, $c, $body_id) = @_;
+
+ $c->detach( '/page_error_404_not_found' )
+ unless $c->cobrand->moniker eq 'zurich';
+
+ # e.g. for admin
+
+ my $body = $c->model('DB::Body')->find($body_id)
+ or $c->detach( '/page_error_404_not_found' );
+
+ $self->templates_for_body($c, $body);
+}
+
+sub template_edit : Path('templates') : Args(2) {
+ my ( $self, $c, $body_id, $template_id ) = @_;
+
+ $c->detach( '/page_error_404_not_found' )
+ unless $c->cobrand->moniker eq 'zurich';
+
+ my $body = $c->model('DB::Body')->find($body_id)
+ or $c->detach( '/page_error_404_not_found' );
+ $c->stash->{body} = $body;
+
+ my $template;
+ if ($template_id eq 'new') {
+ $template = $body->response_templates->new({});
+ }
+ else {
+ $template = $body->response_templates->find( $template_id )
+ or $c->detach( '/page_error_404_not_found' );
+ }
+
+ if ($c->req->method eq 'POST') {
+ if ($c->req->param('delete_template') eq _("Delete template")) {
+ $template->delete;
+ } else {
+ $template->title( $c->req->param('title') );
+ $template->text ( $c->req->param('text') );
+ $template->update_or_insert;
+ }
+
+ $c->res->redirect( $c->uri_for( 'templates', $body->id ) );
+ }
+
+ $c->stash->{response_template} = $template;
+
+ $c->stash->{template} = 'admin/template_edit.html';
+}
+
+
+sub templates_for_body {
+ my ( $self, $c, $body ) = @_;
+
+ $c->stash->{body} = $body;
+
+ my @templates = $body->response_templates->search(
+ undef,
+ {
+ order_by => 'title'
+ }
+ );
+
+ $c->stash->{response_templates} = \@templates;
+
+ $c->stash->{template} = 'admin/templates.html';
+}
+
sub users: Path('users') : Args(0) {
my ( $self, $c ) = @_;
diff --git a/perllib/FixMyStreet/App/View/Web.pm b/perllib/FixMyStreet/App/View/Web.pm
index 9cc571efc..37a81e444 100644
--- a/perllib/FixMyStreet/App/View/Web.pm
+++ b/perllib/FixMyStreet/App/View/Web.pm
@@ -133,6 +133,9 @@ sub escape_js {
'>' => 'u003e',
);
$text =~ s/([\\"'<>])/\\$lookup{$1}/g;
+
+ $text =~ s/(?:\r\n|\n|\r)/\\n/g; # replace newlines
+
return $text;
}
diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm
index 3c1baaa48..46d57158b 100644
--- a/perllib/FixMyStreet/Cobrand/Zurich.pm
+++ b/perllib/FixMyStreet/Cobrand/Zurich.pm
@@ -9,6 +9,7 @@ use Scalar::Util 'blessed';
use strict;
use warnings;
use feature 'say';
+use utf8;
=head1 NAME
@@ -303,6 +304,7 @@ sub admin_pages {
$pages = { %$pages,
'bodies' => [_('Bodies'), 1],
'body' => [undef, undef],
+ 'templates' => [_('Templates'), 2],
};
return $pages if $type eq 'dm';
@@ -450,7 +452,7 @@ sub admin_report_edit {
}
- # If super or sdm check that the token is correct before proceeding
+ # If super or dm check that the token is correct before proceeding
if ( ($type eq 'super' || $type eq 'dm') && $c->get_param('submit') ) {
$c->forward('check_token');
}
diff --git a/perllib/FixMyStreet/DB/Result/Body.pm b/perllib/FixMyStreet/DB/Result/Body.pm
index 0c1046cd8..a2e004c6a 100644
--- a/perllib/FixMyStreet/DB/Result/Body.pm
+++ b/perllib/FixMyStreet/DB/Result/Body.pm
@@ -18,12 +18,6 @@ __PACKAGE__->add_columns(
is_nullable => 0,
sequence => "body_id_seq",
},
- "name",
- { data_type => "text", is_nullable => 0 },
- "external_url",
- { data_type => "text", is_nullable => 1 },
- "parent",
- { data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
"endpoint",
{ data_type => "text", is_nullable => 1 },
"jurisdiction",
@@ -42,8 +36,14 @@ __PACKAGE__->add_columns(
{ data_type => "boolean", default_value => \"false", is_nullable => 0 },
"send_extended_statuses",
{ data_type => "boolean", default_value => \"false", is_nullable => 0 },
+ "name",
+ { data_type => "text", is_nullable => 0 },
+ "parent",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
"deleted",
{ data_type => "boolean", default_value => \"false", is_nullable => 0 },
+ "external_url",
+ { data_type => "text", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->has_many(
@@ -87,6 +87,12 @@ __PACKAGE__->belongs_to(
},
);
__PACKAGE__->has_many(
+ "response_templates",
+ "FixMyStreet::DB::Result::ResponseTemplate",
+ { "foreign.body_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+__PACKAGE__->has_many(
"user_body_permissions",
"FixMyStreet::DB::Result::UserBodyPermission",
{ "foreign.body_id" => "self.id" },
@@ -100,8 +106,8 @@ __PACKAGE__->has_many(
);
-# Created by DBIx::Class::Schema::Loader v0.07035 @ 2014-07-29 13:54:07
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:PhUeFDRLSQVMk7Sts5K6MQ
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2015-02-19 16:13:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:d6GuQm8vrNmCc4NWw58srA
sub url {
my ( $self, $c, $args ) = @_;
diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm
index 635c41382..d3a30db4e 100644
--- a/perllib/FixMyStreet/DB/Result/Problem.pm
+++ b/perllib/FixMyStreet/DB/Result/Problem.pm
@@ -643,7 +643,28 @@ sub body {
return $body;
}
-# returns true if the external id is the council's ref, i.e., useful to publish it.
+=head2 response_templates
+
+Returns all ResponseTemplates attached to this problem's bodies, in alphabetical
+order of title.
+
+=cut
+
+sub response_templates {
+ my $problem = shift;
+ return FixMyStreet::App->model('DB::ResponseTemplate')->search(
+ {
+ body_id => $problem->bodies_str_ids
+ },
+ {
+ order_by => 'title'
+ }
+ );
+}
+
+# returns true if the external id is the council's ref, i.e., useful to publish it
+# (by way of an example, the barnet send method returns a useful reference when
+# it succeeds, so that is the ref we should show on the problem report page).
# Future: this is installation-dependent so maybe should be using the contact
# data to determine if the external id is public on a council-by-council basis.
# Note: this only makes sense when called on a problem that has been sent!
diff --git a/perllib/FixMyStreet/DB/Result/ResponseTemplate.pm b/perllib/FixMyStreet/DB/Result/ResponseTemplate.pm
new file mode 100644
index 000000000..48a1ab3ae
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/ResponseTemplate.pm
@@ -0,0 +1,49 @@
+use utf8;
+package FixMyStreet::DB::Result::ResponseTemplate;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime", "EncodedColumn");
+__PACKAGE__->table("response_templates");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "response_templates_id_seq",
+ },
+ "body_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+ "title",
+ { data_type => "text", is_nullable => 0 },
+ "text",
+ { data_type => "text", is_nullable => 0 },
+ "created",
+ {
+ data_type => "timestamp",
+ default_value => \"current_timestamp",
+ is_nullable => 0,
+ },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->add_unique_constraint("response_templates_body_id_title_key", ["body_id", "title"]);
+__PACKAGE__->belongs_to(
+ "body",
+ "FixMyStreet::DB::Result::Body",
+ { id => "body_id" },
+ { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2015-02-19 16:13:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:xzhmxtu0taAnBMZN0HBocw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/templates/web/zurich/admin/body-form.html b/templates/web/zurich/admin/body-form.html
index a31dffe7c..ac2887159 100644
--- a/templates/web/zurich/admin/body-form.html
+++ b/templates/web/zurich/admin/body-form.html
@@ -48,6 +48,7 @@
<p>
<input type="hidden" name="posted" value="body">
<input type="hidden" name="token" value="[% token %]">
+ <p>
<input type="submit" value="[% body ? loc('Update body') : loc('Add body') %]">
</p>
</form>
diff --git a/templates/web/zurich/admin/header.html b/templates/web/zurich/admin/header.html
index 281b1de23..a7ae7fb56 100644
--- a/templates/web/zurich/admin/header.html
+++ b/templates/web/zurich/admin/header.html
@@ -19,6 +19,7 @@
.error { color: red; }
.overdue { background-color: #ffcccc; }
select { width: auto; }
+ .admin-report-edit select { max-width: 100%; }
#fms_pan_zoom { top: 13em !important; }
</style>
<script>
diff --git a/templates/web/zurich/admin/report_edit.html b/templates/web/zurich/admin/report_edit.html
index 02c396e1f..d2c1760ff 100644
--- a/templates/web/zurich/admin/report_edit.html
+++ b/templates/web/zurich/admin/report_edit.html
@@ -194,7 +194,7 @@ $(function(){
[% END %]
<li><label for="status_update">[% loc('Public response:') %]</label>
-
+ [% INCLUDE 'admin/response_templates_select.html' for='status_update' %]
<textarea name='status_update' id='status_update' cols=60 rows=5>
[%- IF problem.extra.public_response -%]
[%- problem.extra.public_response | html -%]
diff --git a/templates/web/zurich/admin/response_templates_select.html b/templates/web/zurich/admin/response_templates_select.html
new file mode 100644
index 000000000..a16032790
--- /dev/null
+++ b/templates/web/zurich/admin/response_templates_select.html
@@ -0,0 +1,25 @@
+[% template_name="templates_for_${for}" %]
+
+[% response_templates = problem.response_templates %]
+<div class="response_templates_select">
+<select id="[% template_name %]">
+ <option value="">Choose a template</option>
+[% FOR t IN response_templates %]
+ <option value="[% t.id %]"> [% t.title | html %] </option>
+[% END %]
+</select>
+</p>
+<script>
+ $(function () {
+ var response_template_texts = {
+ [% FOR t IN response_templates %]
+ [% t.id %]: '[% t.text | escape_js %]' [% loop.last ? '' : ',' %]
+ [% END %]
+ };
+ $('#[% template_name %]').change(function () {
+ var val = $(this).val();
+ var text = response_template_texts[val];
+ $('#[% for %]').val( text );
+ });
+ });
+</script>
diff --git a/templates/web/zurich/admin/template_edit.html b/templates/web/zurich/admin/template_edit.html
new file mode 100644
index 000000000..e3e4fe190
--- /dev/null
+++ b/templates/web/zurich/admin/template_edit.html
@@ -0,0 +1,38 @@
+[% INCLUDE 'admin/header.html' title=tprintf(loc('Response Templates for %s'), body.name) -%]
+[% rt = response_template %]
+
+<h2> [% tprintf(loc('Response Templates for %s'), body.name) %] </h2>
+
+<h3> [% IF rt.id %]
+ Template &laquo;[% rt.title %]&raquo;
+ [% ELSE %]
+ New template
+ [% END %]
+</h3>
+
+<form method="post"
+ action="[% c.uri_for('templates', body.id, rt.id || 'new' ) %]"
+ enctype="application/x-www-form-urlencoded"
+ accept-charset="utf-8"
+ class="validate">
+
+ <p>
+ <strong>[% loc('Title:') %] </strong>
+ <input type="text" name="title" class="required" size="30" value="[% rt.title| html %]">
+ </p>
+ <p>
+ <strong>[% loc('Text:') %] </strong>
+ <textarea name="text" class="required">[% rt.text |html %]</textarea>
+ </p>
+ <p>
+ <input type="hidden" name="token" value="[% token %]" >
+ <input type="submit" name="Edit templates" value="[% rt.id ? loc('Save changes') : loc('Create template') %]" >
+ </p>
+ [% IF rt.id %]
+ <p>
+ <input class="delete" type="submit" name="delete_template" value="[% loc('Delete template') %]">
+ </p>
+ [% END %]
+</form>
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/zurich/admin/templates.html b/templates/web/zurich/admin/templates.html
new file mode 100644
index 000000000..d3b334022
--- /dev/null
+++ b/templates/web/zurich/admin/templates.html
@@ -0,0 +1,28 @@
+[% INCLUDE 'admin/header.html' title=tprintf(loc('Response Templates for %s'), body.name) -%]
+
+<h2> [% tprintf(loc('Response Templates for %s'), body.name) %] </h2>
+
+<table>
+ <thead>
+ <tr>
+ <th> [% loc('Title') %] </th>
+ <th> [% loc('Text') %] </th>
+ <th> [% loc('Created') %] </th>
+ <th> &nbsp; </th>
+ </tr>
+ </thead>
+ <tbody>
+[% FOR t IN response_templates %]
+ <tr>
+ <td> [% t.title %] </td>
+ <td> [% t.text %] </td>
+ <td> [% t.created %] </td>
+ <td> <a href="/admin/templates/[% body.id %]/[% t.id %]" class="btn">[% loc('Edit') %]</a> </td>
+ </tr>
+[% END %]
+ </tbody>
+</table>
+
+<a href="[% c.uri_for('templates', body.id, 'new') %]" class="btn">[% loc('New template') %]</a>
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/zurich/header.html b/templates/web/zurich/header.html
index 78ed678f6..7e88f3f0f 100644
--- a/templates/web/zurich/header.html
+++ b/templates/web/zurich/header.html
@@ -72,6 +72,11 @@
<li [% IF pagename == 'stats' %]class="current"[% END %]>
<a href="/admin/stats">[% loc('Stats') %]</a>
</li>
+ [% IF admin_type == 'dm' %]
+ <li [% IF pagename == 'templates' OR pagename == 'template' %]class="current"[% END %]>
+ <a href="/admin/templates">[% loc('Templates') %]</a>
+ </li>
+ [% END %]
<li class="search-box">
<form method="get" action="[% c.uri_for('reports') %]" enctype="application/x-www-form-urlencoded" accept-charset="utf-8">
<input type="text" name="search" size="20" id="search" placeholder="[% loc('Search reports') %]">
diff --git a/web/cobrands/zurich/layout.scss b/web/cobrands/zurich/layout.scss
index d0b24b49c..c753e2081 100644
--- a/web/cobrands/zurich/layout.scss
+++ b/web/cobrands/zurich/layout.scss
@@ -262,6 +262,18 @@ body.mappage.admin .content {
}
}
+ button, input[type=submit], .btn {
+ &.delete {
+ font-size: 0.75em;
+ color: #933;
+ margin: 2em 0;
+
+ &:hover {
+ @include background(linear-gradient(#fcc, #daa 50%));
+ };
+ }
+ }
+
#zurich-footer {
margin: 2em auto 3em auto;
}