aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/DB
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/DB')
-rw-r--r--perllib/FixMyStreet/DB/Factories.pm173
-rw-r--r--perllib/FixMyStreet/DB/Result/Body.pm34
-rw-r--r--perllib/FixMyStreet/DB/Result/Comment.pm113
-rw-r--r--perllib/FixMyStreet/DB/Result/Contact.pm36
-rw-r--r--perllib/FixMyStreet/DB/Result/ContactsHistory.pm10
-rw-r--r--perllib/FixMyStreet/DB/Result/Problem.pm201
-rw-r--r--perllib/FixMyStreet/DB/Result/ReportExtraFields.pm45
-rw-r--r--perllib/FixMyStreet/DB/Result/ResponsePriority.pm6
-rw-r--r--perllib/FixMyStreet/DB/Result/State.pm48
-rw-r--r--perllib/FixMyStreet/DB/Result/Translation.pm44
-rw-r--r--perllib/FixMyStreet/DB/Result/User.pm43
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/Body.pm11
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/Contact.pm15
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/DefectType.pm32
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/ReportExtraFields.pm25
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/ResponsePriority.pm24
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/ResponseTemplate.pm27
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/State.pm84
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/UserPlannedReport.pm12
-rw-r--r--perllib/FixMyStreet/DB/Schema.pm32
20 files changed, 767 insertions, 248 deletions
diff --git a/perllib/FixMyStreet/DB/Factories.pm b/perllib/FixMyStreet/DB/Factories.pm
new file mode 100644
index 000000000..ec4dd630a
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Factories.pm
@@ -0,0 +1,173 @@
+use FixMyStreet::DB;
+
+package FixMyStreet::DB::Factory::Base;
+
+use parent "DBIx::Class::Factory";
+
+sub find_or_create {
+ my ($class, $fields) = @_;
+ my $key_field = $class->key_field;
+ my $id = $class->get_fields($fields)->{$key_field};
+ my $rs = $class->_class_data->{resultset};
+ my $obj = $rs->find({ $key_field => $id });
+ return $obj if $obj;
+ return $class->create($fields);
+}
+
+#######################
+
+package FixMyStreet::DB::Factory::Problem;
+
+use parent "DBIx::Class::Factory";
+
+__PACKAGE__->resultset(FixMyStreet::DB->resultset("Problem"));
+
+__PACKAGE__->exclude(['body']);
+
+__PACKAGE__->fields({
+ postcode => '',
+ title => __PACKAGE__->seq(sub { 'Title #' . (shift()+1) }),
+ detail => __PACKAGE__->seq(sub { 'Detail #' . (shift()+1) }),
+ name => __PACKAGE__->callback(sub { shift->get('user')->name }),
+ bodies_str => __PACKAGE__->callback(sub { shift->get('body')->id }),
+ confirmed => \'current_timestamp',
+ whensent => \'current_timestamp',
+ state => 'confirmed',
+ cobrand => 'default',
+ latitude => 0,
+ longitude => 0,
+ areas => '',
+ used_map => 't',
+ anonymous => 'f',
+ category => 'Other',
+});
+
+#######################
+
+package FixMyStreet::DB::Factory::Body;
+
+use parent -norequire, "FixMyStreet::DB::Factory::Base";
+use mySociety::MaPit;
+
+__PACKAGE__->resultset(FixMyStreet::DB->resultset("Body"));
+
+__PACKAGE__->exclude(['area_id', 'categories']);
+
+__PACKAGE__->fields({
+ name => __PACKAGE__->callback(sub {
+ my $area_id = shift->get('area_id');
+ my $area = mySociety::MaPit::call('area', $area_id);
+ $area->{name};
+ }),
+ body_areas => __PACKAGE__->callback(sub {
+ my $area_id = shift->get('area_id');
+ [ { area_id => $area_id } ]
+ }),
+ contacts => __PACKAGE__->callback(sub {
+ my $categories = shift->get('categories');
+ push @$categories, 'Other' unless @$categories;
+ [ map { FixMyStreet::DB::Factory::Contact->get_fields({ category => $_ }) } @$categories ];
+ }),
+});
+
+sub key_field { 'id' }
+
+#######################
+
+package FixMyStreet::DB::Factory::Contact;
+
+use parent "DBIx::Class::Factory";
+
+__PACKAGE__->resultset(FixMyStreet::DB->resultset("Contact"));
+
+__PACKAGE__->fields({
+ body_id => __PACKAGE__->callback(sub {
+ my $fields = shift;
+ return $fields->get('body')->id if $fields->get('body');
+ }),
+ category => 'Other',
+ email => __PACKAGE__->callback(sub {
+ my $category = shift->get('category');
+ (my $email = lc $_) =~ s/ /-/g;
+ lc $category . '@example.org';
+ }),
+ state => 'confirmed',
+ editor => 'Factory',
+ whenedited => \'current_timestamp',
+ note => 'Created by factory',
+});
+
+#######################
+
+package FixMyStreet::DB::Factory::ResponseTemplate;
+
+use parent -norequire, "FixMyStreet::DB::Factory::Base";
+
+__PACKAGE__->resultset(FixMyStreet::DB->resultset("ResponseTemplate"));
+
+__PACKAGE__->fields({
+ text => __PACKAGE__->seq(sub { 'Template text #' . (shift()+1) }),
+});
+
+#######################
+
+package FixMyStreet::DB::Factory::ResponsePriority;
+
+use parent "DBIx::Class::Factory";
+
+__PACKAGE__->resultset(FixMyStreet::DB->resultset("ResponsePriority"));
+
+__PACKAGE__->fields({
+ name => __PACKAGE__->seq(sub { 'Priority #' . (shift()+1) }),
+ description => __PACKAGE__->seq(sub { 'Description #' . (shift()+1) }),
+});
+
+#######################
+
+package FixMyStreet::DB::Factory::Comment;
+
+use parent "DBIx::Class::Factory";
+
+__PACKAGE__->resultset(FixMyStreet::DB->resultset("Comment"));
+
+__PACKAGE__->fields({
+ anonymous => 'f',
+ name => __PACKAGE__->callback(sub { shift->get('user')->name }),
+ text => __PACKAGE__->seq(sub { 'Comment #' . (shift()+1) }),
+ confirmed => \'current_timestamp',
+ state => 'confirmed',
+ cobrand => 'default',
+ mark_fixed => 0,
+});
+
+#######################
+
+package FixMyStreet::DB::Factory::User;
+
+use parent -norequire, "FixMyStreet::DB::Factory::Base";
+
+__PACKAGE__->resultset(FixMyStreet::DB->resultset("User"));
+
+__PACKAGE__->exclude(['body', 'permissions']);
+
+__PACKAGE__->fields({
+ name => 'User',
+ email => 'user@example.org',
+ password => 'password',
+ from_body => __PACKAGE__->callback(sub {
+ my $fields = shift;
+ if (my $body = $fields->get('body')) {
+ return $body->id;
+ }
+ }),
+ user_body_permissions => __PACKAGE__->callback(sub {
+ my $fields = shift;
+ my $body = $fields->get('body');
+ my $permissions = $fields->get('permissions');
+ [ map { { body_id => $body->id, permission_type => $_ } } @$permissions ];
+ }),
+});
+
+sub key_field { 'email' }
+
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Body.pm b/perllib/FixMyStreet/DB/Result/Body.pm
index 82015ad2d..6481d5cfc 100644
--- a/perllib/FixMyStreet/DB/Result/Body.pm
+++ b/perllib/FixMyStreet/DB/Result/Body.pm
@@ -121,18 +121,52 @@ __PACKAGE__->has_many(
# Created by DBIx::Class::Schema::Loader v0.07035 @ 2017-02-13 15:11:11
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:BOJANVwg3kR/1VjDq0LykA
+use Moo;
+use namespace::clean;
+
+with 'FixMyStreet::Roles::Translatable';
+
sub url {
my ( $self, $c, $args ) = @_;
# XXX $areas_info was used here for Norway parent - needs body parents, I guess
return $c->uri_for( '/reports/' . $c->cobrand->short_name( $self ), $args || {} );
}
+__PACKAGE__->might_have(
+ "translations",
+ "FixMyStreet::DB::Result::Translation",
+ sub {
+ my $args = shift;
+ return {
+ "$args->{foreign_alias}.object_id" => { -ident => "$args->{self_alias}.id" },
+ "$args->{foreign_alias}.tbl" => { '=' => \"?" },
+ "$args->{foreign_alias}.col" => { '=' => \"?" },
+ "$args->{foreign_alias}.lang" => { '=' => \"?" },
+ };
+ },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+
+around name => \&translate_around;
+
sub areas {
my $self = shift;
my %ids = map { $_->area_id => 1 } $self->body_areas->all;
return \%ids;
}
+sub first_area_children {
+ my ( $self, $c ) = @_;
+
+ my $area_id = $self->body_areas->first->area_id;
+
+ my $children = mySociety::MaPit::call('area/children', $area_id,
+ type => $c->cobrand->area_types_children,
+ );
+
+ return $children;
+}
+
=head2 get_cobrand_handler
Get a cobrand object for this body, if there is one.
diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm
index cf1ba444d..562f29693 100644
--- a/perllib/FixMyStreet/DB/Result/Comment.pm
+++ b/perllib/FixMyStreet/DB/Result/Comment.pm
@@ -101,6 +101,7 @@ use Moo;
use namespace::clean -except => [ 'meta' ];
with 'FixMyStreet::Roles::Abuser',
+ 'FixMyStreet::Roles::Extra',
'FixMyStreet::Roles::PhotoSet';
my $stz = sub {
@@ -128,9 +129,10 @@ sub check_for_errors {
unless $self->text =~ m/\S/;
# Bromley Council custom character limit
- if ( $self->text && $self->problem && $self->problem->bodies_str
- && $self->problem->bodies_str eq '2482' && length($self->text) > 1750 ) {
- $errors{update} = sprintf( _('Updates are limited to %s characters in length. Please shorten your update'), 1750 );
+ if ( $self->text && $self->problem && $self->problem->bodies_str) {
+ if ($self->problem->to_body_named('Bromley') && length($self->text) > 1750) {
+ $errors{update} = sprintf( _('Updates are limited to %s characters in length. Please shorten your update'), 1750 );
+ }
}
return \%errors;
@@ -149,6 +151,11 @@ sub confirm {
$self->confirmed( \'current_timestamp' );
}
+sub url {
+ my $self = shift;
+ return "/report/" . $self->problem_id . '#update_' . $self->id;
+}
+
sub photos {
my $self = shift;
my $photoset = $self->get_photoset;
@@ -169,22 +176,6 @@ sub photos {
return \@photos;
}
-=head2 problem_state_display
-
-Returns a string suitable for display lookup in the update meta section.
-Removes the '- council/user' bit from fixed states.
-
-=cut
-
-sub problem_state_display {
- my $self = shift;
-
- my $state = $self->problem_state;
- $state =~ s/ -.*$//;
-
- return $state;
-}
-
=head2 latest_moderation_log_entry
Return most recent ModerationLog object
@@ -236,8 +227,6 @@ sub meta_line {
my $meta = '';
- $c->stash->{last_state} ||= '';
-
if ($self->anonymous or !$self->name) {
$meta = sprintf( _( 'Posted anonymously at %s' ), Utils::prettify_dt( $self->confirmed ) )
} elsif ($self->user->from_body) {
@@ -248,68 +237,54 @@ sub meta_line {
} elsif ($body eq 'Royal Borough of Greenwich') {
$body = "$body <img src='/cobrands/greenwich/favicon.png' alt=''>";
}
- if ($c->user_exists and $c->user->has_permission_to('view_body_contribute_details', $self->problem->bodies_str_ids)) {
- $meta = sprintf( _( 'Posted by <strong>%s</strong> (%s) at %s' ), $body, $user_name, Utils::prettify_dt( $self->confirmed ) );
+ my $can_view_contribute = $c->user_exists && $c->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 ) );
+ } else {
+ $meta = sprintf( _( 'Posted by <strong>%s</strong> at %s' ), $body, Utils::prettify_dt( $self->confirmed ) );
+ }
} else {
- $meta = sprintf( _( 'Posted by <strong>%s</strong> at %s' ), $body, Utils::prettify_dt( $self->confirmed ) );
+ if ($can_view_contribute) {
+ $meta = sprintf( _( 'Updated by <strong>%s</strong> (%s) at %s' ), $body, $user_name, Utils::prettify_dt( $self->confirmed ) );
+ } else {
+ $meta = sprintf( _( 'Updated by <strong>%s</strong> at %s' ), $body, Utils::prettify_dt( $self->confirmed ) );
+ }
}
} else {
$meta = sprintf( _( 'Posted by %s at %s' ), FixMyStreet::Template::html_filter($self->name), Utils::prettify_dt( $self->confirmed ) )
}
+ if ($self->get_extra_metadata('defect_raised')) {
+ $meta .= ', ' . _( 'and a defect raised' );
+ }
+
+ return $meta;
+};
+
+sub problem_state_display {
+ my ( $self, $c ) = @_;
+
my $update_state = '';
+ my $cobrand = $c->cobrand->moniker;
if ($self->mark_fixed) {
- $update_state = _( 'marked as fixed' );
+ return FixMyStreet::DB->resultset("State")->display('fixed', 1);
} elsif ($self->mark_open) {
- $update_state = _( 'reopened' );
+ return FixMyStreet::DB->resultset("State")->display('confirmed', 1);
} elsif ($self->problem_state) {
- my $state = $self->problem_state_display;
-
- if ($state eq 'confirmed') {
- if ($c->stash->{last_state}) {
- $update_state = _( 'reopened' )
- }
- } elsif ($state eq 'investigating') {
- $update_state = _( 'marked as investigating' )
- } elsif ($state eq 'planned') {
- $update_state = _( 'marked as planned' )
- } elsif ($state eq 'in progress') {
- $update_state = _( 'marked as in progress' )
- } elsif ($state eq 'action scheduled') {
- $update_state = _( 'marked as action scheduled' )
- } elsif ($state eq 'closed') {
- $update_state = _( 'marked as closed' )
- } elsif ($state eq 'fixed') {
- $update_state = _( 'marked as fixed' )
- } elsif ($state eq 'unable to fix') {
- $update_state = _( 'marked as no further action' )
- } elsif ($state eq 'not responsible') {
- $update_state = _( "marked as not the council's responsibility" )
- } elsif ($state eq 'duplicate') {
- $update_state = _( 'closed as a duplicate report' )
- } elsif ($state eq 'internal referral') {
- $update_state = _( 'marked as an internal referral' )
- }
-
- if ($c->cobrand->moniker eq 'bromley' || (
- $self->problem->bodies_str &&
- $self->problem->bodies_str eq '2482'
- )) {
- if ($state eq 'not responsible') {
- $update_state = 'marked as third party responsibility'
+ 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);
}
-
- }
-
- if ($update_state ne $c->stash->{last_state} and $update_state) {
- $meta .= ", $update_state";
}
- $c->stash->{last_state} = $update_state;
-
- return $meta;
-};
+ return $update_state;
+}
1;
diff --git a/perllib/FixMyStreet/DB/Result/Contact.pm b/perllib/FixMyStreet/DB/Result/Contact.pm
index a620b7358..f9cbf1c44 100644
--- a/perllib/FixMyStreet/DB/Result/Contact.pm
+++ b/perllib/FixMyStreet/DB/Result/Contact.pm
@@ -11,8 +11,17 @@ use base 'DBIx::Class::Core';
__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime", "EncodedColumn");
__PACKAGE__->table("contacts");
__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "contacts_id_seq",
+ },
"body_id",
{ data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+ "category",
+ { data_type => "text", default_value => "Other", is_nullable => 0 },
"email",
{ data_type => "text", is_nullable => 0 },
"editor",
@@ -21,19 +30,6 @@ __PACKAGE__->add_columns(
{ data_type => "timestamp", is_nullable => 0 },
"note",
{ data_type => "text", is_nullable => 0 },
- "confirmed",
- { data_type => "boolean", is_nullable => 0 },
- "category",
- { data_type => "text", default_value => "Other", is_nullable => 0 },
- "deleted",
- { data_type => "boolean", is_nullable => 0 },
- "id",
- {
- data_type => "integer",
- is_auto_increment => 1,
- is_nullable => 0,
- sequence => "contacts_id_seq",
- },
"extra",
{ data_type => "text", is_nullable => 1 },
"non_public",
@@ -46,6 +42,8 @@ __PACKAGE__->add_columns(
{ data_type => "text", default_value => "", is_nullable => 1 },
"send_method",
{ data_type => "text", is_nullable => 1 },
+ "state",
+ { data_type => "text", is_nullable => 0 },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->add_unique_constraint("contacts_body_id_category_idx", ["body_id", "category"]);
@@ -75,8 +73,8 @@ __PACKAGE__->has_many(
);
-# Created by DBIx::Class::Schema::Loader v0.07035 @ 2017-02-13 15:11:11
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:f9VepR/oPyr3z6PUpJ4w2A
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2017-07-08 20:45:04
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:t/VtPP11R8bbqPZdEVXffw
__PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn");
__PACKAGE__->rabx_column('extra');
@@ -84,12 +82,18 @@ __PACKAGE__->rabx_column('extra');
use Moo;
use namespace::clean -except => [ 'meta' ];
-with 'FixMyStreet::Roles::Extra';
+with 'FixMyStreet::Roles::Extra',
+ 'FixMyStreet::Roles::Translatable';
__PACKAGE__->many_to_many( response_templates => 'contact_response_templates', 'response_template' );
__PACKAGE__->many_to_many( response_priorities => 'contact_response_priorities', 'response_priority' );
__PACKAGE__->many_to_many( defect_types => 'contact_defect_types', 'defect_type' );
+sub category_display {
+ my $self = shift;
+ $self->translate_column('category');
+}
+
sub get_metadata_for_input {
my $self = shift;
my $id_field = $self->id_field;
diff --git a/perllib/FixMyStreet/DB/Result/ContactsHistory.pm b/perllib/FixMyStreet/DB/Result/ContactsHistory.pm
index 7126d91c9..c90bb9d66 100644
--- a/perllib/FixMyStreet/DB/Result/ContactsHistory.pm
+++ b/perllib/FixMyStreet/DB/Result/ContactsHistory.pm
@@ -26,22 +26,20 @@ __PACKAGE__->add_columns(
{ data_type => "text", default_value => "Other", is_nullable => 0 },
"email",
{ data_type => "text", is_nullable => 0 },
- "confirmed",
- { data_type => "boolean", is_nullable => 0 },
- "deleted",
- { data_type => "boolean", is_nullable => 0 },
"editor",
{ data_type => "text", is_nullable => 0 },
"whenedited",
{ data_type => "timestamp", is_nullable => 0 },
"note",
{ data_type => "text", is_nullable => 0 },
+ "state",
+ { data_type => "text", is_nullable => 0 },
);
__PACKAGE__->set_primary_key("contacts_history_id");
-# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-12-12 16:37:16
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:sxflEBBn0Mn0s3MroWnWFA
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2017-07-08 20:45:04
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:HTt0g29yXTM/WyHKN179FA
# You can replace this text with custom code or comments, and it will be preserved on regeneration
diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm
index 84db41490..3b622b561 100644
--- a/perllib/FixMyStreet/DB/Result/Problem.pm
+++ b/perllib/FixMyStreet/DB/Result/Problem.pm
@@ -206,6 +206,7 @@ my $IM = eval {
with 'FixMyStreet::Roles::Abuser',
'FixMyStreet::Roles::Extra',
+ 'FixMyStreet::Roles::Translatable',
'FixMyStreet::Roles::PhotoSet';
=head2
@@ -219,15 +220,8 @@ HASHREF.
=cut
sub open_states {
- my $states = {
- 'confirmed' => 1,
- 'investigating' => 1,
- 'in progress' => 1,
- 'planned' => 1,
- 'action scheduled' => 1,
- };
-
- return wantarray ? keys %{$states} : $states;
+ my @states = map { $_->label } @{FixMyStreet::DB->resultset("State")->open};
+ return wantarray ? @states : { map { $_ => 1 } @states };
}
=head2
@@ -241,13 +235,9 @@ HASHREF.
=cut
sub fixed_states {
- my $states = {
- 'fixed' => 1,
- 'fixed - user' => 1,
- 'fixed - council' => 1,
- };
-
- return wantarray ? keys %{ $states } : $states;
+ my @states = map { $_->label } @{FixMyStreet::DB->resultset("State")->fixed};
+ push @states, 'fixed - user', 'fixed - council' if @states;
+ return wantarray ? @states : { map { $_ => 1 } @states };
}
=head2
@@ -261,18 +251,10 @@ HASHREF.
=cut
sub closed_states {
- my $states = {
- 'closed' => 1,
- 'unable to fix' => 1,
- 'not responsible' => 1,
- 'duplicate' => 1,
- 'internal referral' => 1,
- };
-
- return wantarray ? keys %{$states} : $states;
+ my @states = map { $_->label } @{FixMyStreet::DB->resultset("State")->closed};
+ return wantarray ? @states : { map { $_ => 1 } @states };
}
-
=head2
@states = FixMyStreet::DB::Problem::all_states();
@@ -288,21 +270,10 @@ sub all_states {
'hidden' => 1,
'partial' => 1,
'unconfirmed' => 1,
- 'confirmed' => 1,
- 'investigating' => 1,
- 'in progress' => 1,
- 'planned' => 1,
- 'action scheduled' => 1,
- 'fixed' => 1,
'fixed - council' => 1,
'fixed - user' => 1,
- 'unable to fix' => 1,
- 'not responsible' => 1,
- 'duplicate' => 1,
- 'closed' => 1,
- 'internal referral' => 1,
};
-
+ map { $states->{$_->label} = 1 } @{FixMyStreet::DB->resultset("State")->states};
return wantarray ? keys %{$states} : $states;
}
@@ -322,75 +293,31 @@ my $hidden_states = {
'unconfirmed' => 1,
};
-my $visible_states = {
- map {
- $hidden_states->{$_} ? () : ($_ => 1)
- } all_states()
-};
- ## e.g.:
- # 'confirmed' => 1,
- # 'investigating' => 1,
- # 'in progress' => 1,
- # 'planned' => 1,
- # 'action scheduled' => 1,
- # 'fixed' => 1,
- # 'fixed - council' => 1,
- # 'fixed - user' => 1,
- # 'unable to fix' => 1,
- # 'not responsible' => 1,
- # 'duplicate' => 1,
- # 'closed' => 1,
- # 'internal referral' => 1,
-
sub hidden_states {
return wantarray ? keys %{$hidden_states} : $hidden_states;
}
sub visible_states {
- return wantarray ? keys %{$visible_states} : $visible_states;
+ my %visible_states = map {
+ $hidden_states->{$_} ? () : ($_ => 1)
+ } all_states();
+ return wantarray ? keys %visible_states : \%visible_states;
}
sub visible_states_add {
my ($self, @states) = @_;
for my $state (@states) {
delete $hidden_states->{$state};
- $visible_states->{$state} = 1;
}
}
sub visible_states_remove {
my ($self, @states) = @_;
for my $state (@states) {
- delete $visible_states->{$state};
$hidden_states->{$state} = 1;
}
}
-=head2
-
- @states = FixMyStreet::DB::Problem::council_states();
-
-Get a list of states that are availble to council users. If called in
-array context then returns an array of names, otherwise returns a
-HASHREF.
-
-=cut
-sub council_states {
- my $states = {
- 'confirmed' => 1,
- 'investigating' => 1,
- 'action scheduled' => 1,
- 'in progress' => 1,
- 'fixed - council' => 1,
- 'unable to fix' => 1,
- 'not responsible' => 1,
- 'duplicate' => 1,
- 'internal referral' => 1,
- };
-
- return wantarray ? keys %{$states} : $states;
-}
-
my $stz = sub {
my ( $orig, $self ) = ( shift, shift );
my $s = $self->$orig(@_);
@@ -456,12 +383,6 @@ sub check_for_errors {
$errors{category} = _('Please choose a category');
$self->category(undef);
}
- elsif ($self->category
- && $self->category eq _('-- Pick a property type --') )
- {
- $errors{category} = _('Please choose a property type');
- $self->category(undef);
- }
return \%errors;
}
@@ -489,6 +410,11 @@ sub confirm {
return 1;
}
+sub category_display {
+ my $self = shift;
+ $self->translate_column('category');
+}
+
sub bodies_str_ids {
my $self = shift;
return [] unless $self->bodies_str;
@@ -502,12 +428,36 @@ Returns a hashref of bodies to which a report was sent.
=cut
-sub bodies($) {
+has bodies => (
+ is => 'ro',
+ lazy => 1,
+ default => sub {
+ my $self = shift;
+ return {} unless $self->bodies_str;
+ my $cache = $self->result_source->schema->cache;
+ return $cache->{bodies}{$self->bodies_str} if $cache->{bodies}{$self->bodies_str};
+
+ my $bodies = $self->bodies_str_ids;
+ my @bodies = $self->result_source->schema->resultset('Body')->search(
+ { id => $bodies },
+ { prefetch => 'body_areas' },
+ )->all;
+ $cache->{bodies}{$self->bodies_str} = { map { $_->id => $_ } @bodies };
+ return $cache->{bodies}{$self->bodies_str};
+ },
+);
+
+sub body_names($) {
my $self = shift;
- return {} unless $self->bodies_str;
- my $bodies = $self->bodies_str_ids;
- my @bodies = $self->result_source->schema->resultset('Body')->search({ id => $bodies })->all;
- return { map { $_->id => $_ } @bodies };
+ my $bodies = $self->bodies;
+ my @names = map { $_->name } values %$bodies;
+ return \@names;
+}
+
+sub to_body_named($$) {
+ my ($self, $re) = @_;
+ my $names = join(',,', @{$self->body_names});
+ $names =~ /$re/;
}
=head2 url
@@ -609,19 +559,6 @@ sub is_visible {
return exists $self->visible_states->{ $self->state } ? 1 : 0;
}
-=head2 state_display
-
-Returns a string suitable for display lookup in the update meta section.
-Removes the '- council/user' bit from fixed states.
-
-=cut
-
-sub state_display {
- my $self = shift;
- (my $state = $self->state) =~ s/ -.*$//;
- return $state;
-}
-
=head2 meta_line
Returns a string to be used on a problem report page, describing some of the
@@ -635,7 +572,7 @@ sub meta_line {
my $date_time = Utils::prettify_dt( $problem->confirmed );
my $meta = '';
- my $category = $problem->category;
+ my $category = $problem->category_display;
$category = $c->cobrand->call_hook(change_category_text => $category) || $category;
if ( $problem->anonymous ) {
@@ -787,7 +724,7 @@ sub defect_types {
# Note: this only makes sense when called on a problem that has been sent!
sub can_display_external_id {
my $self = shift;
- if ($self->external_id && $self->send_method_used && $self->bodies_str =~ /(2237|2550)/) {
+ if ($self->external_id && $self->send_method_used && $self->to_body_named('Oxfordshire|Angus')) {
return 1;
}
return 0;
@@ -924,6 +861,7 @@ sub as_hashref {
latitude => $self->latitude,
longitude => $self->longitude,
postcode => $self->postcode,
+ areas => $self->areas,
state => $self->state,
state_t => _( $self->state ),
used_map => $self->used_map,
@@ -953,15 +891,21 @@ sub photos {
my $id = $self->id;
my @photos = map {
my $cachebust = substr($_, 0, 8);
+ # Some Varnish configurations (e.g. on mySociety infra) strip cookies from
+ # images, which means image requests will be redirected to the login page
+ # 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 ($hash, $format) = split /\./, $_;
{
id => $hash,
- url_temp => "/photo/temp.$hash.$format",
- url_temp_full => "/photo/fulltemp.$hash.$format",
- 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",
+ 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",
idx => $i++,
}
} $photoset->all_ids;
@@ -1013,13 +957,14 @@ has get_cobrand_logged => (
sub pin_data {
my ($self, $c, $page, %opts) = @_;
my $colour = $c->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;
{
latitude => $self->latitude,
longitude => $self->longitude,
colour => $colour,
id => $self->id,
- title => $opts{private} ? $self->title : $self->title_safe,
+ title => $title,
problem => $self,
type => $opts{type},
}
@@ -1082,6 +1027,7 @@ sub static_map {
if ($pin) {
my $im = Image::Magick->new;
$im->read(FixMyStreet->path_to('web', 'i', 'pin-yellow.png'));
+ $im->Scale( geometry => '48x64' );
$image->Composite(image => $im, gravity => 'NorthWest',
x => $pin->{px} - 24, y => $pin->{py} - 64);
}
@@ -1113,6 +1059,16 @@ has shortlisted_user => (
},
);
+sub set_duplicate_of {
+ my ($self, $other_id) = @_;
+ $self->set_extra_metadata( duplicate_of => $other_id );
+ my $dupe = $self->result_source->schema->resultset("Problem")->find($other_id);
+ my $dupes_duplicates = $dupe->get_extra_metadata('duplicates') || [];
+ push @$dupes_duplicates, $self->id;
+ $dupe->set_extra_metadata( duplicates => $dupes_duplicates );
+ $dupe->update;
+}
+
has duplicate_of => (
is => 'ro',
lazy => 1,
@@ -1130,8 +1086,9 @@ has duplicates => (
lazy => 1,
default => sub {
my $self = shift;
- my $rabx_id = RABX::serialise( $self->id );
- my @duplicates = $self->result_source->schema->resultset('Problem')->search({ extra => { like => "\%duplicate_of,$rabx_id%" } })->all;
+ my $duplicates = $self->get_extra_metadata("duplicates") || [];
+ return [] unless $duplicates && @$duplicates;
+ my @duplicates = $self->result_source->schema->resultset('Problem')->search({ id => $duplicates })->all;
return \@duplicates;
},
);
diff --git a/perllib/FixMyStreet/DB/Result/ReportExtraFields.pm b/perllib/FixMyStreet/DB/Result/ReportExtraFields.pm
new file mode 100644
index 000000000..27a6bd2c6
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/ReportExtraFields.pm
@@ -0,0 +1,45 @@
+use utf8;
+package FixMyStreet::DB::Result::ReportExtraFields;
+
+# 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("report_extra_fields");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "report_extra_fields_id_seq",
+ },
+ "name",
+ { data_type => "text", is_nullable => 0 },
+ "cobrand",
+ { data_type => "text", is_nullable => 1 },
+ "language",
+ { data_type => "text", is_nullable => 1 },
+ "extra",
+ { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2017-07-28 09:51:34
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:LkfbsUInnEyXowdcCEPjUQ
+
+__PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn");
+__PACKAGE__->rabx_column('extra');
+
+use Moo;
+use namespace::clean -except => [ 'meta' ];
+
+with 'FixMyStreet::Roles::Extra';
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/DB/Result/ResponsePriority.pm b/perllib/FixMyStreet/DB/Result/ResponsePriority.pm
index 44635d174..df54cfa08 100644
--- a/perllib/FixMyStreet/DB/Result/ResponsePriority.pm
+++ b/perllib/FixMyStreet/DB/Result/ResponsePriority.pm
@@ -28,6 +28,8 @@ __PACKAGE__->add_columns(
{ data_type => "text", is_nullable => 1 },
"external_id",
{ data_type => "text", is_nullable => 1 },
+ "is_default",
+ { data_type => "boolean", default_value => \"false", is_nullable => 0 },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->add_unique_constraint("response_priorities_body_id_name_key", ["body_id", "name"]);
@@ -51,8 +53,8 @@ __PACKAGE__->has_many(
);
-# Created by DBIx::Class::Schema::Loader v0.07035 @ 2016-12-14 17:12:09
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:glsO0fLK6fNvg4TmW1DMPg
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2017-09-12 09:32:53
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:JBIHFnaLvXCAUjgwTSB3CQ
__PACKAGE__->many_to_many( contacts => 'contact_response_priorities', 'contact' );
diff --git a/perllib/FixMyStreet/DB/Result/State.pm b/perllib/FixMyStreet/DB/Result/State.pm
new file mode 100644
index 000000000..b8a35d42b
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/State.pm
@@ -0,0 +1,48 @@
+use utf8;
+package FixMyStreet::DB::Result::State;
+
+# 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("state");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "state_id_seq",
+ },
+ "label",
+ { data_type => "text", is_nullable => 0 },
+ "type",
+ { data_type => "text", is_nullable => 0 },
+ "name",
+ { data_type => "text", is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->add_unique_constraint("state_label_key", ["label"]);
+__PACKAGE__->add_unique_constraint("state_name_key", ["name"]);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2017-08-22 15:17:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dvtAOpeYqEF9T3otHHgLqw
+
+use Moo;
+use namespace::clean;
+
+with 'FixMyStreet::Roles::Translatable';
+
+sub msgstr {
+ my $self = shift;
+ my $lang = $self->result_source->schema->lang;
+ return $self->name unless $lang && $self->translated->{name}{$lang};
+ return $self->translated->{name}{$lang}{msgstr};
+}
+
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Translation.pm b/perllib/FixMyStreet/DB/Result/Translation.pm
new file mode 100644
index 000000000..fafc7ccf1
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/Translation.pm
@@ -0,0 +1,44 @@
+use utf8;
+package FixMyStreet::DB::Result::Translation;
+
+# 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("translation");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "translation_id_seq",
+ },
+ "tbl",
+ { data_type => "text", is_nullable => 0 },
+ "object_id",
+ { data_type => "integer", is_nullable => 0 },
+ "col",
+ { data_type => "text", is_nullable => 0 },
+ "lang",
+ { data_type => "text", is_nullable => 0 },
+ "msgstr",
+ { data_type => "text", is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->add_unique_constraint(
+ "translation_tbl_object_id_col_lang_key",
+ ["tbl", "object_id", "col", "lang"],
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2017-07-14 23:24:32
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:///VNqg4BOuO29xKhnY8vw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm
index cf6de9a76..19adf5d49 100644
--- a/perllib/FixMyStreet/DB/Result/User.pm
+++ b/perllib/FixMyStreet/DB/Result/User.pm
@@ -204,6 +204,27 @@ sub alert_for_problem {
} );
}
+=head2 create_alert
+
+Sign a user up to receive alerts on a given problem
+
+=cut
+
+sub create_alert {
+ my ( $self, $id, $options ) = @_;
+ my $alert = $self->alert_for_problem($id);
+
+ unless ( $alert ) {
+ $alert = $self->alerts->create({
+ %$options,
+ alert_type => 'new_updates',
+ parameter => $id,
+ });
+ }
+
+ $alert->confirm();
+}
+
sub body {
my $self = shift;
return '' unless $self->from_body;
@@ -274,6 +295,16 @@ sub permissions {
sub has_permission_to {
my ($self, $permission_type, $body_ids) = @_;
+ # Nobody, including superusers, can have a permission which isn't available
+ # in the current cobrand.
+ my $cobrand = $self->result_source->schema->cobrand;
+ my $cobrand_perms = $cobrand->available_permissions;
+ my %available = map { %$_ } values %$cobrand_perms;
+ # The 'trusted' permission is never set in the cobrand's
+ # available_permissions (see note there in Default.pm) so include it here.
+ $available{trusted} = 1;
+ return 0 unless $available{$permission_type};
+
return 1 if $self->is_superuser;
return 0 if !$body_ids || (ref $body_ids && !@$body_ids);
$body_ids = [ $body_ids ] unless ref $body_ids;
@@ -391,9 +422,19 @@ sub active_planned_reports {
$self->planned_reports->search({ removed => undef });
}
+has active_user_planned_reports => (
+ is => 'ro',
+ lazy => 1,
+ default => sub {
+ my $self = shift;
+ [ $self->user_planned_reports->search({ removed => undef })->all ];
+ },
+);
+
sub is_planned_report {
my ($self, $problem) = @_;
- return $self->active_planned_reports->find({ id => $problem->id });
+ my $id = $problem->id;
+ return scalar grep { $_->report_id == $id } @{$self->active_user_planned_reports};
}
sub update_reputation {
diff --git a/perllib/FixMyStreet/DB/ResultSet/Body.pm b/perllib/FixMyStreet/DB/ResultSet/Body.pm
index 6802ed604..e79d038b1 100644
--- a/perllib/FixMyStreet/DB/ResultSet/Body.pm
+++ b/perllib/FixMyStreet/DB/ResultSet/Body.pm
@@ -14,4 +14,15 @@ sub for_areas {
return $result;
}
+sub all_translated {
+ my $rs = shift;
+ my $schema = $rs->result_source->schema;
+ my @bodies = $rs->search(undef, {
+ '+columns' => { 'msgstr' => 'translations.msgstr' },
+ join => 'translations',
+ bind => [ 'name', $schema->lang, 'body' ],
+ })->all;
+ return @bodies;
+}
+
1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/Contact.pm b/perllib/FixMyStreet/DB/ResultSet/Contact.pm
index f402b5461..8ef6d1ac5 100644
--- a/perllib/FixMyStreet/DB/ResultSet/Contact.pm
+++ b/perllib/FixMyStreet/DB/ResultSet/Contact.pm
@@ -10,13 +10,18 @@ sub me { join('.', shift->current_source_alias, shift || q{}) }
$rs = $rs->not_deleted();
-Filter down to not deleted contacts - which have C<deleted> set to false;
+Filter down to not deleted contacts (so active or inactive).
=cut
sub not_deleted {
my $rs = shift;
- return $rs->search( { $rs->me('deleted') => 0 } );
+ return $rs->search( { $rs->me('state') => { '!=' => 'deleted' } } );
+}
+
+sub active {
+ my $rs = shift;
+ $rs->search( { $rs->me('state') => [ 'unconfirmed', 'confirmed' ] } );
}
sub summary_count {
@@ -25,9 +30,9 @@ sub summary_count {
return $rs->search(
$restriction,
{
- group_by => ['confirmed'],
- select => [ 'confirmed', { count => 'id' } ],
- as => [qw/confirmed confirmed_count/]
+ group_by => ['state'],
+ select => [ 'state', { count => 'id' } ],
+ as => [qw/state state_count/]
}
);
}
diff --git a/perllib/FixMyStreet/DB/ResultSet/DefectType.pm b/perllib/FixMyStreet/DB/ResultSet/DefectType.pm
index a873ef252..b2ef77f7c 100644
--- a/perllib/FixMyStreet/DB/ResultSet/DefectType.pm
+++ b/perllib/FixMyStreet/DB/ResultSet/DefectType.pm
@@ -3,20 +3,26 @@ use base 'DBIx::Class::ResultSet';
use strict;
use warnings;
+use Moo;
+use HTML::Entities;
-sub for_bodies {
- my ($rs, $bodies, $category) = @_;
- my $attrs = {
- 'me.body_id' => $bodies,
- };
- if ($category) {
- $attrs->{'contact.category'} = [ $category, undef ];
- }
- $rs->search($attrs, {
- order_by => 'name',
- join => { 'contact_defect_types' => 'contact' },
- distinct => 1,
- });
+with('FixMyStreet::Roles::ContactExtra');
+
+sub join_table {
+ return 'contact_defect_types';
+}
+
+sub map_extras {
+ my ($rs, @ts) = @_;
+ return map {
+ my $meta = $_->get_extra_metadata();
+ my %extra = map { $_ => encode_entities($meta->{$_}) } keys %$meta;
+ {
+ id => $_->id,
+ name => encode_entities($_->name),
+ extra => \%extra
+ }
+ } @ts;
}
1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/ReportExtraFields.pm b/perllib/FixMyStreet/DB/ResultSet/ReportExtraFields.pm
new file mode 100644
index 000000000..1348df3c2
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/ReportExtraFields.pm
@@ -0,0 +1,25 @@
+package FixMyStreet::DB::ResultSet::ReportExtraFields;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+
+sub for_cobrand {
+ my ( $rs, $cobrand ) = @_;
+
+ my $result = $rs->search(
+ { cobrand => [ undef, $cobrand->moniker, '' ] }
+ );
+ return $result;
+}
+
+sub for_language {
+ my ( $rs, $language ) = @_;
+
+ my $result = $rs->search(
+ { language => [ undef, $language, '' ] }
+ );
+ return $result;
+}
+
+1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/ResponsePriority.pm b/perllib/FixMyStreet/DB/ResultSet/ResponsePriority.pm
index aa9c426f4..89bb4dfd7 100644
--- a/perllib/FixMyStreet/DB/ResultSet/ResponsePriority.pm
+++ b/perllib/FixMyStreet/DB/ResultSet/ResponsePriority.pm
@@ -3,20 +3,18 @@ use base 'DBIx::Class::ResultSet';
use strict;
use warnings;
+use Moo;
+use HTML::Entities;
-sub for_bodies {
- my ($rs, $bodies, $category) = @_;
- my $attrs = {
- 'me.body_id' => $bodies,
- };
- if ($category) {
- $attrs->{'contact.category'} = [ $category, undef ];
- }
- $rs->search($attrs, {
- order_by => 'name',
- join => { 'contact_response_priorities' => 'contact' },
- distinct => 1,
- });
+with('FixMyStreet::Roles::ContactExtra');
+
+sub join_table {
+ return 'contact_response_priorities';
+}
+
+sub map_extras {
+ my ($rs, @ts) = @_;
+ return map { { id => $_->id, name => encode_entities($_->name) } } @ts;
}
1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/ResponseTemplate.pm b/perllib/FixMyStreet/DB/ResultSet/ResponseTemplate.pm
new file mode 100644
index 000000000..aa070daa3
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/ResponseTemplate.pm
@@ -0,0 +1,27 @@
+package FixMyStreet::DB::ResultSet::ResponseTemplate;
+use base 'DBIx::Class::ResultSet';
+
+use Moo;
+use HTML::Entities;
+
+with('FixMyStreet::Roles::ContactExtra');
+
+sub join_table {
+ return 'contact_response_templates';
+}
+
+sub name_column {
+ 'title';
+}
+
+sub map_extras {
+ my ($rs, @ts) = @_;
+ return map {
+ my $out = { id => encode_entities($_->text), name => encode_entities($_->title) };
+ $out->{state} = encode_entities($_->state) if $_->state;
+ $out;
+ } @ts;
+}
+
+1;
+
diff --git a/perllib/FixMyStreet/DB/ResultSet/State.pm b/perllib/FixMyStreet/DB/ResultSet/State.pm
new file mode 100644
index 000000000..ac13ec2a4
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/State.pm
@@ -0,0 +1,84 @@
+package FixMyStreet::DB::ResultSet::State;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+use Memcached;
+
+sub _hardcoded_states {
+ my $rs = shift;
+ # These are translated on use, not here
+ my $open = $rs->new({ id => -1, label => 'confirmed', type => 'open', name => "Open" });
+ my $closed = $rs->new({ id => -2, label => 'closed', type => 'closed', name => "Closed" });
+ return ($open, $closed);
+}
+
+# As states will change rarely, and then only through the admin,
+# we cache these in the package on first use, and clear on update.
+
+sub clear {
+ Memcached::set('states', '');
+}
+
+sub states {
+ my $rs = shift;
+
+ my $states = Memcached::get('states');
+ if ($states) {
+ # Need to reattach schema
+ $states->[0]->result_source->schema( $rs->result_source->schema ) if $states->[0];
+ return $states;
+ }
+
+ # Pick up and cache any translations
+ my $q = $rs->result_source->schema->resultset("Translation")->search({
+ tbl => 'state',
+ col => 'name',
+ });
+ my %trans;
+ $trans{$_->object_id}{$_->lang} = { id => $_->id, msgstr => $_->msgstr } foreach $q->all;
+
+ my @states = ($rs->_hardcoded_states, $rs->search(undef, { order_by => 'label' })->all);
+ $_->translated->{name} = $trans{$_->id} || {} foreach @states;
+ $states = \@states;
+ Memcached::set('states', $states);
+ return $states;
+}
+
+# Some functions to provide filters on the above data
+
+sub open { [ $_[0]->_filter(sub { $_->type eq 'open' }) ] }
+sub closed { [ $_[0]->_filter(sub { $_->type eq 'closed' }) ] }
+sub fixed { [ $_[0]->_filter(sub { $_->type eq 'fixed' }) ] }
+
+# We sometimes have only a state label to display, no associated object.
+# This function can be used to return that label's display name.
+
+sub display {
+ my ($rs, $label, $single_fixed) = @_;
+ my $unchanging = {
+ unconfirmed => _("Unconfirmed"),
+ hidden => _("Hidden"),
+ partial => _("Partial"),
+ 'fixed - council' => _("Fixed - Council"),
+ 'fixed - user' => _("Fixed - User"),
+ };
+ my $translate_now = {
+ confirmed => _("Open"),
+ closed => _("Closed"),
+ };
+ $label = 'fixed' if $single_fixed && $label =~ /^fixed - (council|user)$/;
+ return $unchanging->{$label} if $unchanging->{$label};
+ my ($state) = $rs->_filter(sub { $_->label eq $label });
+ return $label unless $state;
+ $state->name($translate_now->{$label}) if $translate_now->{$label};
+ return $state->msgstr;
+}
+
+sub _filter {
+ my ($rs, $fn) = @_;
+ my $states = $rs->states;
+ grep &$fn, @$states;
+}
+
+1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/UserPlannedReport.pm b/perllib/FixMyStreet/DB/ResultSet/UserPlannedReport.pm
index 7e16e2dd3..460a4912e 100644
--- a/perllib/FixMyStreet/DB/ResultSet/UserPlannedReport.pm
+++ b/perllib/FixMyStreet/DB/ResultSet/UserPlannedReport.pm
@@ -6,7 +6,17 @@ use warnings;
sub active {
my $rs = shift;
- $rs->search({ removed => undef });
+
+ # If we have been prefetched we can't use `active` as that'll blow away the
+ # cache and query the DB due to the `removed IS NULL` clause. So let's do
+ # the filtering here instead, if the query has been prefetched.
+ if ( $rs->get_cache ) {
+ my @users = grep { !defined($_->removed) } $rs->all;
+ $rs->set_cache(\@users);
+ $rs;
+ } else {
+ $rs->search({ removed => undef });
+ }
}
sub for_report {
diff --git a/perllib/FixMyStreet/DB/Schema.pm b/perllib/FixMyStreet/DB/Schema.pm
new file mode 100644
index 000000000..be39069d8
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Schema.pm
@@ -0,0 +1,32 @@
+use utf8;
+package FixMyStreet::DB::Schema;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Schema';
+
+__PACKAGE__->load_namespaces(
+ result_namespace => "+FixMyStreet::DB::Result",
+ resultset_namespace => "+FixMyStreet::DB::ResultSet",
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07035 @ 2017-07-13 14:15:09
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:UpH30RXb6SbCqRv2FPmpkg
+
+use Moo;
+use FixMyStreet;
+
+__PACKAGE__->connection(FixMyStreet->dbic_connect_info);
+
+has lang => ( is => 'rw' );
+
+has cobrand => ( is => 'rw' );
+
+has cache => ( is => 'rw', lazy => 1, default => sub { {} } );
+
+1;