From e6e7805c29f185fbdc90ee4449280e4f6688a3c0 Mon Sep 17 00:00:00 2001 From: ivanscinote <138504771+ivanscinote@users.noreply.github.com> Date: Fri, 3 Nov 2023 13:19:46 +0100 Subject: [PATCH] Apply layout for Grover PDF library [SCI-9566] (#6547) --- app/assets/stylesheets/reports.scss | 1 + app/assets/stylesheets/reports_pdf.sass.scss | 1 + app/controllers/reports_controller.rb | 2 +- app/helpers/reports_helper.rb | 10 +- app/jobs/reports/pdf_job.rb | 301 ++++++++++++------ .../layouts/reports/footer_header.html.erb | 1 - app/views/reports/report.html.erb | 24 +- .../scinote_template/footer.html.erb | 4 +- .../scinote_template/header.html.erb | 48 ++- 9 files changed, 254 insertions(+), 138 deletions(-) diff --git a/app/assets/stylesheets/reports.scss b/app/assets/stylesheets/reports.scss index 5b6c58477..592aba862 100644 --- a/app/assets/stylesheets/reports.scss +++ b/app/assets/stylesheets/reports.scss @@ -233,6 +233,7 @@ label { /* GLOBAL REPORT ELEMENT STYLE */ .report-element { width: 100%; + max-width: 270mm; margin-bottom: 15px; diff --git a/app/assets/stylesheets/reports_pdf.sass.scss b/app/assets/stylesheets/reports_pdf.sass.scss index 7dc4a0091..f811678e9 100644 --- a/app/assets/stylesheets/reports_pdf.sass.scss +++ b/app/assets/stylesheets/reports_pdf.sass.scss @@ -16,6 +16,7 @@ .print-report { overflow-y: hidden !important; overflow-x: hidden !important; + padding-top: 0; } } diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb index dafa45d00..f05261718 100644 --- a/app/controllers/reports_controller.rb +++ b/app/controllers/reports_controller.rb @@ -53,7 +53,7 @@ class ReportsController < ApplicationController report = current_team.reports.new(project: @project) end - if lookup_context.template_exists?("reports/templates/#{template}/edit") + if Rails.root.join('app', 'views', 'reports', 'templates', template, 'edit.html.erb').exist? render json: { html: render_to_string( template: "reports/templates/#{template}/edit", diff --git a/app/helpers/reports_helper.rb b/app/helpers/reports_helper.rb index f789d50d6..48a276c2f 100644 --- a/app/helpers/reports_helper.rb +++ b/app/helpers/reports_helper.rb @@ -64,12 +64,10 @@ module ReportsHelper "[#{text}]".html_safe end - def font_awesome_cdn_link_tag - stylesheet_link_tag( - 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/fontawesome.min.css', - 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/regular.min.css', - 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/solid.min.css' - ) + def font_awesome_links + [{ url: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/fontawesome.min.css' }, + { url: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/regular.min.css' }, + { url: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/solid.min.css' }] end def filter_steps_for_report(steps, settings) diff --git a/app/jobs/reports/pdf_job.rb b/app/jobs/reports/pdf_job.rb index d2069d36f..86c166794 100644 --- a/app/jobs/reports/pdf_job.rb +++ b/app/jobs/reports/pdf_job.rb @@ -15,97 +15,171 @@ module Reports PREVIEW_EXTENSIONS = %w(docx pdf).freeze def perform(report_id, user_id:) - report = Report.find(report_id) - user = User.find(user_id) - file = Tempfile.new(['report', '.pdf'], binmode: true) - begin - template = - if Extends::REPORT_TEMPLATES.key?(report.settings[:template]&.to_sym) - report.settings[:template] - else - Extends::REPORT_TEMPLATES.keys.first.to_s - end - - raise StandardError, 'Report template not found!' if template.blank? - - I18n.backend.date_format = user.settings[:date_format] - ActionController::Renderer::RACK_KEY_TRANSLATION['warden'] ||= 'warden' - proxy = Warden::Proxy.new({}, Warden::Manager.new({})) - proxy.set_user(user, scope: :user, store: false) - ApplicationController.renderer.defaults[:http_host] = Rails.application.routes.default_url_options[:host] - renderer = ApplicationController.renderer.new(warden: proxy) - Rails.application.config.x.custom_sanitizer_config = build_custom_sanitizer_config - - header_html = renderer.render_to_string( - template: "reports/templates/#{template}/header", - layout: false, - locals: { report: report, user: user, logo: report_logo } - ) - footer_html = renderer.render_to_string( - template: "reports/templates/#{template}/footer", - layout: false, - locals: { report: report, user: user, logo: report_logo } - ) - report_html = renderer.render_to_string( - template: 'reports/report', - layout: false, - assigns: { settings: report.settings }, - locals: { report: report, user: user } - ) - - Grover.new( - report_html, - format: 'A4', - print_background: true, - margin: { top: '2cm', bottom: '5cm', left: '1cm', right: '2cm' }, - display_header_footer: true, - header_template: header_html, - footer_template: footer_html - ).to_pdf(file.path) - - file.rewind - - file = prepend_title_page(file, template, report, renderer) - - file = append_result_asset_previews(report, file, user) if report.settings.dig(:task, :file_results_previews) - - report.pdf_file.attach(io: file, filename: 'report.pdf') - report.pdf_ready! - - report_path = Rails.application.routes.url_helpers - .reports_path(team: report.team.id, preview_report_id: report.id, preview_type: :pdf) - notification = Notification.create( - type_of: :deliver, - title: I18n.t('projects.reports.index.generation.completed_pdf_notification_title'), - message: I18n.t('projects.reports.index.generation.completed_notification_message', - report_link: "#{escape_input(report.name)}", - team_name: escape_input(report.team.name)) - ) - notification.create_user_notification(user) - ensure - Rails.application.config.x.custom_sanitizer_config = nil - I18n.backend.date_format = nil - file.close(true) - end + @report = Report.find(report_id) + @user = User.find(user_id) + @file = Tempfile.new(['report', '.pdf'], binmode: true) + initialize_template + set_renderer_context + generate_pdf_content + process_attach_pdf_report_and_notify rescue StandardError => e - raise e if report.blank? + raise e if @report.blank? ActiveRecord::Base.no_touching do - report.pdf_error! + @report.pdf_error! end - Rails.logger.error("Couldn't generate PDF for Report with id: #{report.id}. Error:\n #{e.message}") + Rails.logger.error("Couldn't generate PDF for Report with id: #{@report.id}. Error:\n #{e.message}") raise e + ensure + Rails.application.config.x.custom_sanitizer_config = nil + I18n.backend.date_format = nil + @file.close(true) end private - def append_result_asset_previews(report, report_file, user) + def initialize_template + @template = if Extends::REPORT_TEMPLATES.key?(@report.settings[:template]&.to_sym) + @report.settings[:template] + else + Extends::REPORT_TEMPLATES.keys.first.to_s + end + + raise StandardError, 'Report template not found!' if @template.blank? + end + + def set_renderer_context + I18n.backend.date_format = @user.settings[:date_format] + ActionController::Renderer::RACK_KEY_TRANSLATION['warden'] ||= 'warden' + proxy = Warden::Proxy.new({}, Warden::Manager.new({})) + proxy.set_user(@user, scope: :user, store: false) + ApplicationController.renderer.defaults[:http_host] = Rails.application.routes.default_url_options[:host] + @renderer = ApplicationController.renderer.new(warden: proxy) + Rails.application.config.x.custom_sanitizer_config = build_custom_sanitizer_config + end + + def generate_pdf_content + @has_cover = Rails.root.join('app', 'views', 'reports', 'templates', @template, 'cover.html.erb').exist? + + render_header_footer_and_report + + gather_styles_and_scripts + + generate_pdf_file + end + + def render_header_footer_and_report + @header_html = @renderer.render_to_string( + template: "reports/templates/#{@template}/header", + layout: false, + locals: { report: @report, user: @user, logo: report_logo } + ) + @footer_html = @renderer.render_to_string( + template: "reports/templates/#{@template}/footer", + layout: false, + locals: { report: @report, user: @user, logo: report_logo } + ) + @report_html = @renderer.render_to_string( + template: 'reports/report', + layout: false, + assigns: { settings: @report.settings }, + locals: { report: @report, user: @user, has_cover: @has_cover } + ) + end + + def gather_styles_and_scripts + css_files = [ + 'application', + 'reports_pdf', + 'bootstrap_pack', + 'handsontable.formula' + ] + + javascript_files = [ + 'jquery_bundle', + 'handsontable.full', + 'lodash', + 'numeral', + 'numeric', + 'md5', + 'jstat', + 'formula', + 'parser', + 'ruleJS', + 'big.min', + 'handsontable.formula', + 'reports/content', + 'reports/template_helpers' + ] + + @style_tag_options = [] + @script_tag_options = [] + + @style_tag_options = css_files.map do |file_name| + { content: fetch_asset_content("#{file_name}.css") } + end + + @style_tag_options.concat(font_awesome_links) + + @script_tag_options = javascript_files.map do |file_name| + { content: fetch_asset_content("#{file_name}.js") } + end + end + + def generate_pdf_file + current_margin = extract_margins_from_header || + { top: '2cm', bottom: '2cm', left: '1cm', right: '1.5cm' } + + cover_pages_shift = cover_page_shift_from_template + + Grover.new( + @report_html, + format: 'A4', + print_background: true, + margin: current_margin, + display_header_footer: true, + header_template: @header_html, + footer_template: @footer_html, + style_tag_options: @style_tag_options, + script_tag_options: @script_tag_options, + page_ranges: "#{cover_pages_shift}-999999", + emulate_media: 'screen', + display_url: "#{Rails.application.config.force_ssl ? 'https' : 'http'}://" \ + "#{Rails.application.routes.default_url_options[:host]}" + ).to_pdf(@file.path) + end + + def process_attach_pdf_report_and_notify + @file.rewind + @file = prepend_title_page if @has_cover + @file = append_result_asset_previews if @report.settings.dig(:task, :file_results_previews) + + @report.pdf_file.attach(io: @file, filename: 'report.pdf') + @report.pdf_ready! + + create_notification_for_user + end + + def create_notification_for_user + report_path = Rails.application.routes.url_helpers + .reports_path(team: @report.team.id, preview_report_id: @report.id, preview_type: :pdf) + notification = Notification.create( + type_of: :deliver, + title: I18n.t('projects.reports.index.generation.completed_pdf_notification_title'), + message: I18n.t('projects.reports.index.generation.completed_notification_message', + report_link: "#{escape_input(@report.name)}", + team_name: escape_input(@report.team.name)) + ) + notification.create_user_notification(@user) + end + + def append_result_asset_previews Dir.mktmpdir do |tmp_dir| - report.report_elements.my_module.each do |my_module_element| - next unless can_read_my_module?(user, my_module_element.my_module) + @report.report_elements.my_module.each do |my_module_element| + next unless can_read_my_module?(@user, my_module_element.my_module) results = my_module_element.my_module.results - order_results_for_report(results, report.settings.dig(:task, :result_order)).each do |result| + order_results_for_report(results, @report.settings.dig(:task, :result_order)).each do |result| result.assets.each do |asset| next unless PREVIEW_EXTENSIONS.include?(asset.file.blob.filename.extension) @@ -114,13 +188,13 @@ module Reports asset.reload end asset.file_pdf_preview.open(tmpdir: tmp_dir) do |file| - report_file = merge_pdf_files(file, report_file) + @file = merge_pdf_files(file, @file) end end end end end - report_file + @file end def merge_pdf_files(file, report_file) @@ -149,14 +223,10 @@ module Reports merged_file end - def prepend_title_page(file, template, report, renderer) - unless File.exist?(Rails.root.join('app', 'views', 'reports', 'templates', template, 'cover')) - return file - end - + def prepend_title_page total_pages = 0 - IO.popen(['pdfinfo', file.path], 'r+') do |f| + IO.popen(['pdfinfo', @file.path], 'r+') do |f| total_pages = f.read.split("\n") .find { |i| i.split(':')[0] == 'Pages' } .gsub(/[^0-9]/, '') @@ -165,27 +235,33 @@ module Reports title_page = Tempfile.new(['title_page', '.pdf'], binmode: true) merged_file = Tempfile.new(['report', '.pdf'], binmode: true) - title_page_html = renderer.render_to_string( - template: "reports/templates/#{template}/cover", + title_page_html = @renderer.render_to_string( + template: "reports/templates/#{@template}/cover", layout: false, formats: :html, - locals: { report: report, total_pages: total_pages.to_i, logo: report_logo } + locals: { report: @report, total_pages: total_pages.to_i, logo: report_logo } ) Grover.new( title_page_html, - format: 'A4' + format: 'A4', + style_tag_options: @style_tag_options, + script_tag_options: @script_tag_options, + emulate_media: 'screen', + print_background: true, + display_url: "#{Rails.application.config.force_ssl ? 'https' : 'http'}://" \ + "#{Rails.application.routes.default_url_options[:host]}" ).to_pdf(title_page.path) title_page.rewind success = system( - 'pdfunite', title_page.path, file.path, merged_file.path + 'pdfunite', title_page.path, @file.path, merged_file.path ) raise StandardError, 'There was an error merging report and title page' unless success && File.file?(merged_file) - file.close(true) + @file.close(true) title_page.close(true) merged_file @@ -226,5 +302,44 @@ module Reports report_link: "#{escape_input(report.name)}", team_name: escape_input(report.team.name)) end + + def fetch_asset_content(asset_name) + Rails.application + .assets_manifest + .find_sources(asset_name) + .first + .to_s + .force_encoding(Encoding::UTF_8) + end + + def extract_margins_from_header + header_file_path = Rails.root.join('app', 'views', 'reports', 'templates', @template, 'header.html.erb') + return nil unless header_file_path.exist? + + content = File.read(header_file_path) + + margin_comment = content.match(//) + return nil unless margin_comment + + margins = {} + margin_comment[1].split(',').each do |margin_pair| + key, value = margin_pair.split(':').map(&:strip) + margins[key.to_sym] = value + end + + margins + end + + def cover_page_shift_from_template + cover_file_path = Rails.root.join('app', 'views', 'reports', 'templates', @template, 'cover.html.erb') + return 1 unless cover_file_path.exist? + + content = File.read(cover_file_path) + + cover_pages_comment = content.match(//) + return 2 unless cover_pages_comment + + cover_pages_comment[1].to_i + 1 + end end end diff --git a/app/views/layouts/reports/footer_header.html.erb b/app/views/layouts/reports/footer_header.html.erb index dc172d968..31ef3d159 100644 --- a/app/views/layouts/reports/footer_header.html.erb +++ b/app/views/layouts/reports/footer_header.html.erb @@ -5,6 +5,5 @@ <%= yield %> - <%= javascript_include_tag wicked_pdf_asset_base64("reports/template_helpers") %> diff --git a/app/views/reports/report.html.erb b/app/views/reports/report.html.erb index 997e976f3..742d6e7ee 100644 --- a/app/views/reports/report.html.erb +++ b/app/views/reports/report.html.erb @@ -1,32 +1,16 @@ - <%= wicked_pdf_stylesheet_link_tag "application" %> - <%= wicked_pdf_stylesheet_link_tag "reports_pdf" %> - <%= wicked_pdf_stylesheet_link_tag "bootstrap_pack" %> - <%= font_awesome_cdn_link_tag %> - <%= wicked_pdf_javascript_include_tag "jquery_bundle" %> - <%= wicked_pdf_javascript_include_tag "handsontable.full" %> - - <%= wicked_pdf_javascript_include_tag "lodash" %> - <%= wicked_pdf_javascript_include_tag "numeral" %> - <%= wicked_pdf_javascript_include_tag "numeric" %> - <%= wicked_pdf_javascript_include_tag "md5" %> - <%= wicked_pdf_javascript_include_tag "jstat" %> - <%= wicked_pdf_javascript_include_tag "formula" %> - <%= wicked_pdf_javascript_include_tag "parser" %> - <%= wicked_pdf_javascript_include_tag "ruleJS" %> - <%= wicked_pdf_javascript_include_tag "big.min" %> - <%= wicked_pdf_javascript_include_tag "handsontable.formula" %> - <%= wicked_pdf_stylesheet_link_tag "handsontable.formula" %> + + <% if has_cover %> +
+ <% end %> - - <%= wicked_pdf_javascript_include_tag "reports/content" %> diff --git a/app/views/reports/templates/scinote_template/footer.html.erb b/app/views/reports/templates/scinote_template/footer.html.erb index fa89161dd..c04b93659 100644 --- a/app/views/reports/templates/scinote_template/footer.html.erb +++ b/app/views/reports/templates/scinote_template/footer.html.erb @@ -1,8 +1,10 @@ diff --git a/app/views/reports/templates/scinote_template/header.html.erb b/app/views/reports/templates/scinote_template/header.html.erb index 4757469e1..1a7cebde0 100644 --- a/app/views/reports/templates/scinote_template/header.html.erb +++ b/app/views/reports/templates/scinote_template/header.html.erb @@ -1,24 +1,40 @@ + +