diff options
Diffstat (limited to 'perllib/FixMyStreet')
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Admin/ManifestTheme.pm | 99 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Location.pm | 20 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Offline.pm | 106 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Root.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Form/ManifestTheme.pm | 68 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Default.pm | 1 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/AdminLog.pm | 6 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/Comment.pm | 1 | ||||
-rw-r--r-- | perllib/FixMyStreet/Geocode/Bexley.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/Geocode/Bing.pm | 1 | ||||
-rw-r--r-- | perllib/FixMyStreet/Geocode/Google.pm | 1 | ||||
-rw-r--r-- | perllib/FixMyStreet/Geocode/OSM.pm | 1 | ||||
-rw-r--r-- | perllib/FixMyStreet/Geocode/Zurich.pm | 1 | ||||
-rw-r--r-- | perllib/FixMyStreet/PhotoStorage.pm | 6 |
14 files changed, 278 insertions, 37 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Admin/ManifestTheme.pm b/perllib/FixMyStreet/App/Controller/Admin/ManifestTheme.pm new file mode 100644 index 000000000..9e3bdc33e --- /dev/null +++ b/perllib/FixMyStreet/App/Controller/Admin/ManifestTheme.pm @@ -0,0 +1,99 @@ +package FixMyStreet::App::Controller::Admin::ManifestTheme; +use Moose; +use namespace::autoclean; + +BEGIN { extends 'Catalyst::Controller'; } + +use FixMyStreet::App::Form::ManifestTheme; + +sub auto :Private { + my ($self, $c) = @_; + + if ( $c->cobrand->moniker eq 'fixmystreet' ) { + $c->stash(rs => $c->model('DB::ManifestTheme')->search_rs({}), show_all => 1); + } else { + $c->stash(rs => $c->model('DB::ManifestTheme')->search_rs({ cobrand => $c->cobrand->moniker })); + } +} + +sub index :Path :Args(0) { + my ( $self, $c ) = @_; + + unless ( $c->stash->{show_all} ) { + if ( $c->stash->{rs}->count ) { + $c->res->redirect($c->uri_for($self->action_for('edit'), [ $c->stash->{rs}->first->cobrand ])); + } else { + $c->res->redirect($c->uri_for($self->action_for('create'))); + } + $c->detach; + } +} + +sub item :PathPart('admin/manifesttheme') :Chained :CaptureArgs(1) { + my ($self, $c, $cobrand) = @_; + + my $obj = $c->stash->{rs}->find({ cobrand => $cobrand }) + or $c->detach('/page_error_404_not_found', []); + $c->stash(obj => $obj); +} + +sub edit :PathPart('') :Chained('item') :Args(0) { + my ($self, $c) = @_; + + 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->stash->{editing_manifest_theme} = $c->forward('/offline/_find_manifest_theme', [ $c->stash->{obj}->cobrand, 1 ]); + + return $form; +} + + +sub create :Local :Args(0) { + my ($self, $c) = @_; + + unless ( $c->stash->{show_all} || $c->stash->{rs}->count == 0) { + $c->res->redirect($c->uri_for($self->action_for('edit'), [ $c->stash->{rs}->first->cobrand ])); + $c->detach; + } + + my $theme = $c->stash->{rs}->new_result({}); + return $self->form($c, $theme); +} + +sub form { + my ($self, $c, $theme) = @_; + + if ($c->get_param('delete_theme')) { + $c->forward('_delete_all_manifest_icons'); + $c->forward('/offline/_clear_manifest_theme_cache', [ $theme->cobrand ]); + $theme->delete; + $c->forward('/admin/log_edit', [ $theme->id, 'manifesttheme', 'delete' ]); + $c->response->redirect($c->uri_for($self->action_for('index'))); + $c->detach; + } + + 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); + 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->forward('/offline/_clear_manifest_theme_cache', [ $theme->cobrand ]); + $c->response->redirect($c->uri_for($self->action_for('index'))); +} + +sub _delete_all_manifest_icons :Private { + my ($self, $c) = @_; + + my $theme = $c->forward('/offline/_find_manifest_theme', [ $c->stash->{obj}->cobrand, 1 ]); + foreach my $icon ( @{ $theme->{icons} } ) { + unlink FixMyStreet->path_to('web', $icon->{src}); + } +} + +1; diff --git a/perllib/FixMyStreet/App/Controller/Location.pm b/perllib/FixMyStreet/App/Controller/Location.pm index 8d5b0b147..81c2c33fc 100644 --- a/perllib/FixMyStreet/App/Controller/Location.pm +++ b/perllib/FixMyStreet/App/Controller/Location.pm @@ -6,6 +6,7 @@ BEGIN {extends 'Catalyst::Controller'; } use Encode; use FixMyStreet::Geocode; +use Try::Tiny; use Utils; =head1 NAME @@ -107,6 +108,25 @@ sub determine_location_from_pc : Private { # pass errors back to the template $c->stash->{location_error_pc_lookup} = 1; $c->stash->{location_error} = $error; + + # Log failure in a log db + try { + my $dbfile = FixMyStreet->path_to('../data/analytics.sqlite'); + my $db = DBI->connect("dbi:SQLite:dbname=$dbfile", undef, undef) or die "$DBI::errstr\n"; + my $sth = $db->prepare("INSERT INTO location_searches_with_no_results + (datetime, cobrand, geocoder, url, user_input) + VALUES (?, ?, ?, ?, ?)") or die $db->errstr . "\n"; + my $rv = $sth->execute( + POSIX::strftime("%Y-%m-%d %H:%M:%S", localtime(time())), + $c->cobrand->moniker, + $c->cobrand->get_geocoder(), + $c->stash->{geocoder_url}, + $pc, + ); + } catch { + $c->log->debug("Unable to log to analytics.sqlite: $_"); + }; + return; } diff --git a/perllib/FixMyStreet/App/Controller/Offline.pm b/perllib/FixMyStreet/App/Controller/Offline.pm index 57cbe201c..adb3de14d 100644 --- a/perllib/FixMyStreet/App/Controller/Offline.pm +++ b/perllib/FixMyStreet/App/Controller/Offline.pm @@ -33,42 +33,12 @@ sub manifest: Path("/.well-known/manifest.webmanifest") { my ($self, $c) = @_; $c->res->content_type('application/manifest+json'); - my $theme = $c->model('DB::ManifestTheme')->find({ cobrand => $c->cobrand->moniker }); - unless ( $theme ) { - $theme = $c->model('DB::ManifestTheme')->new({ - name => $c->stash->{site_name}, - short_name => $c->stash->{site_name}, - background_colour => '#ffffff', - theme_colour => '#ffd000', - }); - } - - my @icons; - my $uri = '/theme/' . $c->cobrand->moniker; - my $theme_path = path(FixMyStreet->path_to('web' . $uri)); - $theme_path->visit( - sub { - my ($x, $y, $typ) = Image::Size::imgsize($_->stringify); - push @icons, { - src => join('/', $uri, $_->basename), - sizes => join('x', $x, $y), - type => $typ eq 'PNG' ? 'image/png' : $typ eq 'GIF' ? 'image/gif' : $typ eq 'JPG' ? 'image/jpeg' : '', - }; - } - ); - - unless (@icons) { - push @icons, - { src => "/cobrands/fixmystreet/images/192.png", sizes => "192x192", type => "image/png" }, - { src => "/cobrands/fixmystreet/images/512.png", sizes => "512x512", type => "image/png" }; - } - my $data = { - name => $theme->name, - short_name => $theme->short_name, - background_color => $theme->background_colour, - theme_color => $theme->theme_colour, - icons => \@icons, + name => $c->stash->{manifest_theme}->{name}, + short_name => $c->stash->{manifest_theme}->{short_name}, + background_color => $c->stash->{manifest_theme}->{background_colour}, + theme_color => $c->stash->{manifest_theme}->{theme_colour}, + icons => $c->stash->{manifest_theme}->{icons}, lang => $c->stash->{lang_code}, display => "minimal-ui", start_url => "/?pwa", @@ -82,6 +52,72 @@ sub manifest: Path("/.well-known/manifest.webmanifest") { $c->res->body($json); } +sub _stash_manifest_theme : Private { + my ($self, $c, $cobrand) = @_; + + $c->stash->{manifest_theme} = $c->forward('_find_manifest_theme', [ $cobrand ]); +} + +sub _find_manifest_theme : Private { + my ($self, $c, $cobrand, $ignore_cache_and_defaults) = @_; + + my $key = "manifest_theme:$cobrand"; + # ignore_cache_and_defaults is only used in the admin, so no harm bypassing cache + my $manifest_theme = $ignore_cache_and_defaults ? undef : Memcached::get($key); + + unless ( $manifest_theme ) { + my $theme = $c->model('DB::ManifestTheme')->find({ cobrand => $cobrand }); + unless ( $theme ) { + $theme = $c->model('DB::ManifestTheme')->new({ + name => $c->stash->{site_name}, + short_name => $c->stash->{site_name}, + background_colour => '#ffffff', + theme_colour => '#ffd000', + }); + } + + my @icons; + my $uri = '/theme/' . $cobrand; + my $theme_path = path(FixMyStreet->path_to('web' . $uri)); + $theme_path->visit( + sub { + my ($x, $y, $typ) = Image::Size::imgsize($_->stringify); + push @icons, { + src => join('/', $uri, $_->basename), + sizes => join('x', $x, $y), + type => $typ eq 'PNG' ? 'image/png' : $typ eq 'GIF' ? 'image/gif' : $typ eq 'JPG' ? 'image/jpeg' : '', + }; + } + ); + + unless (@icons || $ignore_cache_and_defaults) { + push @icons, + { src => "/cobrands/fixmystreet/images/192.png", sizes => "192x192", type => "image/png" }, + { src => "/cobrands/fixmystreet/images/512.png", sizes => "512x512", type => "image/png" }; + } + + $manifest_theme = { + icons => \@icons, + background_colour => $theme->background_colour, + theme_colour => $theme->theme_colour, + name => $theme->name, + short_name => $theme->short_name, + }; + + unless ($ignore_cache_and_defaults) { + Memcached::set($key, $manifest_theme); + } + } + + return $manifest_theme; +} + +sub _clear_manifest_theme_cache : Private { + my ($self, $c, $cobrand ) = @_; + + Memcached::delete("manifest_theme:$cobrand"); +} + __PACKAGE__->meta->make_immutable; 1; diff --git a/perllib/FixMyStreet/App/Controller/Root.pm b/perllib/FixMyStreet/App/Controller/Root.pm index caaa260ff..71dcf8e27 100644 --- a/perllib/FixMyStreet/App/Controller/Root.pm +++ b/perllib/FixMyStreet/App/Controller/Root.pm @@ -42,6 +42,8 @@ sub auto : Private { $c->forward('check_password_expiry'); $c->detach('/auth/redirect') if $c->cobrand->call_hook('check_login_disallowed'); + $c->forward('/offline/_stash_manifest_theme', [ $c->cobrand->moniker ]); + return 1; } diff --git a/perllib/FixMyStreet/App/Form/ManifestTheme.pm b/perllib/FixMyStreet/App/Form/ManifestTheme.pm new file mode 100644 index 000000000..aa2d467d6 --- /dev/null +++ b/perllib/FixMyStreet/App/Form/ManifestTheme.pm @@ -0,0 +1,68 @@ +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; +use List::MoreUtils qw(uniq); +extends 'HTML::FormHandler::Model::DBIC'; +use namespace::autoclean; + +has 'cobrand' => ( isa => 'Str', is => 'ro' ); + +has '+widget_name_space' => ( default => sub { ['FixMyStreet::App::Form::Widget'] } ); +has '+widget_tags' => ( default => sub { { wrapper_tag => 'p' } } ); +has '+item_class' => ( default => 'ManifestTheme' ); +has_field 'cobrand' => ( type => 'Select', empty_select => 'Select a cobrand', required => 1 ); +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' ); + +sub _build_language_handle { FixMyStreet::App::Form::I18N->new } + +sub options_cobrand { + my @cobrands = uniq sort map { $_->{moniker} } FixMyStreet::Cobrand->available_cobrand_classes; + return map { $_ => $_ } @cobrands; +} + +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)); + $theme_path->mkpath; + 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; diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index 9851b4896..695487268 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -685,6 +685,7 @@ sub admin_pages { $pages->{flagged} = [ _('Flagged'), 7 ]; $pages->{states} = [ _('States'), 8 ]; $pages->{config} = [ _('Configuration'), 9]; + $pages->{manifesttheme} = [ _('Manifest Theme'), 11]; $pages->{user_import} = [ undef, undef ]; }; # And some that need special permissions diff --git a/perllib/FixMyStreet/DB/Result/AdminLog.pm b/perllib/FixMyStreet/DB/Result/AdminLog.pm index 5564d829a..4c89138c9 100644 --- a/perllib/FixMyStreet/DB/Result/AdminLog.pm +++ b/perllib/FixMyStreet/DB/Result/AdminLog.pm @@ -91,6 +91,10 @@ sub link { my $category = $self->object; return "/admin/body/" . $category->body_id . '/' . $category->category; } + if ($type eq 'manifesttheme') { + my $theme = $self->object; + return "/admin/manifesttheme/" . $theme->cobrand; + } return ''; } @@ -114,6 +118,7 @@ sub object_summary { role => 'name', template => 'title', category => 'category', + manifesttheme => 'cobrand', }; my $thing = $type_to_thing->{$self->object_type} || 'id'; @@ -130,6 +135,7 @@ sub object { template => 'ResponseTemplate', category => 'Contact', update => 'Comment', + manifesttheme => 'ManifestTheme', }; $type = $type_to_object->{$type} || ucfirst $type; my $object = $self->result_source->schema->resultset($type)->find($id); diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm index bac183271..b217bf96c 100644 --- a/perllib/FixMyStreet/DB/Result/Comment.pm +++ b/perllib/FixMyStreet/DB/Result/Comment.pm @@ -292,6 +292,7 @@ sub is_latest { { problem_id => $self->problem_id, state => 'confirmed' }, { order_by => [ { -desc => 'confirmed' }, { -desc => 'id' } ] } )->first; + return unless $latest_update; return $latest_update->id == $self->id; } diff --git a/perllib/FixMyStreet/Geocode/Bexley.pm b/perllib/FixMyStreet/Geocode/Bexley.pm index a70a42cd1..8a1a886bb 100644 --- a/perllib/FixMyStreet/Geocode/Bexley.pm +++ b/perllib/FixMyStreet/Geocode/Bexley.pm @@ -23,6 +23,8 @@ sub string { my $js = query_layer($s); return $osm unless $js && @{$js->{features}}; + $c->stash->{geocoder_url} = $s; + my ( $error, @valid_locations, $latitude, $longitude, $address ); foreach (sort { $a->{properties}{ADDRESS} cmp $b->{properties}{ADDRESS} } @{$js->{features}}) { my @lines = @{$_->{geometry}{coordinates}}; diff --git a/perllib/FixMyStreet/Geocode/Bing.pm b/perllib/FixMyStreet/Geocode/Bing.pm index ee5e15f8c..1d39d911f 100644 --- a/perllib/FixMyStreet/Geocode/Bing.pm +++ b/perllib/FixMyStreet/Geocode/Bing.pm @@ -38,6 +38,7 @@ sub string { $url .= '&userLocation=' . $params->{centre} if $params->{centre}; $url .= '&c=' . $params->{bing_culture} if $params->{bing_culture}; + $c->stash->{geocoder_url} = $url; my $js = FixMyStreet::Geocode::cache('bing', $url, 'key=' . FixMyStreet->config('BING_MAPS_API_KEY')); if (!$js) { return { error => _('Sorry, we could not parse that location. Please try again.') }; diff --git a/perllib/FixMyStreet/Geocode/Google.pm b/perllib/FixMyStreet/Geocode/Google.pm index 455d9cec0..ffbad96ba 100644 --- a/perllib/FixMyStreet/Geocode/Google.pm +++ b/perllib/FixMyStreet/Geocode/Google.pm @@ -49,6 +49,7 @@ sub string { $url .= '&components=' . $components if $components; + $c->stash->{geocoder_url} = $url; my $args = 'key=' . FixMyStreet->config('GOOGLE_MAPS_API_KEY'); my $js = FixMyStreet::Geocode::cache('google', $url, $args, qr/"status"\s*:\s*"(OVER_QUERY_LIMIT|REQUEST_DENIED|INVALID_REQUEST|UNKNOWN_ERROR)"/); if (!$js) { diff --git a/perllib/FixMyStreet/Geocode/OSM.pm b/perllib/FixMyStreet/Geocode/OSM.pm index b979e2a10..20e653cf6 100644 --- a/perllib/FixMyStreet/Geocode/OSM.pm +++ b/perllib/FixMyStreet/Geocode/OSM.pm @@ -47,6 +47,7 @@ sub string { if $params->{country}; $url .= join('&', map { "$_=$query_params{$_}" } sort keys %query_params); + $c->stash->{geocoder_url} = $url; my $js = FixMyStreet::Geocode::cache('osm', $url); if (!$js) { return { error => _('Sorry, we could not find that location.') }; diff --git a/perllib/FixMyStreet/Geocode/Zurich.pm b/perllib/FixMyStreet/Geocode/Zurich.pm index 0b85ab7b2..b0c0b528e 100644 --- a/perllib/FixMyStreet/Geocode/Zurich.pm +++ b/perllib/FixMyStreet/Geocode/Zurich.pm @@ -97,6 +97,7 @@ sub string { my $cache_dir = path(FixMyStreet->config('GEO_CACHE'), 'zurich')->absolute(FixMyStreet->path_to()); my $cache_file = $cache_dir->child(md5_hex($s)); my $result; + $c->stash->{geocoder_url} = $s; if (-s $cache_file && -M $cache_file <= 7 && !FixMyStreet->config('STAGING_SITE')) { $result = retrieve($cache_file); } else { diff --git a/perllib/FixMyStreet/PhotoStorage.pm b/perllib/FixMyStreet/PhotoStorage.pm index 9b0e5c9c3..256d46361 100644 --- a/perllib/FixMyStreet/PhotoStorage.pm +++ b/perllib/FixMyStreet/PhotoStorage.pm @@ -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 (); } } |