From b5c36d1886e02388af624712ac6ecddc34b7ccf7 Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Fri, 26 Sep 2025 16:40:21 +0100 Subject: [PATCH] Complete default report setup Move PDF functionality to `GADS::PDFGenerator` class --- lib/GADS.pm | 6 +- lib/GADS/PDFGenerator.pm | 178 +++++++++++++++++++++++++++++++ lib/GADS/Record.pm | 84 ++------------- lib/GADS/Schema/Result/Report.pm | 126 +++------------------- 4 files changed, 205 insertions(+), 189 deletions(-) create mode 100644 lib/GADS/PDFGenerator.pm diff --git a/lib/GADS.pm b/lib/GADS.pm index 62ba1fa71..1a94f837e 100644 --- a/lib/GADS.pm +++ b/lib/GADS.pm @@ -1948,8 +1948,10 @@ any qr{/(record|history|purge|purgehistory)/([0-9]+)} => require_login sub { if (defined param('pdf') && !$record->layout->no_download_pdf) { - my $pdf = $record->pdf->content; - return send_file(\$pdf, content_type => 'application/pdf', filename => "Record-".$record->current_id.".pdf" ); + my $site = var 'site' + or error __"No site configured"; + my $pdf = $record->pdf($site)->content; + return send_file( \$pdf, content_type => 'application/pdf', filename => "Record-".$record->current_id.".pdf"); } if (query_parameters->get('report')) diff --git a/lib/GADS/PDFGenerator.pm b/lib/GADS/PDFGenerator.pm new file mode 100644 index 000000000..178ea9ae6 --- /dev/null +++ b/lib/GADS/PDFGenerator.pm @@ -0,0 +1,178 @@ +package GADS::PDFGenerator; + +use strict; +use warnings; + +use Moo; + +use GADS::Config; + +use CtrlO::PDF; + +has site => ( + is => 'ro', + required => 1, +); + +has layouts => ( + is => 'ro', + required => 1, +); + +has record => ( + is => 'ro', + required => 1 +); + +has user => ( + is => 'ro', + required => 1, +); + +has security_marking => ( + is => 'ro', +); + +has _default_marking => ( + is => 'lazy', +); + +sub _build__default_marking { + return shift->site->read_security_marking; +} + +has _logo => ( + is => 'lazy', +); + +sub _build__logo { + return shift->site->create_temp_logo; +} + +sub build { + my ($self, %options) = @_; + + my $marking = $self->security_marking || $self->_default_marking; + my $logo = $self->_logo; + + my $pdf; + my $topmargin = 0; + + if ($logo) + { + $pdf = CtrlO::PDF->new( + header => $marking, + footer => $marking, + logo => $logo, + ); + + # Adjust the top margin to allow for the logo - 30px allows the table (below the logo) to not encroach on the logo when rendered + # This is used rather than overcomplicating and using image size to centre the header, and then having to "drop" the table down to avoid the logo + $topmargin = -30; + } + else + { + $pdf = CtrlO::PDF->new( + header => $marking, + footer => $marking, + ); + } + + $pdf->add_page; + + $pdf->heading($options{title}, topmargin => $topmargin); + $pdf->text($options{subtitle}, size => 14) if $options{subtitle}; + + my $hdr_props = { + repeat => 0, + justify => 'center', + font_size => 12, + bg_color => '#007c88', + fg_color => '#ffffff', + }; + + my $result = $self->layouts; + + my @cols = $self->record->presentation_map_columns(columns => $result); + my @topics = $self->record->get_topics(\@cols); + + my $i = 0; + foreach my $topic (@topics) + { + my $topic_name = $topic->{topic} ? $topic->{topic}->name : 'Other'; + my $fields = [ [$topic_name] ]; + + my $width = 0; + foreach my $col (@{ $topic->{columns} }) + { + if ($col->{data}->{selected_values}) + { + my $first = 1; + foreach my $c (@{ $col->{data}->{selected_values} }) + { + my $values = $c->{values}; + $width = + $width < (scalar(@$values) + 1) + ? scalar(@$values) + 1 + : $width; + push @$fields, [ $first ? $col->{name} : '', @$values ]; + $first = 0; + } + } + else + { + if ($col->{data}->{value}) + { + push @$fields, + [ $col->{name}, $col->{data}->{value} || "" ]; + } + else + { + push @$fields, [ $col->{name}, $col->{data}->{grade} ]; + } + $width = 2 if $width < 2; + } + } + + my $cell_props = []; + foreach my $d (@$fields) + { + my $has = @$d; + + # $max_fields does not include field name + my $gap = $width - $has + 1; + push @$d, undef for (1 .. $gap); + push @$cell_props, + [ (undef) x ($has - 1), { colspan => $gap + 1 }, ]; + } + + $pdf->table( + data => $fields, + header_props => $hdr_props, + border_c => '#007C88', + h_border_w => 1, + cell_props => $cell_props, + size => '4cm *', + ); + } + + my $now = DateTime->now; + my $format = GADS::Config->instance->dateformat; + $pdf->text( + 'Last edited by ' + . $self->record->edited_user->as_string . ' on ' + . $self->record->edited_time->as_string, + size => 10 + ); + $pdf->text( + 'Report generated by ' + . $self->user->value . ' on ' + . $now->format_cldr($format) . ' at ' + . $now->hms, + size => 10 + ); + + return $pdf; +} + +1; diff --git a/lib/GADS/Record.pm b/lib/GADS/Record.pm index 1dfbc64c1..f34962202 100644 --- a/lib/GADS/Record.pm +++ b/lib/GADS/Record.pm @@ -2721,83 +2721,21 @@ sub for_code } sub pdf -{ my $self = shift; - - my $dateformat = GADS::Config->instance->dateformat; - my $now = DateTime->now; - $now->set_time_zone('Europe/London'); - my $now_formatted = $now->format_cldr($dateformat)." at ".$now->hms; - my $updated = $self->edited_time->as_string; - - my $config = GADS::Config->instance; - my $header = $config && $config->gads && $config->gads->{header}; - my $pdf = CtrlO::PDF->new( - header => $header, - footer => "Downloaded by ".$self->user->value." on $now_formatted", - ); +{ my ($self, $site) = @_; - $pdf->add_page; - $pdf->heading('Record '.$self->current_id); - $pdf->heading('Last updated by '.$self->edited_user->as_string." on $updated", size => 12); + my $result = [$self->layout->all_user_read]; - my $data =[ - ['Field', 'Value'], - ]; - my $max_fields; - foreach my $col ($self->layout->all_user_read) - { - my $datum = $self->get_field_value($col); - next if $datum->dependent_not_shown; - if ($col->is_curcommon) - { - my $first = 1; - foreach my $line (@{$datum->values}) - { - my $field_count; - my @l = ($first ? $col->name : ''); - foreach my $v (@{$line->{values}}) - { - push @l, $v; - $field_count++; - } - push @$data, \@l; - $first = 0; - $max_fields = $field_count if !$max_fields || $max_fields < $field_count; - } - } - else { - push @$data, [ - $col->name, - $datum->as_string, - ], - } - } - - my $hdr_props = { - repeat => 1, - justify => 'center', - font_size => 8, - }; - - my $cell_props = []; - foreach my $d (@$data) - { - my $has = @$d; - # $max_fields does not include field name - my $gap = $max_fields - $has + 1; - push @$d, undef for (1..$gap); - push @$cell_props, [ - (undef) x ($has - 1), - {colspan => $gap + 1} - ]; - } - - $pdf->table( - data => $data, - cell_props => $cell_props, + my $generator = GADS::PDFGenerator->new( + site => $site, + layouts => $result, + record => $self, + user => $self->user, + security_marking => $self->layout->security_marking ); - $pdf; + my $pdf = $generator->build(title => "Record-" . $self->current_id . ".pdf"); + + return $pdf; } sub get_report diff --git a/lib/GADS/Schema/Result/Report.pm b/lib/GADS/Schema/Result/Report.pm index 5d47502a6..3d74e4e2e 100644 --- a/lib/GADS/Schema/Result/Report.pm +++ b/lib/GADS/Schema/Result/Report.pm @@ -13,6 +13,7 @@ use Log::Report 'linkspace'; use CtrlO::PDF 0.06; use PDF::Table 1.006; # Needed for colspan feature use GADS::Config; +use GADS::PDFGenerator; use Moo; extends 'DBIx::Class::Core'; @@ -290,125 +291,22 @@ Function to create a PDF of the report - it will return a PDF object sub create_pdf { my ($self, $record, $user) = @_; - my $marking = $self->_read_security_marking; - my $logo = $self->instance->site->create_temp_logo; - - my $pdf; - my $topmargin = 0; - - if ($logo) - { - $pdf = CtrlO::PDF->new( - header => $marking, - footer => $marking, - logo => $logo, - ); - -# Adjust the top margin to allow for the logo - 30px allows the table (below the logo) to not encroach on the logo when rendered -# This is used rather than overcomplicating and using image size to centre the header, and then having to "drop" the table down to avoid the logo - $topmargin = -30; - } - else - { - $pdf = CtrlO::PDF->new( - header => $marking, - footer => $marking, - ); - } - - $pdf->add_page; - $pdf->heading($self->title || $self->name, topmargin => $topmargin); - $pdf->text($self->description, size => 14) if $self->description; - - my $hdr_props = { - repeat => 0, - justify => 'center', - font_size => 12, - bg_color => '#007c88', - fg_color => '#ffffff', - }; - my %include = map { $_->layout_id => 1 } $self->report_layouts; my $result = [ grep $include{ $_->id }, @{ $record->columns_render } ]; - my @cols = $record->presentation_map_columns(columns => $result); - my @topics = $record->get_topics(\@cols); - - my $i = 0; - foreach my $topic (@topics) - { - my $topic_name = $topic->{topic} ? $topic->{topic}->name : 'Other'; - my $fields = [ [$topic_name] ]; - - my $width = 0; - foreach my $col (@{ $topic->{columns} }) - { - if ($col->{data}->{selected_values}) - { - my $first = 1; - foreach my $c (@{ $col->{data}->{selected_values} }) - { - my $values = $c->{values}; - $width = - $width < (scalar(@$values) + 1) - ? scalar(@$values) + 1 - : $width; - push @$fields, [ $first ? $col->{name} : '', @$values ]; - $first = 0; - } - } - else - { - if ($col->{data}->{value}) - { - push @$fields, - [ $col->{name}, $col->{data}->{value} || "" ]; - } - else - { - push @$fields, [ $col->{name}, $col->{data}->{grade} ]; - } - $width = 2 if $width < 2; - } - } - - my $cell_props = []; - foreach my $d (@$fields) - { - my $has = @$d; - - # $max_fields does not include field name - my $gap = $width - $has + 1; - push @$d, undef for (1 .. $gap); - push @$cell_props, - [ (undef) x ($has - 1), { colspan => $gap + 1 }, ]; - } + my $generator = GADS::PDFGenerator->new( + site => $self->instance->site, + instance => $self->instance, + layouts => $result, + record => $record, + user => $user, + security_marking => $self->_read_security_marking + ); - $pdf->table( - data => $fields, - header_props => $hdr_props, - border_c => '#007C88', - h_border_w => 1, - cell_props => $cell_props, - size => '4cm *', - ); - } + my %options = ( title => $self->title || $self->name || "Report" ); + $options{subtitle} = $self->description if $self->description; - my $now = DateTime->now; - my $format = GADS::Config->instance->dateformat; - $pdf->text( - 'Last edited by ' - . $record->edited_user->as_string . ' on ' - . $record->edited_time->as_string, - size => 10 - ); - $pdf->text( - 'Report generated by ' - . $user->value . ' on ' - . $now->format_cldr($format) . ' at ' - . $now->hms, - size => 10 - ); + my $pdf = $generator->build(%options); $pdf; }