diff options
Diffstat (limited to 'perllib/FixMyStreet/DB/Result')
-rw-r--r-- | perllib/FixMyStreet/DB/Result/Comment.pm | 88 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/Contact.pm | 31 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm | 24 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/Problem.pm | 180 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/User.pm | 7 |
5 files changed, 182 insertions, 148 deletions
diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm index b217bf96c..82476ba10 100644 --- a/perllib/FixMyStreet/DB/Result/Comment.pm +++ b/perllib/FixMyStreet/DB/Result/Comment.pm @@ -110,6 +110,61 @@ with 'FixMyStreet::Roles::Abuser', 'FixMyStreet::Roles::Moderation', 'FixMyStreet::Roles::PhotoSet'; +=head2 FOREIGNBUILDARGS + +Make sure that when creating a new Comment object, certain +other fields are set based upon the supplied data. + +=cut + +sub FOREIGNBUILDARGS { + my ($class, $opts) = @_; + + if (my $user = $opts->{user}) { + my $name; + if ($user->is_superuser) { + $opts->{extra}->{is_superuser} = 1; + $name = _('an administrator'); + } elsif (my $body = $user->from_body) { + $opts->{extra}->{is_body_user} = $body->id; + $name = $body->name; + $name = 'Island Roads' if $name eq 'Isle of Wight Council'; + } else { + $name = $user->name; + } + $opts->{name} //= $name; + } + + $opts->{anonymous} //= 0; + $opts->{mark_fixed} //= 0; + $opts->{state} //= 'confirmed'; # it's only public updates that need to be unconfirmed + if ($opts->{state} eq 'confirmed') { + $opts->{confirmed} //= \'current_timestamp'; + } + + return $opts; +}; + +=head2 around user + +Also make sure we catch the setting of a user on an object at a time other than +object creation, to set the extra field needed. + +=cut + +around user => sub { + my ( $orig, $self ) = ( shift, shift ); + my $res = $self->$orig(@_); + if (@_) { + if ($_[0]->is_superuser) { + $self->set_extra_metadata( is_superuser => 1 ); + } elsif (my $body = $_[0]->from_body) { + $self->set_extra_metadata( is_body_user => $body->id ); + } + } + return $res; +}; + =head2 get_cobrand_logged Get a cobrand object for the cobrand the update was made on. @@ -207,13 +262,19 @@ about an update. Can include HTML. =cut sub meta_line { - my ( $self, $c ) = @_; + my ( $self, $user ) = @_; + my $cobrand = $self->result_source->schema->cobrand; my $meta = ''; - if ($self->anonymous or !$self->name) { - $meta = sprintf( _( 'Posted anonymously at %s' ), Utils::prettify_dt( $self->confirmed ) ) - } elsif ($self->user->from_body || $self->get_extra_metadata('is_body_user') || $self->get_extra_metadata('is_superuser') ) { + my $contributed_as = $self->get_extra_metadata('contributed_as') || ''; + my $staff = $self->user->from_body || $self->get_extra_metadata('is_body_user') || $self->get_extra_metadata('is_superuser'); + my $anon = $self->anonymous || !$self->name; + + if ($anon && (!$staff || $contributed_as eq 'anonymous_user' || $contributed_as eq 'another_user')) { + $meta = $cobrand->call_hook(update_anonymous_message => $self); + $meta ||= sprintf( _( 'Posted anonymously at %s' ), Utils::prettify_dt( $self->confirmed ) ) + } elsif ($staff) { my $user_name = FixMyStreet::Template::html_filter($self->user->name); my $body; if ($self->get_extra_metadata('is_superuser')) { @@ -237,9 +298,9 @@ sub meta_line { $body = 'Island Roads'; } } - my $cobrand_always_view_body_user = $c->cobrand->call_hook("always_view_body_contribute_details"); + my $cobrand_always_view_body_user = $cobrand->call_hook("always_view_body_contribute_details"); my $can_view_contribute = $cobrand_always_view_body_user || - ($c->user_exists && $c->user->has_permission_to('view_body_contribute_details', $self->problem->bodies_str_ids)); + ($user && $user->has_permission_to('view_body_contribute_details', $self->problem->bodies_str_ids)); if ($self->text) { if ($can_view_contribute) { $meta = sprintf( _( 'Posted by <strong>%s</strong> (%s) at %s' ), $body, $user_name, Utils::prettify_dt( $self->confirmed ) ); @@ -268,16 +329,20 @@ sub problem_state_processed { my $self = shift; return 'fixed - user' if $self->mark_fixed; return 'confirmed' if $self->mark_open; - return $self->problem_state; + my $cobrand = $self->result_source->schema->cobrand; + my $cobrand_state = $cobrand->call_hook(problem_state_processed => $self); + + return $cobrand_state || $self->problem_state; } sub problem_state_display { - my ( $self, $c ) = @_; + my $self = shift; my $state = $self->problem_state_processed; return '' unless $state; - my $cobrand_name = $c->cobrand->moniker; + my $cobrand = $self->result_source->schema->cobrand; + my $cobrand_name = $cobrand->moniker; my $names = join(',,', @{$self->problem->body_names}); if ($names =~ /(Bromley|Isle of Wight|TfL)/) { ($cobrand_name = lc $1) =~ s/ //g; @@ -313,7 +378,8 @@ sub hide { } sub as_hashref { - my ($self, $c, $cols) = @_; + my ($self, $cols) = @_; + my $cobrand = $self->result_source->schema->cobrand; my $out = { id => $self->id, @@ -329,7 +395,7 @@ sub as_hashref { if ($self->confirmed) { $out->{confirmed} = $self->confirmed if !$cols || $cols->{confirmed}; - $out->{confirmed_pp} = $c->cobrand->prettify_dt( $self->confirmed ) if !$cols || $cols->{confirmed_pp}; + $out->{confirmed_pp} = $cobrand->prettify_dt( $self->confirmed ) if !$cols || $cols->{confirmed_pp}; } return $out; diff --git a/perllib/FixMyStreet/DB/Result/Contact.pm b/perllib/FixMyStreet/DB/Result/Contact.pm index affc6d480..69f8886eb 100644 --- a/perllib/FixMyStreet/DB/Result/Contact.pm +++ b/perllib/FixMyStreet/DB/Result/Contact.pm @@ -113,9 +113,11 @@ sub category_display { $self->get_extra_metadata('display_name') || $self->translate_column('category'); } +# Returns an arrayref of groups this Contact is in; if it is +# not in any group, returns an arrayref of the empty string. sub groups { my $self = shift; - my $groups = $self->get_extra_metadata('group') || []; + my $groups = $self->get_extra_metadata('group') || ['']; $groups = [ $groups ] unless ref $groups eq 'ARRAY'; return $groups; } @@ -175,4 +177,31 @@ sub disable_form_field { return $field; } +sub sent_by_open311 { + my $self = shift; + my $body = $self->body; + my $method = $self->send_method || ''; + my $body_method = $body->send_method || ''; + return 1 if + (!$body->can_be_devolved && $body_method eq 'Open311') + || ($body->can_be_devolved && $body_method eq 'Open311' && !$method) + || ($body->can_be_devolved && $method eq 'Open311'); + return 0; +} + +# We do not want to allow editing of a category's name +# if it's Open311, unless it's marked as protected +# Also prevent editing of hardcoded categories +sub category_uneditable { + my $self = shift; + return 1 if + $self->in_storage + && !$self->get_extra_metadata('open311_protect') + && $self->sent_by_open311; + return 1 if + $self->in_storage + && $self->get_extra_metadata('hardcoded'); + return 0; +} + 1; diff --git a/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm b/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm index 1805e1fd2..dd76a52c0 100644 --- a/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm +++ b/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm @@ -156,6 +156,20 @@ sub compare_photo { return FixMyStreet::Template::SafeString->new($s); } +# This is a list of extra keys that could be set on a report after a moderation +# has occurred. This can confuse the display of the last moderation entry, as +# the comparison with the problem's extra will be wrong. +my @keys_to_ignore = ( + 'sent_to', # SendReport::Email adds this arrayref when sent + 'closed_updates', # Marked to close a report to updates + 'closure_alert_sent_at', # Set by alert sending if update closes a report + # Can be set/changed by an Open311 update + 'external_status_code', 'customer_reference', + # Can be set by inspectors + 'traffic_information', 'detailed_information', 'duplicates', 'duplicate_of', 'order', +); +my %keys_to_ignore = map { $_ => 1 } @keys_to_ignore; + sub compare_extra { my ($self, $other) = @_; @@ -163,18 +177,20 @@ sub compare_extra { my $new = $other->get_extra_metadata; my $both = { %$old, %$new }; - my @all_keys = sort keys %$both; + my @all_keys = grep { !$keys_to_ignore{$_} } sort keys %$both; my @s; foreach (@all_keys) { + $old->{$_} = join(', ', @{$old->{$_}}) if ref $old->{$_} eq 'ARRAY'; + $new->{$_} = join(', ', @{$new->{$_}}) if ref $new->{$_} eq 'ARRAY'; if ($old->{$_} && $new->{$_}) { push @s, string_diff("$_ = $old->{$_}", "$_ = $new->{$_}"); } elsif ($new->{$_}) { push @s, string_diff("", "$_ = $new->{$_}"); - } else { + } elsif ($old->{$_}) { push @s, string_diff("$_ = $old->{$_}", ""); } } - return join ', ', grep { $_ } @s; + return join '; ', grep { $_ } @s; } sub extra_diff { @@ -193,7 +209,7 @@ sub string_diff { $new = FixMyStreet::Template::html_filter($new); if ($options{single}) { - return unless $old; + return '' unless $old; $old = [ $old ]; $new = [ $new ]; } diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index 37563d327..ceb41b40f 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -193,6 +193,27 @@ __PACKAGE__->might_have( cascade_copy => 0, cascade_delete => 1 }, ); +# Add a possible join for the Contact object associated with +# this report (based on bodies_str and category). If the report +# was sent to multiple bodies, only returns the first. +__PACKAGE__->belongs_to( + contact => "FixMyStreet::DB::Result::Contact", + sub { + my $args = shift; + return { + "$args->{foreign_alias}.category" => { -ident => "$args->{self_alias}.category" }, + -and => [ + \[ "CAST($args->{foreign_alias}.body_id AS text) = (regexp_split_to_array($args->{self_alias}.bodies_str, ','))[1]" ], + ] + }; + }, + { + join_type => "LEFT", + on_delete => "NO ACTION", + on_update => "NO ACTION", + }, +); + __PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn"); __PACKAGE__->rabx_column('extra'); __PACKAGE__->rabx_column('geocode'); @@ -407,30 +428,11 @@ sub confirm { sub category_display { my $self = shift; - my $contact = $self->category_row; + my $contact = $self->contact; return $self->category unless $contact; # Fallback; shouldn't happen, but some tests return $contact->category_display; } -=head2 category_row - -Returns the corresponding Contact object for this problem's category and body. -If the report was sent to multiple bodies, only returns the first. - -=cut - -sub category_row { - my $self = shift; - my $schema = $self->result_source->schema; - my $body_id = $self->bodies_str_ids->[0]; - return unless $body_id && $body_id =~ /^[0-9]+$/; - my $contact = $schema->resultset("Contact")->find({ - body_id => $body_id, - category => $self->category, - }); - return $contact; -} - sub bodies_str_ids { my $self = shift; return [] unless $self->bodies_str; @@ -629,13 +631,14 @@ meta data about the report. =cut sub meta_line { - my ( $problem, $c ) = @_; + my ( $problem, $user ) = @_; + my $cobrand = $problem->result_source->schema->cobrand; my $date_time = Utils::prettify_dt( $problem->confirmed ); my $meta = ''; my $category = $problem->category_display; - $category = $c->cobrand->call_hook(change_category_text => $category) || $category; + $category = $cobrand->call_hook(change_category_text => $category) || $category; if ( $problem->anonymous ) { if ( $problem->service and $category && $category ne _('Other') ) { @@ -654,8 +657,8 @@ sub meta_line { } else { my $problem_name = $problem->name; - if ($c->user_exists and - $c->user->has_permission_to('view_body_contribute_details', $problem->bodies_str_ids) and + if ($user and + $user->has_permission_to('view_body_contribute_details', $problem->bodies_str_ids) and $problem->name ne $problem->user->name) { $problem_name = sprintf('%s (%s)', $problem->name, $problem->user->name ); } @@ -690,12 +693,12 @@ sub nearest_address { } sub body { - my ( $problem, $c ) = @_; + my ( $problem, $link ) = @_; my $body; if ($problem->external_body) { if ($problem->cobrand eq 'zurich') { my $cache = $problem->result_source->schema->cache; - return $cache->{bodies}{$problem->external_body} //= $c->model('DB::Body')->find({ id => $problem->external_body }); + return $cache->{bodies}{$problem->external_body} //= FixMyStreet::DB->resultset('Body')->find({ id => $problem->external_body }); } else { $body = FixMyStreet::Template::html_filter($problem->external_body); } @@ -703,7 +706,7 @@ sub body { my $bodies = $problem->bodies; my @body_names = sort map { my $name = $_->name; - if ($c and FixMyStreet->config('AREA_LINKS_FROM_PROBLEMS')) { + if ($link and FixMyStreet->config('AREA_LINKS_FROM_PROBLEMS')) { '<a href="' . $_->url . '">' . FixMyStreet::Template::html_filter($name) . '</a>'; } else { FixMyStreet::Template::html_filter($name); @@ -777,7 +780,11 @@ alphabetical order of name. sub response_priorities { my $self = shift; - return $self->result_source->schema->resultset('ResponsePriority')->for_bodies($self->bodies_str_ids, $self->category); + my $rs = $self->result_source->schema->resultset('ResponsePriority')->for_bodies($self->bodies_str_ids, $self->category); + $rs->search([ + 'me.deleted' => 0, + 'me.id' => $self->response_priority_id, + ]); } =head2 defect_types @@ -808,9 +815,10 @@ sub can_display_external_id { # This can return HTML and is safe, so returns a FixMyStreet::Template::SafeString sub duration_string { - my ( $problem, $c ) = @_; - my $body = $c->cobrand->call_hook(link_to_council_cobrand => $problem) || $problem->body($c); - my $handler = $c->cobrand->call_hook(get_body_handler_for_problem => $problem); + my $problem = shift; + my $cobrand = $problem->result_source->schema->cobrand; + my $body = $cobrand->call_hook(link_to_council_cobrand => $problem) || $problem->body(1); + my $handler = $cobrand->call_hook(get_body_handler_for_problem => $problem); if ( $handler && $handler->call_hook('is_council_with_case_management') ) { my $s = sprintf(_('Received by %s moments later'), $body); return FixMyStreet::Template::SafeString->new($s); @@ -836,87 +844,6 @@ sub local_coords { } } -=head2 update_from_open311_service_request - - $p->update_from_open311_service_request( $request, $body, $system_user ); - -Updates the problem based on information in the passed in open311 request -(standard, not the extension that uses GetServiceRequestUpdates) . If the -request has an older update time than the problem's lastupdate time then -nothing happens. - -Otherwise a comment will be created if there is status update text in the -open311 request. If the open311 request has a state of closed then the problem -will be marked as fixed. - -NB: a comment will always be created if the problem is being marked as fixed. - -Fixed problems will not be re-opened by this method. - -=cut - -sub update_from_open311_service_request { - my ( $self, $request, $body, $system_user ) = @_; - - my ( $updated, $status_notes ); - - if ( ! ref $request->{updated_datetime} ) { - $updated = $request->{updated_datetime}; - } - - if ( ! ref $request->{status_notes} ) { - $status_notes = $request->{status_notes}; - } - - my $update = $self->new_related(comments => { - state => 'confirmed', - created => $updated || \'current_timestamp', - confirmed => \'current_timestamp', - text => $status_notes, - mark_open => 0, - mark_fixed => 0, - user => $system_user, - anonymous => 0, - name => $body->name, - }); - - my $w3c = DateTime::Format::W3CDTF->new; - my $req_time = $w3c->parse_datetime( $request->{updated_datetime} ); - - # set a timezone here as the $req_time will have one and if we don't - # use a timezone then the date comparisons are invalid. - # of course if local timezone is not the one that went into the data - # base then we're also in trouble - my $lastupdate = $self->lastupdate; - $lastupdate->set_time_zone( FixMyStreet->local_time_zone ); - - # update from open311 is older so skip - if ( $req_time < $lastupdate ) { - return 0; - } - - if ( $request->{status} eq 'closed' ) { - if ( $self->state ne 'fixed' ) { - $self->state('fixed'); - $update->mark_fixed(1); - - if ( !$status_notes ) { - # FIXME - better text here - $status_notes = _('Closed by council'); - } - } - } - - if ( $status_notes ) { - $update->text( $status_notes ); - $self->lastupdate( $req_time ); - $self->update; - $update->insert; - } - - return 1; -} - sub update_send_failed { my $self = shift; my $msg = shift; @@ -975,7 +902,8 @@ sub resend { } sub as_hashref { - my ($self, $c, $cols) = @_; + my ($self, $cols) = @_; + my $cobrand = $self->result_source->schema->cobrand; my $state_t = FixMyStreet::DB->resultset("State")->display($self->state); @@ -995,11 +923,11 @@ sub as_hashref { }; $out->{is_fixed} = $self->fixed_states->{ $self->state } ? 1 : 0 if !$cols || $cols->{is_fixed}; $out->{photos} = [ map { $_->{url} } @{$self->photos} ] if !$cols || $cols->{photos}; - $out->{meta} = $self->confirmed ? $self->meta_line( $c ) : '' if !$cols || $cols->{meta}; - $out->{created_pp} = $c->cobrand->prettify_dt( $self->created ) if !$cols || $cols->{created_pp}; + $out->{meta} = $self->confirmed ? $self->meta_line : '' if !$cols || $cols->{meta}; + $out->{created_pp} = $cobrand->prettify_dt( $self->created ) if !$cols || $cols->{created_pp}; if ($self->confirmed) { $out->{confirmed} = $self->confirmed if !$cols || $cols->{confirmed}; - $out->{confirmed_pp} = $c->cobrand->prettify_dt( $self->confirmed ) if !$cols || $cols->{confirmed_pp}; + $out->{confirmed_pp} = $cobrand->prettify_dt( $self->confirmed ) if !$cols || $cols->{confirmed_pp}; } return $out; } @@ -1052,10 +980,11 @@ has get_cobrand_logged => ( sub pin_data { - my ($self, $c, $page, %opts) = @_; - my $colour = $c->cobrand->pin_colour($self, $page); + my ($self, $page, %opts) = @_; + my $cobrand = $self->result_source->schema->cobrand; + my $colour = $cobrand->pin_colour($self, $page); my $title = $opts{private} ? $self->title : $self->title_safe; - $title = $c->cobrand->call_hook(pin_hover_title => $self, $title) || $title; + $title = $cobrand->call_hook(pin_hover_title => $self, $title) || $title; { latitude => $self->latitude, longitude => $self->longitude, @@ -1065,7 +994,7 @@ sub pin_data { problem => $self, draggable => $opts{draggable}, type => $opts{type}, - base_url => $c->cobrand->relative_url_for_report($self), + base_url => $cobrand->relative_url_for_report($self), } }; @@ -1196,17 +1125,6 @@ has duplicates => ( }, ); -has traffic_management_options => ( - is => 'ro', - lazy => 1, - default => sub { - my $self = shift; - my $cobrand = $self->get_cobrand_logged; - $cobrand = $cobrand->call_hook(get_body_handler_for_problem => $self) || $cobrand; - return $cobrand->traffic_management_options; - }, -); - has inspection_log_entry => ( is => 'ro', lazy => 1, diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index b0a05d0b7..e5be14abf 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -179,6 +179,11 @@ sub check_password { } } +sub access_token { + my $self = shift; + return $self->get_extra_metadata('access_token'); +} + around password => sub { my ($orig, $self) = (shift, shift); if (@_) { @@ -444,7 +449,7 @@ sub permissions { my $body_id = $problem->bodies_str; - return unless $self->belongs_to_body($body_id); + return {} unless $self->belongs_to_body($body_id); my @permissions = grep { $_->{body_id} == $self->from_body->id } @{$self->body_permissions}; return { map { $_->{permission} => 1 } @permissions }; |