diff options
Diffstat (limited to 'perllib/FixMyStreet/DB')
-rw-r--r-- | perllib/FixMyStreet/DB/Factories.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/Body.pm | 34 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/Comment.pm | 34 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/Problem.pm | 55 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/User.pm | 59 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/ResultSet/Nearby.pm | 4 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/ResultSet/Problem.pm | 12 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/ResultSet/State.pm | 11 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/ResultSet/User.pm | 35 |
9 files changed, 199 insertions, 47 deletions
diff --git a/perllib/FixMyStreet/DB/Factories.pm b/perllib/FixMyStreet/DB/Factories.pm index ec4dd630a..7b8234aec 100644 --- a/perllib/FixMyStreet/DB/Factories.pm +++ b/perllib/FixMyStreet/DB/Factories.pm @@ -118,7 +118,7 @@ use parent "DBIx::Class::Factory"; __PACKAGE__->resultset(FixMyStreet::DB->resultset("ResponsePriority")); __PACKAGE__->fields({ - name => __PACKAGE__->seq(sub { 'Priority #' . (shift()+1) }), + name => __PACKAGE__->seq(sub { 'Priority ' . (shift()+1) }), description => __PACKAGE__->seq(sub { 'Description #' . (shift()+1) }), }); diff --git a/perllib/FixMyStreet/DB/Result/Body.pm b/perllib/FixMyStreet/DB/Result/Body.pm index 6481d5cfc..e5cd2b907 100644 --- a/perllib/FixMyStreet/DB/Result/Body.pm +++ b/perllib/FixMyStreet/DB/Result/Body.pm @@ -156,12 +156,13 @@ sub areas { } sub first_area_children { - my ( $self, $c ) = @_; + my ( $self ) = @_; my $area_id = $self->body_areas->first->area_id; + my $cobrand = $self->result_source->schema->cobrand; my $children = mySociety::MaPit::call('area/children', $area_id, - type => $c->cobrand->area_types_children, + type => $cobrand->area_types_children, ); return $children; @@ -182,4 +183,33 @@ sub get_cobrand_handler { return FixMyStreet::Cobrand->body_handler($self->areas); } +sub calculate_average { + my ($self) = @_; + + my $substmt = "select min(id) from comment where me.problem_id=comment.problem_id and (problem_state in ('fixed', 'fixed - council', 'fixed - user') or mark_fixed)"; + my $subquery = FixMyStreet::DB->resultset('Comment')->to_body($self)->search({ + -or => [ + problem_state => [ FixMyStreet::DB::Result::Problem->fixed_states() ], + mark_fixed => 1, + ], + 'me.id' => \"= ($substmt)", + 'me.state' => 'confirmed', + }, { + select => [ + { extract => "epoch from me.confirmed-problem.confirmed", -as => 'time' }, + ], + as => [ qw/time/ ], + rows => 100, + order_by => { -desc => 'me.confirmed' }, + join => 'problem' + })->as_subselect_rs; + + my $avg = $subquery->search({ + }, { + select => [ { avg => "time" } ], + as => [ qw/avg/ ], + })->first->get_column('avg'); + return $avg; +} + 1; diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm index 562f29693..60fd31510 100644 --- a/perllib/FixMyStreet/DB/Result/Comment.pm +++ b/perllib/FixMyStreet/DB/Result/Comment.pm @@ -229,13 +229,24 @@ sub meta_line { if ($self->anonymous or !$self->name) { $meta = sprintf( _( 'Posted anonymously at %s' ), Utils::prettify_dt( $self->confirmed ) ) - } elsif ($self->user->from_body) { + } elsif ($self->user->from_body || $self->get_extra_metadata('is_body_user') || $self->get_extra_metadata('is_superuser') ) { my $user_name = FixMyStreet::Template::html_filter($self->user->name); - my $body = $self->user->body; - if ($body eq 'Bromley Council') { - $body = "$body <img src='/cobrands/bromley/favicon.png' alt=''>"; - } elsif ($body eq 'Royal Borough of Greenwich') { - $body = "$body <img src='/cobrands/greenwich/favicon.png' alt=''>"; + my $body; + if ($self->get_extra_metadata('is_superuser')) { + $body = _('an administrator'); + } else { + # use this meta data in preference to the user's from_body setting + # in case they are no longer with the body, or have changed body. + if (my $body_id = $self->get_extra_metadata('is_body_user')) { + $body = FixMyStreet::App->model('DB::Body')->find({id => $body_id})->name; + } else { + $body = $self->user->body; + } + if ($body eq 'Bromley Council') { + $body = "$body <img src='/cobrands/bromley/favicon.png' alt=''>"; + } elsif ($body eq 'Royal Borough of Greenwich') { + $body = "$body <img src='/cobrands/greenwich/favicon.png' alt=''>"; + } } my $can_view_contribute = $c->user_exists && $c->user->has_permission_to('view_body_contribute_details', $self->problem->bodies_str_ids); if ($self->text) { @@ -274,14 +285,9 @@ sub problem_state_display { return FixMyStreet::DB->resultset("State")->display('confirmed', 1); } elsif ($self->problem_state) { my $state = $self->problem_state; - if ($state eq 'not responsible') { - $update_state = _( "not the council's responsibility" ); - if ($cobrand eq 'bromley' || $self->problem->to_body_named('Bromley')) { - $update_state = 'third party responsibility'; - } - } else { - $update_state = FixMyStreet::DB->resultset("State")->display($state, 1); - } + my $cobrand_name = $cobrand; + $cobrand_name = 'bromley' if $self->problem->to_body_named('Bromley'); + $update_state = FixMyStreet::DB->resultset("State")->display($state, 1, $cobrand_name); } return $update_state; diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index 3b622b561..8625bf17a 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -485,12 +485,21 @@ Return a url for this problem report that logs a user in sub tokenised_url { my ($self, $user, $params) = @_; + my %params; + if ($user->email_verified) { + $params{email} = $user->email; + } elsif ($user->phone_verified) { + $params{phone} = $user->phone; + # This is so the email token can look up/ log in a phone user + $params{login_type} = 'phone'; + } + my $token = FixMyStreet::App->model('DB::Token')->create( { scope => 'email_sign_in', data => { + %params, id => $self->id, - email => $user->email, r => $self->url, p => $params, } @@ -618,6 +627,15 @@ sub meta_line { return $meta; } +sub nearest_address { + my $self = shift; + + return '' unless $self->geocode; + + my $address = $self->geocode->{resourceSets}[0]{resources}[0]; + return $address->{name}; +} + sub body { my ( $problem, $c ) = @_; my $body; @@ -849,10 +867,23 @@ sub update_send_failed { } ); } +sub add_send_method { + my $self = shift; + my $sender = shift; + ($sender = ref $sender) =~ s/^.*:://; + if (my $send_method = $self->send_method_used) { + $self->send_method_used("$send_method,$sender"); + } else { + $self->send_method_used($sender); + } +} + sub as_hashref { my $self = shift; my $c = shift; + my $state_t = FixMyStreet::DB->resultset("State")->display($self->state); + return { id => $self->id, title => $self->title, @@ -863,12 +894,16 @@ sub as_hashref { postcode => $self->postcode, areas => $self->areas, state => $self->state, - state_t => _( $self->state ), + state_t => $state_t, used_map => $self->used_map, is_fixed => $self->fixed_states->{ $self->state } ? 1 : 0, photos => [ map { $_->{url} } @{$self->photos} ], meta => $self->confirmed ? $self->meta_line( $c ) : '', - confirmed_pp => $self->confirmed ? $c->cobrand->prettify_dt( $self->confirmed ): '', + ($self->confirmed ? ( + confirmed => $self->confirmed, + confirmed_pp => $c->cobrand->prettify_dt( $self->confirmed ), + ) : ()), + created => $self->created, created_pp => $c->cobrand->prettify_dt( $self->created ), }; } @@ -896,16 +931,20 @@ sub photos { # if LOGIN_REQUIRED is set. To stop this happening, Varnish should be # configured to not strip cookies if the cookie_passthrough param is # present, which this line ensures will be if LOGIN_REQUIRED is set. - my $extra = (FixMyStreet->config('LOGIN_REQUIRED')) ? "&cookie_passthrough=1" : ""; + my $extra = ''; + if (FixMyStreet->config('LOGIN_REQUIRED')) { + $cachebust .= '&cookie_passthrough=1'; + $extra = '?cookie_passthrough=1'; + } my ($hash, $format) = split /\./, $_; { id => $hash, url_temp => "/photo/temp.$hash.$format$extra", url_temp_full => "/photo/fulltemp.$hash.$format$extra", - url => "/photo/$id.$i.$format?$cachebust$extra", - url_full => "/photo/$id.$i.full.$format?$cachebust$extra", - url_tn => "/photo/$id.$i.tn.$format?$cachebust$extra", - url_fp => "/photo/$id.$i.fp.$format?$cachebust$extra", + url => "/photo/$id.$i.$format?$cachebust", + url_full => "/photo/$id.$i.full.$format?$cachebust", + url_tn => "/photo/$id.$i.tn.$format?$cachebust", + url_fp => "/photo/$id.$i.fp.$format?$cachebust", idx => $i++, } } $photoset->all_ids; diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index 19adf5d49..d02039ac3 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -19,7 +19,7 @@ __PACKAGE__->add_columns( sequence => "users_id_seq", }, "email", - { data_type => "text", is_nullable => 0 }, + { data_type => "text", is_nullable => 1 }, "name", { data_type => "text", is_nullable => 1 }, "phone", @@ -30,21 +30,24 @@ __PACKAGE__->add_columns( { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, "flagged", { data_type => "boolean", default_value => \"false", is_nullable => 0 }, + "is_superuser", + { data_type => "boolean", default_value => \"false", is_nullable => 0 }, "title", { data_type => "text", is_nullable => 1 }, "twitter_id", { data_type => "bigint", is_nullable => 1 }, "facebook_id", { data_type => "bigint", is_nullable => 1 }, - "is_superuser", - { data_type => "boolean", default_value => \"false", is_nullable => 0 }, "area_id", { data_type => "integer", is_nullable => 1 }, "extra", { data_type => "text", is_nullable => 1 }, + "email_verified", + { data_type => "boolean", default_value => \"false", is_nullable => 0 }, + "phone_verified", + { data_type => "boolean", default_value => \"false", is_nullable => 0 }, ); __PACKAGE__->set_primary_key("id"); -__PACKAGE__->add_unique_constraint("users_email_key", ["email"]); __PACKAGE__->add_unique_constraint("users_facebook_id_key", ["facebook_id"]); __PACKAGE__->add_unique_constraint("users_twitter_id_key", ["twitter_id"]); __PACKAGE__->has_many( @@ -102,13 +105,19 @@ __PACKAGE__->has_many( ); -# Created by DBIx::Class::Schema::Loader v0.07035 @ 2016-09-16 14:22:10 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:7wfF1VnZax2QTXCIPXr+vg +# Created by DBIx::Class::Schema::Loader v0.07035 @ 2017-09-19 18:02:17 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OKHKCSahWD3Ov6ulj+2f/w + +# These are not fully unique constraints (they only are when the *_verified +# is true), but this is managed in ResultSet::User's find() wrapper. +__PACKAGE__->add_unique_constraint("users_email_verified_key", ["email", "email_verified"]); +__PACKAGE__->add_unique_constraint("users_phone_verified_key", ["phone", "phone_verified"]); __PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn"); __PACKAGE__->rabx_column('extra'); use Moo; +use FixMyStreet::SMS; use mySociety::EmailUtil; use namespace::clean -except => [ 'meta' ]; @@ -125,6 +134,26 @@ __PACKAGE__->add_columns( }, ); +=head2 username + +Returns a verified email or phone for this user, preferring email, +or undef if neither verified (shouldn't happen). + +=cut + +sub username { + my $self = shift; + return $self->email if $self->email_verified; + return $self->phone_display if $self->phone_verified; +} + +sub phone_display { + my $self = shift; + return $self->phone unless $self->phone; + my $parsed = FixMyStreet::SMS->parse_username($self->phone); + return $parsed->{phone} ? $parsed->{phone}->format : $self->phone; +} + sub latest_anonymity { my $self = shift; my $p = $self->problems->search(undef, { order_by => { -desc => 'id' } } )->first; @@ -157,11 +186,19 @@ sub check_for_errors { $errors{name} = _('Please enter your name'); } - if ( $self->email !~ /\S/ ) { - $errors{email} = _('Please enter your email'); - } - elsif ( !mySociety::EmailUtil::is_valid_email( $self->email ) ) { - $errors{email} = _('Please enter a valid email'); + if ($self->email_verified) { + if ($self->email !~ /\S/) { + $errors{username} = _('Please enter your email'); + } elsif (!mySociety::EmailUtil::is_valid_email($self->email)) { + $errors{username} = _('Please enter a valid email'); + } + } elsif ($self->phone_verified) { + my $parsed = FixMyStreet::SMS->parse_username($self->phone); + if (!$parsed->{phone}) { + $errors{username} = _('Please check your phone number is correct'); + } elsif (!$parsed->{may_be_mobile}) { + $errors{username} = _('Please enter a mobile number'); + } } return \%errors; diff --git a/perllib/FixMyStreet/DB/ResultSet/Nearby.pm b/perllib/FixMyStreet/DB/ResultSet/Nearby.pm index 8b8951007..6e5e0220f 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Nearby.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Nearby.pm @@ -10,7 +10,7 @@ sub to_body { } sub nearby { - my ( $rs, $c, $dist, $ids, $limit, $mid_lat, $mid_lon, $interval, $categories, $states ) = @_; + my ( $rs, $c, $dist, $ids, $limit, $mid_lat, $mid_lon, $categories, $states ) = @_; unless ( $states ) { $states = FixMyStreet::DB::Result::Problem->visible_states(); @@ -20,8 +20,6 @@ sub nearby { non_public => 0, state => [ keys %$states ], }; - $params->{'current_timestamp-lastupdate'} = { '<', \"'$interval'::interval" } - if $interval; $params->{id} = { -not_in => $ids } if $ids; $params->{category} = $categories if $categories && @$categories; diff --git a/perllib/FixMyStreet/DB/ResultSet/Problem.pm b/perllib/FixMyStreet/DB/ResultSet/Problem.pm index f1ed50721..ae45351c4 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Problem.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Problem.pm @@ -140,11 +140,11 @@ sub _recent { # Problems around a location sub around_map { - my ( $rs, $limit, %p) = @_; + my ( $rs, $c, %p) = @_; my $attr = { order_by => $p{order}, }; - $attr->{rows} = $limit if $limit; + $attr->{rows} = $c->cobrand->reports_per_page; unless ( $p{states} ) { $p{states} = FixMyStreet::DB::Result::Problem->visible_states(); @@ -156,12 +156,12 @@ sub around_map { latitude => { '>=', $p{min_lat}, '<', $p{max_lat} }, longitude => { '>=', $p{min_lon}, '<', $p{max_lon} }, }; - $q->{'current_timestamp - lastupdate'} = { '<', \"'$p{interval}'::interval" } - if $p{interval}; $q->{category} = $p{categories} if $p{categories} && @{$p{categories}}; - my @problems = mySociety::Locale::in_gb_locale { $rs->search( $q, $attr )->include_comment_counts->all }; - return \@problems; + my $problems = mySociety::Locale::in_gb_locale { + $rs->search( $q, $attr )->include_comment_counts->page($p{page}); + }; + return $problems; } # Admin functions diff --git a/perllib/FixMyStreet/DB/ResultSet/State.pm b/perllib/FixMyStreet/DB/ResultSet/State.pm index ac13ec2a4..3e6169aeb 100644 --- a/perllib/FixMyStreet/DB/ResultSet/State.pm +++ b/perllib/FixMyStreet/DB/ResultSet/State.pm @@ -24,7 +24,10 @@ sub states { my $rs = shift; my $states = Memcached::get('states'); - if ($states) { + # If tests are run in parallel, the cached state in Memcached could be + # corrupted by multiple tests changing it at the same time + # uncoverable branch true + if ($states && !FixMyStreet->test_mode) { # Need to reattach schema $states->[0]->result_source->schema( $rs->result_source->schema ) if $states->[0]; return $states; @@ -55,7 +58,7 @@ sub fixed { [ $_[0]->_filter(sub { $_->type eq 'fixed' }) ] } # This function can be used to return that label's display name. sub display { - my ($rs, $label, $single_fixed) = @_; + my ($rs, $label, $single_fixed, $cobrand) = @_; my $unchanging = { unconfirmed => _("Unconfirmed"), hidden => _("Hidden"), @@ -69,6 +72,10 @@ sub display { }; $label = 'fixed' if $single_fixed && $label =~ /^fixed - (council|user)$/; return $unchanging->{$label} if $unchanging->{$label}; + if ($cobrand && $label eq 'not responsible') { + return 'third party responsibility' if $cobrand eq 'bromley'; + return _("not the council's responsibility"); + } my ($state) = $rs->_filter(sub { $_->label eq $label }); return $label unless $state; $state->name($translate_now->{$label}) if $translate_now->{$label}; diff --git a/perllib/FixMyStreet/DB/ResultSet/User.pm b/perllib/FixMyStreet/DB/ResultSet/User.pm index 7e657a936..9a8a50559 100644 --- a/perllib/FixMyStreet/DB/ResultSet/User.pm +++ b/perllib/FixMyStreet/DB/ResultSet/User.pm @@ -4,5 +4,40 @@ use base 'DBIx::Class::ResultSet'; use strict; use warnings; +use Moo; + +# The database has a partial unique index on email (when email_verified is +# true), and phone (when phone_verified is true). In the code, we can only +# say these are fully unique indices, which they aren't, as there could be +# multiple identical unverified phone numbers. +# +# We assume that any and all calls to find (also called using find_or_new, +# find_or_create, or update_or_new/create) are to look up verified entries +# only (it would make no sense to find() a non-unique entry). Therefore we +# help the code along by specifying the most appropriate key to use, given +# the data provided, and setting the appropriate verified boolean. + +around find => sub { + my ($orig, $self) = (shift, shift); + # If there's already a key, assume caller knows what they're doing + if (ref $_[0] eq 'HASH' && !$_[1]->{key}) { + if ($_[0]->{id}) { + $_[1]->{key} = 'primary'; + } elsif (exists $_[0]->{email} && exists $_[0]->{phone}) { + # If there's both email and phone, caller must also have specified + # a verified boolean so that we know what we're looking for + if (!$_[0]->{email_verified} && !$_[0]->{phone_verified}) { + die "Cannot perform a User find() with both email and phone and no verified"; + } + } elsif (exists $_[0]->{email}) { + $_[0]->{email_verified} = 1; + $_[1]->{key} = 'users_email_verified_key'; + } elsif (exists $_[0]->{phone}) { + $_[0]->{phone_verified} = 1; + $_[1]->{key} = 'users_phone_verified_key'; + } + } + $self->$orig(@_); +}; 1; |