Skip to content

Commit

Permalink
Add manifest icon management to admin forms
Browse files Browse the repository at this point in the history
  • Loading branch information
davea committed Jan 30, 2020
1 parent 59e1289 commit 4ede088
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 11 deletions.
21 changes: 19 additions & 2 deletions perllib/FixMyStreet/App/Controller/Admin/ManifestTheme.pm
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,14 @@ sub item :PathPart('admin/manifesttheme') :Chained :CaptureArgs(1) {

sub edit :PathPart('') :Chained('item') :Args(0) {
my ($self, $c) = @_;
return $self->form($c, $c->stash->{obj});

my $form = $self->form($c, $c->stash->{obj});

# We need to do this after form processing, in case a form POST has deleted
# an icon.
$c->forward('/offline/_stash_manifest_icons', [ $c->stash->{obj}->cobrand, 1 ]);

return $form;
}


Expand All @@ -59,6 +66,7 @@ sub form {
my ($self, $c, $theme) = @_;

if ($c->get_param('delete_theme')) {
$c->forward('_delete_all_manifest_icons');
$theme->delete;
$c->forward('/admin/log_edit', [ $theme->id, 'manifesttheme', 'delete' ]);
$c->response->redirect($c->uri_for($self->action_for('index')));
Expand All @@ -68,13 +76,22 @@ sub form {
my $action = $theme->in_storage ? 'edit' : 'add';
my $form = FixMyStreet::App::Form::ManifestTheme->new( cobrand => $c->cobrand->moniker );
$c->stash(template => 'admin/manifesttheme/form.html', form => $form);
$form->process(item => $theme, params => $c->req->params);
my $params = $c->req->params;
$params->{icon} = $c->req->upload('icon') if $params->{icon};
$form->process(item => $theme, params => $params);
return unless $form->validated;

$c->forward('/admin/log_edit', [ $theme->id, 'manifesttheme', $action ]);
$c->response->redirect($c->uri_for($self->action_for('index')));
}

sub _delete_all_manifest_icons :Private {
my ($self, $c) = @_;

$c->forward('/offline/_stash_manifest_icons', [ $c->stash->{obj}->cobrand, 1 ]);
foreach my $icon ( @{ $c->stash->{manifest_icons} } ) {
unlink FixMyStreet->path_to('web', $icon->{src});
}
}

1;
38 changes: 38 additions & 0 deletions perllib/FixMyStreet/App/Form/ManifestTheme.pm
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package FixMyStreet::App::Form::ManifestTheme;

use Path::Tiny;
use File::Copy;
use Digest::SHA qw(sha1_hex);
use File::Basename;
use HTML::FormHandler::Moose;
use FixMyStreet::App::Form::I18N;
extends 'HTML::FormHandler::Model::DBIC';
Expand All @@ -15,6 +19,8 @@ has_field 'name' => ( required => 1 );
has_field 'short_name' => ( required => 1 );
has_field 'background_colour' => ( required => 0 );
has_field 'theme_colour' => ( required => 0 );
has_field 'icon' => ( required => 0, type => 'Upload', label => "Add icon" );
has_field 'delete_icon' => ( type => 'Multiple' );

before 'update_model' => sub {
my $self = shift;
Expand All @@ -23,6 +29,38 @@ before 'update_model' => sub {

sub _build_language_handle { FixMyStreet::App::Form::I18N->new }

sub validate {
my $self = shift;

my $value = $self->value;
my $cobrand = $value->{cobrand} || $self->cobrand;
my $upload = $value->{icon};

if ( $upload ) {
if( $upload->type !~ /^image/ ) {
$self->field('icon')->add_error( _("File type not recognised. Please upload an image.") );
return;
}

my $uri = '/theme/' . $cobrand;
my $theme_path = path(FixMyStreet->path_to('web' . $uri));
FixMyStreet::PhotoStorage::base64_decode_upload(undef, $upload);
my ($p, $n, $ext) = fileparse($upload->filename, qr/\.[^.]*/);
my $key = sha1_hex($upload->slurp) . $ext;
my $out = path($theme_path, $key);
unless (copy($upload->tempname, $out)) {
$self->field('icon')->add_error( _("Sorry, we couldn't save your file(s), please try again.") );
return;
}
}

foreach my $delete_icon ( @{ $value->{delete_icon} } ) {
unlink FixMyStreet->path_to('web', $delete_icon);
}

return 1;
}

__PACKAGE__->meta->make_immutable;

1;
6 changes: 4 additions & 2 deletions perllib/FixMyStreet/PhotoStorage.pm
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ sub base64_decode_upload {
print $fh $decoded;
close $fh
} else {
$c->log->info('Couldn\'t open temp file to save base64 decoded image: ' . $!);
$c->stash->{photo_error} = _("Sorry, we couldn't save your file(s), please try again.");
if ($c) {
$c->log->info('Couldn\'t open temp file to save base64 decoded image: ' . $!);
$c->stash->{photo_error} = _("Sorry, we couldn't save your file(s), please try again.");
}
return ();
}
}
Expand Down
103 changes: 98 additions & 5 deletions t/app/controller/admin/manifesttheme.t
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use FixMyStreet::TestMech;
use Path::Tiny;
use FixMyStreet::DB;
use FixMyStreet::TestMech;

my $mech = FixMyStreet::TestMech->new;

Expand Down Expand Up @@ -70,21 +71,92 @@ subtest "cobrand admin lets you create a new theme" => sub {
is $log->action, "add";
is $log->object_summary, "lincolnshire";
is $log->link, "/admin/manifesttheme/lincolnshire";
};

$fields = {
subtest "cobrand admin lets you update an existing theme" => sub {
$mech->get_ok("/admin/manifesttheme/lincolnshire");

my $fields = {
background_colour => "#663399",
theme_colour => "rgb(102, 51, 153)",
};
$mech->submit_form_ok( { with_fields => $fields } );
$theme->discard_changes;

my $theme = FixMyStreet::DB->resultset('ManifestTheme')->find({ cobrand => 'lincolnshire' });
is $theme->background_colour, "#663399";
is $theme->theme_colour, "rgb(102, 51, 153)";

$log = $superuser->admin_logs->search({}, { order_by => { -desc => 'id' } })->first;
my $log = $superuser->admin_logs->search({}, { order_by => { -desc => 'id' } })->first;
is $log->object_id, $theme->id;
is $log->action, "edit";
};

subtest "cobrand admin lets you add an icon to an existing theme" => sub {
$mech->get_ok("/admin/manifesttheme/lincolnshire");

my $sample_jpeg = path(__FILE__)->parent->parent->child("sample.jpg");
ok $sample_jpeg->exists, "sample image $sample_jpeg exists";
my $icon_filename = '74e3362283b6ef0c48686fb0e161da4043bbcc97.jpg';

$mech->post( '/admin/manifesttheme/lincolnshire',
Content_Type => 'form-data',
Content => {
name => "Lincolnshire FixMyStreet",
short_name => "Lincs FMS",
background_colour => "#663399",
theme_colour => "rgb(102, 51, 153)",
icon => [ $sample_jpeg, undef, Content_Type => 'image/jpeg' ],
},
);
ok $mech->success, 'Posted request successfully';

is $mech->uri->path, '/admin/manifesttheme/lincolnshire', "redirected back to edit page";
$mech->content_contains($icon_filename);
$mech->content_contains("133x100");
my $icon_dest = path(FixMyStreet->path_to('web/theme/lincolnshire/', $icon_filename));
ok $icon_dest->exists, "Icon stored on disk";
};

subtest "cobrand admin lets you delete an icon from an existing theme" => sub {
my $icon_filename = '74e3362283b6ef0c48686fb0e161da4043bbcc97.jpg';
my $icon_dest = path(FixMyStreet->path_to('web/theme/lincolnshire/', $icon_filename));
ok $icon_dest->exists, "Icon exists on disk";

$mech->get_ok("/admin/manifesttheme/lincolnshire");
my $fields = {
delete_icon => "/theme/lincolnshire/$icon_filename",
};
$mech->submit_form_ok( { with_fields => $fields } );

is $mech->uri->path, '/admin/manifesttheme/lincolnshire', "redirected back to edit page";
$mech->content_lacks($icon_filename);
$mech->content_lacks("133x100");
ok !$icon_dest->exists, "Icon removed from disk";
};

subtest "cobrand admin rejects non-images" => sub {
$mech->get_ok("/admin/manifesttheme/lincolnshire");

my $sample_pdf = path(__FILE__)->parent->parent->child("sample.pdf");
ok $sample_pdf->exists, "sample image $sample_pdf exists";

$mech->post( '/admin/manifesttheme/lincolnshire',
Content_Type => 'form-data',
Content => {
name => "Lincolnshire FixMyStreet",
short_name => "Lincs FMS",
background_colour => "#663399",
theme_colour => "rgb(102, 51, 153)",
icon => [ $sample_pdf, undef, Content_Type => 'application/pdf' ],
},
);
ok $mech->success, 'Posted request successfully';

is $mech->uri->path, '/admin/manifesttheme/lincolnshire', "redirected back to edit page";
$mech->content_lacks("90f7a64043fb458d58de1a0703a6355e2856b15e.pdf");
$mech->content_contains("File type not recognised. Please upload an image.");
};

subtest "theme link on cobrand admin goes to edit form when theme exists" => sub {
is( FixMyStreet::DB->resultset('ManifestTheme')->count, 1, "theme exists" );

Expand All @@ -108,13 +180,34 @@ subtest "create page on cobrand admin redirects to edit form when theme exists"
subtest "can delete theme" => sub {
is( FixMyStreet::DB->resultset('ManifestTheme')->count, 1, "theme exists" );

$mech->get_ok("/admin/manifesttheme/lincolnshire");
my $theme_id = FixMyStreet::DB->resultset('ManifestTheme')->find({ cobrand => 'lincolnshire' })->id;

# Add an icon so we can test it gets deleted when the theme is deleted
my $sample_jpeg = path(__FILE__)->parent->parent->child("sample.jpg");
ok $sample_jpeg->exists, "sample image $sample_jpeg exists";
my $icon_filename = '74e3362283b6ef0c48686fb0e161da4043bbcc97.jpg';

$mech->post( '/admin/manifesttheme/lincolnshire',
Content_Type => 'form-data',
Content => {
name => "Lincolnshire FixMyStreet",
short_name => "Lincs FMS",
background_colour => "#663399",
theme_colour => "rgb(102, 51, 153)",
icon => [ $sample_jpeg, undef, Content_Type => 'image/jpeg' ],
},
);
ok $mech->success, 'Posted request successfully';

is $mech->uri->path, '/admin/manifesttheme/lincolnshire', "redirected back to edit page";
my $icon_dest = path(FixMyStreet->path_to('web/theme/lincolnshire/', $icon_filename));
ok $icon_dest->exists, "Icon stored on disk";

$mech->submit_form_ok({ button => 'delete_theme' });
is $mech->uri->path, '/admin/manifesttheme/create', "redirected to create page";

is( FixMyStreet::DB->resultset('ManifestTheme')->count, 0, "theme deleted" );
ok !$icon_dest->exists, "Icon removed from disk";

my $log = $superuser->admin_logs->search({}, { order_by => { -desc => 'id' } })->first;
is $log->object_id, $theme_id;
Expand Down
29 changes: 28 additions & 1 deletion templates/web/base/admin/manifesttheme/form.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[% INCLUDE 'admin/header.html' title=loc('Theme') -%]

<form method="post">
<form method="post" enctype="multipart/form-data">
<div class="admin-hint">
<p>[% loc("The <strong>name</strong> is a string that represents the name of the web application as it is usually displayed to the user (e.g., amongst a list of other applications, or as a label for an icon).") %]</p>
</div>
Expand All @@ -25,6 +25,33 @@
[% form.field('cobrand').render | safe %]
[% END %]

<table>
<thead>
<tr>
<th>Icon</th>
<th>Size</th>
<th>Delete?</th>
</tr>
</thead>
<tbody>
[% FOREACH icon IN manifest_icons %]
<tr>
<td><img src="[% icon.src %]" /></td>
<td class="icon-size">[% icon.sizes %]</td>
<td><input type=checkbox name=delete_icon value='[% icon.src %]' /></td>
</tr>
[% END %]
<tr>
<td colspan=3>
<div class="admin-hint">
<p>[% loc("The <strong>icons</strong> are used when the application is installed to the user's home screen. Icons must be <strong>square</strong>, with <strong>512x512</strong>px and <strong>192x192</strong>px being the most common sizes.") %]</p>
</div>
[% form.field('icon').render | safe %]
</td>
</tr>
</tbody>
</table>

<p>
<input class="btn" type="submit" name="submit" value="[% loc('Save changes') %]">
</p>
Expand Down
7 changes: 7 additions & 0 deletions web/cobrands/fixmystreet/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,5 +190,12 @@ $(function(){
$('.js-metadata-items').on('click', '.js-metadata-option-remove', function(){
$(this).parents('.js-metadata-option').remove();
});

// On the manifest theme editing page we have tickboxes for deleting individual
// icons - ticking one of these should grey out that row to indicate it will be
// deleted upon form submission.
$("input[name=delete_icon]").change(function() {
$(this).closest("tr").toggleClass("is-deleted", this.checked);
});
});

5 changes: 4 additions & 1 deletion web/cobrands/sass/_admin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ $button_bg_col: #a1a1a1; // also search bar (tables)
}
tr.is-deleted {
background-color: #ffdddd;
td.contact-category {
img {
filter: grayscale(1);
}
td.contact-category, td.icon-size {
text-decoration: line-through;
}
}
Expand Down

0 comments on commit 4ede088

Please sign in to comment.