diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb index ae97316..589b7f8 100644 --- a/app/controllers/plans_controller.rb +++ b/app/controllers/plans_controller.rb @@ -234,58 +234,33 @@ def export - @plan = Plan.find(params[:id]) + @plan = Plan.includes(:answers).joins(:answers).find(params[:id]) authorize @plan - # We should re-work this into something more useful than creating a new one - # every time a plan gets exported - @exported_plan = ExportedPlan.new.tap do |ep| - ep.plan = @plan - ep.phase_id = params[:phase_id] - ep.user = current_user - ep.format = params[:format].to_sym - plan_settings = @plan.settings(:export) + @show_coversheet = params[:export][:project_details].present? + @show_sections_questions = params[:export][:question_headings].present? + @show_unanswered = params[:export][:unanswered_questions].present? - Settings::Template::DEFAULT_SETTINGS.each do |key, value| - ep.settings(:export).send("#{key}=", plan_settings.send(key)) + @hash = @plan.as_pdf(@show_coversheet) + @formatting = @plan.settings(:export).formatting + file_name = @plan.title.gsub(/ /, "_") + + respond_to do |format| + format.html + format.csv { send_data @exported_plan.as_csv(@sections, @unanswered_question, @question_headings), filename: "#{file_name}.csv" } + format.text { send_data @exported_plan.as_txt(@sections, @unanswered_question, @question_headings, @show_details), filename: "#{file_name}.txt" } + format.docx { render docx: 'export', filename: "#{file_name}.docx" } + format.pdf do + render pdf: file_name, + margin: @formatting[:margin], + footer: { + center: _('Created using the %{application_name}. Last modified %{date}') % {application_name: Rails.configuration.branding[:application][:name], date: l(@plan.updated_at.to_date, formats: :short)}, + font_size: 8, + spacing: (@formatting[:margin][:bottom] / 2) - 4, + right: '[page] of [topage]' + } end end - - # setup some variables we will need in the export views - # here, if custom sections are included, we want all sections, otherwise, - # we only want those which are not modifiable, as they are the original template - @sections = params[:export][:custom_sections].present? || @plan.template.customization_of.nil? ? @exported_plan.sections.order(:number) : Phase.find(params[:phase_id]).sections.where(modifiable: false) # prefetch questions? - @unanswered_questions = params[:export][:unanswered_questions].present? - @question_headings = params[:export][:question_headings].present? - @show_details = params[:export][:project_details].present? - - - begin - @exported_plan.save! - file_name = @exported_plan.settings(:export)[:value]['title'].gsub(/ /, "_") - - respond_to do |format| - format.html - format.csv { send_data @exported_plan.as_csv(@sections, @unanswered_question, @question_headings), filename: "#{file_name}.csv" } - format.text { send_data @exported_plan.as_txt(@sections, @unanswered_question, @question_headings, @show_details), filename: "#{file_name}.txt" } - format.docx { render docx: 'export', filename: "#{file_name}.docx" } - format.pdf do - @formatting = @plan.settings(:export).formatting - render pdf: file_name, - margin: @formatting[:margin], - footer: { - center: _('Created using the %{application_name}. Last modified %{date}') % {application_name: Rails.configuration.branding[:application][:name], date: l(@plan.updated_at.to_date, formats: :short)}, - font_size: 8, - spacing: (@formatting[:margin][:bottom] / 2) - 4, - right: '[page] of [topage]' - } - end - end - rescue ActiveRecord::RecordInvalid => e - @phase_options = @plan.phases.order(:number).pluck(:title,:id) - redirect_to download_plan_path(@plan), alert: _('%{format} is not a valid exporting format. Available formats to export are %{available_formats}.') % - {format: params[:format], available_formats: ExportedPlan::VALID_FORMATS.to_s} - end end diff --git a/app/controllers/public_pages_controller.rb b/app/controllers/public_pages_controller.rb index a6ef410..98ffad9 100644 --- a/app/controllers/public_pages_controller.rb +++ b/app/controllers/public_pages_controller.rb @@ -46,69 +46,35 @@ # GET plan_export/:id # ------------------------------------------------------------- def plan_export - @plan = Plan.find(params[:id]) + @plan = Plan.includes(:answers).joins(:answers).find(params[:id]) # covers authorization for this action. Pundit dosent support passing objects into scoped policies raise Pundit::NotAuthorizedError unless PublicPagePolicy.new(@plan, current_user).plan_organisationally_exportable? || PublicPagePolicy.new(@plan).plan_export? skip_authorization - # This creates exported_plans with no user. - # Note for reviewers, The ExportedPlan model actually serves no purpose, except - # to store preferences for PDF export. These preferences could be moved into - # the prefs table for individual users, and a more semsible structure implimented - # to track the exports & formats(html/pdf/ect) of users. - @exported_plan = ExportedPlan.new.tap do |ep| - ep.plan = @plan - ep.phase_id = @plan.phases.first.id - ep.format = :pdf - plan_settings = @plan.settings(:export) - Settings::Template::DEFAULT_SETTINGS.each do |key, value| - ep.settings(:export).send("#{key}=", plan_settings.send(key)) + @show_coversheet = true + @show_sections_questions = true + @show_unanswered = true + + @hash = @plan.as_pdf(@show_coversheet) + @formatting = @plan.settings(:export).formatting + file_name = @plan.title.gsub(/ /, "_") + + respond_to do |format| + format.html + format.csv { send_data @exported_plan.as_csv(@sections, @unanswered_question, @question_headings), filename: "#{file_name}.csv" } + format.text { send_data @exported_plan.as_txt(@sections, @unanswered_question, @question_headings, @show_details), filename: "#{file_name}.txt" } + format.docx { render docx: 'export', filename: "#{file_name}.docx" } + format.pdf do + render pdf: file_name, + margin: @formatting[:margin], + footer: { + center: _('Created using the %{application_name}. Last modified %{date}') % {application_name: Rails.configuration.branding[:application][:name], date: l(@plan.updated_at.to_date, formats: :short)}, + font_size: 8, + spacing: (@formatting[:margin][:bottom] / 2) - 4, + right: '[page] of [topage]' + } end end - # need to determine which phases to export - @a_q_ids = Answer.where(plan_id: @plan.id).pluck(:question_id).uniq - @a_s_ids = Question.where(id: @a_q_ids).pluck(:section_id).uniq - a_p_ids = Section.where(id: @a_s_ids).pluck(:phase_id).uniq - @phases = Phase.includes(sections: :questions).where(id: a_p_ids).order(:number) - # name of owner and any co-owners - @creator_text = @plan.owner.name(false) - @plan.roles.administrator.not_creator.each do |role| - @creator_text += ", " + role.user.name(false) - end - # Org name of plan owner - @affiliation = @plan.owner.org.name - # set the funder name - @funder = @plan.template.org.funder? ? @plan.template.org.name : nil - # set the template name and customizer name if applicable - @template = @plan.template.title - @customizer = "" - cust_questions = @plan.questions.where(modifiable: true).pluck(:id) - # if the template is customized, and has custom answered questions - if @plan.template.customization_of.present? && Answer.where(plan_id: @plan.id, question_id: cust_questions).present? - @customizer = _(" Customised By: ") + @plan.template.org.name - end - - - begin - @exported_plan.save! - file_name = @plan.title.gsub(/ /, "_") - - respond_to do |format| - format.pdf do - @formatting = @plan.settings(:export).formatting - render pdf: file_name, show_as_html: params.key?('debug'), - footer: { - center: _('Created using the %{application_name}. Last modified %{date}') % {application_name: Rails.configuration.branding[:application][:name], date: l(@plan.updated_at.to_date, formats: :short)}, - font_size: 8, - spacing: (@formatting[:margin][:bottom] / 2) - 12, - right: '[page] of [topage]', - } - end - end - rescue ActiveRecord::RecordInvalid => e - # send to the public_index page - redirect_to public_plans_path, alert: _('Unable to download the DMP at this time.') - end end # GET /plans_index diff --git a/app/models/concerns/exportable_plan.rb b/app/models/concerns/exportable_plan.rb new file mode 100644 index 0000000..d9a3a03 --- /dev/null +++ b/app/models/concerns/exportable_plan.rb @@ -0,0 +1,90 @@ +module ExportablePlan + extend ActiveSupport::Concern + + included do + + def as_pdf(coversheet = false) + prepare(coversheet) + end + + private + def prepare(coversheet = false) + hash = coversheet ? prepare_coversheet : {} + template = Template.includes(phases: { sections: {questions: :question_format } }). + joins(phases: { sections: { questions: :question_format } }). + where(id: self.template_id).first + + hash[:title] = self.title + hash[:answers] = self.answers + + # add the relevant questions/answers + phases = [] + template.phases.each do |phase| + phs = { title: phase.title, number: phase.number, sections: [] } + phase.sections.each do |section| + sctn = { title: section.title, number: section.number, questions: [] } + section.questions.each do |question| + txt = [] + if question.question_format.option_based? + opts = QuestionOption.where(question_id: question.id) + opts.each do |opt| + txt << opt.text + end + else + txt << question.text + end + sctn[:questions] << { id: question.id, text: txt } + end + phs[:sections] << sctn + end + phases << phs + end + hash[:phases] = phases + + record_plan_export(:pdf) + + hash + end + + def prepare_coversheet + hash = {} + # name of owner and any co-owners + attribution = self.owner.present? ? [self.owner.name(false)] : [] + self.roles.administrator.not_creator.each do |role| + attribution << role.user.name(false) + end + hash[:attribution] = attribution + + # Org name of plan owner's org + hash[:affiliation] = self.owner.present? ? self.owner.org.name : '' + + # set the funder name + hash[:funder] = self.funder_name.present? ? self.funder_name : (self.template.org.present? ? self.template.org.name : '') + + # set the template name and customizer name if applicable + hash[:template] = self.template.title + customizer = "" + cust_questions = self.questions.where(modifiable: true).pluck(:id) + # if the template is customized, and has custom answered questions + if self.template.customization_of.present? && Answer.where(plan_id: self.id, question_id: cust_questions).present? + customizer = _(" Customised By: ") + self.template.org.name + end + hash[:customizer] = customizer + hash + end + + def record_plan_export(format) + exported_plan = ExportedPlan.new.tap do |ep| + ep.plan = self + ep.phase_id = self.phases.first.id + ep.format = format + plan_settings = self.settings(:export) + + Settings::Template::DEFAULT_SETTINGS.each do |key, value| + ep.settings(:export).send("#{key}=", plan_settings.send(key)) + end + end + exported_plan.save + end + end +end \ No newline at end of file diff --git a/app/models/plan.rb b/app/models/plan.rb index 9aa7c51..8c8265b 100644 --- a/app/models/plan.rb +++ b/app/models/plan.rb @@ -1,5 +1,6 @@ class Plan < ActiveRecord::Base include ConditionalUserMailer + include ExportablePlan before_validation :set_creation_defaults ## diff --git a/app/views/plans/export.docx.erb b/app/views/plans/export.docx.erb deleted file mode 100644 index e75c5a4..0000000 --- a/app/views/plans/export.docx.erb +++ /dev/null @@ -1,47 +0,0 @@ -

<%= @plan.title %>

-

<%= @plan.template.title %>

- -<% details = @exported_plan.admin_details %> -<% if details.present? && @show_details %> -

<%= _('Admin Details') %>

- <% details.each do |field| %> - <% value = @exported_plan.send(field) %> -

- <%= admin_field_t(field.to_s) %>: - <%= value.present? ? value : _('-') %> -

- <% end %> -<% end %> - - - -<% @sections.each do |section| %> - <% if @question_headings %> -

<%= section.title %>

- <% end %> - <% section.questions.each do |question| %> - <% answer = @plan.answer(question.id, false) %> - <% if answer.nil? && !@unanswered_questions then next end %> - <% if @question_headings %> - <%= raw question.text %> - <% end %> - <% if answer.nil? %> -

<%= _('Question not answered') %>

- <% else %> - <% q_format = question.question_format %> - <% if q_format.option_based? %> - - <% if question.option_comment_display %> - <%= raw answer.text %> - <% end %> - <% else %> - <%= raw answer.text %> - <% end %> - <% end%> -

- <% end %> -<% end %> diff --git a/app/views/plans/export.erb b/app/views/plans/export.erb new file mode 100644 index 0000000..870f3b9 --- /dev/null +++ b/app/views/plans/export.erb @@ -0,0 +1,2 @@ + +<%= render partial: '/shared/export/plan', locals: local_assigns %> \ No newline at end of file diff --git a/app/views/plans/export.html.erb b/app/views/plans/export.html.erb deleted file mode 100644 index cd8b28d..0000000 --- a/app/views/plans/export.html.erb +++ /dev/null @@ -1,82 +0,0 @@ -
-

<%= @plan.title %>

-

<%= @plan.template.title %>

-
-
- <% details = @exported_plan.admin_details %> - <% if details.present? && @show_details %> -

<%= _('Admin Details') %>

- - - - - - - - - <% - details.each do |field| - value = @exported_plan.send(field) - %> - - - - - <% end %> - -
<%= _('Title')%><%= _('Description')%>

- <%= admin_field_t(field.to_s) -%>

<%= value.present? ? value : _('-') %>
- <% end %> - <% @sections.each do |section| %> - <% if @question_headings %> -

<%= section.title %>

- <% end %> - - - - <% if @question_headings %> - - - <% else %> - - <% end %> - - - - <% section.questions.order(:number).each do |question| %> - - <% answer = @plan.answer(question.id, false) %> - <% if !@unanswered_questions && answer.blank? - next # skip unanswered questions - end %> - <% if @question_headings %> - - <% end %> - - - <% end %> - -
<%= _('Questions')%><%= _('Answers')%>
-

- <%= raw question.text %>

-
- <% if answer.nil? %> -

<%= _('Question not answered') %>

- <% else %> - <% q_format = question.question_format %> - <% if q_format.option_based? %> -
    - <% answer.question_options.each do |option| %> -
  • <%= option.text %>
  • - <% end %> -
- <% if question.option_comment_display == true %> - <%= raw answer.text %> - <% end %> - <% else%> - <%= raw answer.text %> - <% end%> - <% end %> -
- <% end %> -
-
-
\ No newline at end of file diff --git a/app/views/plans/export.json.jbuilder b/app/views/plans/export.json.jbuilder deleted file mode 100644 index 405d931..0000000 --- a/app/views/plans/export.json.jbuilder +++ /dev/null @@ -1,49 +0,0 @@ -json.project_title @exported_plan.plan.project.title -json.phase_title @exported_plan.plan.version.phase.title - -details = @exported_plan.admin_details - -if details.present? - json.details do - details.each do |field| - value = @exported_plan.send(field) - json.set! field.to_sym, value if value.present? - end - end -end - -json.sections do - @exported_plan.sections.each do |section| - json.set! section.number do - if @question_headings - json.title section.title - end - json.questions do - @exported_plan.questions_for_section(section.id).each do |question| - json.set! question.number do - json.question_text question.text - - answer = @exported_plan.plan.answer(question.id, false) - q_format = question.question_format - - if answer.present? - if (q_format.title == "Check box" || q_format.title == "Multi select box" || - q_format.title == "Radio buttons" || q_format.title == "Dropdown") - json.selections do - answer.options.each do |o| - json.set! o.number, o.text - end - end - if question.option_comment_display == true - json.comment_text (answer.try(:text) || 'No comment') - end - else - json.answer_text (answer.try(:text) || 'Question not answered') - end - end - end - end - end - end - end -end diff --git a/app/views/plans/export.pdf.erb b/app/views/plans/export.pdf.erb deleted file mode 100644 index 16ff8b5..0000000 --- a/app/views/plans/export.pdf.erb +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - <%= @plan.title %> - - - - -
-
-

<%= @plan.title %>

-

<%= _("A Data Management Plan created using ") + Rails.configuration.branding[:application][:name] %>

-
-
-
-

<%= _("Creator(s): ") + @creator_text %>


-

<%= _("Affiliation: ") + @affiliation %>


- <% if @funder.present? %> -

<%= _("Template: ") + @funder %>


- <% else %> -

<%= _("Template: ") + @template + @customizer %>


- <% end %> - <% if @plan.grant_number.present? %> -

<%= _("Grant number: ") + @plan.grant_number %>


- <% end %> - <% if @plan.description.present? %> -

<%= _("Project abstract: ") %>

-
<%= raw(@plan.description) %>

- <% end %> -

<%= _("Last modified: ") + @plan.updated_at.to_date.to_s %>


- -

<%= _("Copyright information:") %>

-

<%= _(" The above plan creator(s) have agreed that others may use as much of the text of this plan as they would like in their own plans, and customise it as necessary. You do not need to credit the creator(s) as the source of the language used, but using any of the plan's text does not imply that the creator(s) endorse, or have any relationship to, your project or proposal") %>

-
-
- - <% @phases.each do |phase| %> -
- -

<%= (@phases.length > 1 ? "#{@plan.title} - #{phase.title}" : @plan.title) %>

-
- <% Section.where(phase_id: phase.id, id: @a_s_ids).order(:number).each do |section| %> -

<%= section.title %>

- <% Question.where(section_id: section, id: @a_q_ids).order(:number).each do |question| %> -
- <% answer = @plan.answer(question.id, false) %> - <% if answer.nil? %> -

<%= _('Question not answered.') -%>

- <% else %> - <% q_format = question.question_format %> - <% if q_format.option_based? %> - - - <% if question.option_comment_display == true %> - <% if !answer.text.nil? %> - <%= raw answer.text.gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") %> - <% end %> - <% end %> - <% else %> - - <% if !answer.text.nil? %> - <%= raw answer.text.gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") %> - <% end %> - <% end %> - <% end %> -
- <% end %> - <% end %> - <% end %> - - diff --git a/app/views/public_pages/plan_export.pdf.erb b/app/views/public_pages/plan_export.pdf.erb index 16ff8b5..870f3b9 100644 --- a/app/views/public_pages/plan_export.pdf.erb +++ b/app/views/public_pages/plan_export.pdf.erb @@ -1,139 +1,2 @@ - - - - - - <%= @plan.title %> - - - - -
-
-

<%= @plan.title %>

-

<%= _("A Data Management Plan created using ") + Rails.configuration.branding[:application][:name] %>

-
-
-
-

<%= _("Creator(s): ") + @creator_text %>


-

<%= _("Affiliation: ") + @affiliation %>


- <% if @funder.present? %> -

<%= _("Template: ") + @funder %>


- <% else %> -

<%= _("Template: ") + @template + @customizer %>


- <% end %> - <% if @plan.grant_number.present? %> -

<%= _("Grant number: ") + @plan.grant_number %>


- <% end %> - <% if @plan.description.present? %> -

<%= _("Project abstract: ") %>

-
<%= raw(@plan.description) %>

- <% end %> -

<%= _("Last modified: ") + @plan.updated_at.to_date.to_s %>


- -

<%= _("Copyright information:") %>

-

<%= _(" The above plan creator(s) have agreed that others may use as much of the text of this plan as they would like in their own plans, and customise it as necessary. You do not need to credit the creator(s) as the source of the language used, but using any of the plan's text does not imply that the creator(s) endorse, or have any relationship to, your project or proposal") %>

-
-
- - <% @phases.each do |phase| %> -
- -

<%= (@phases.length > 1 ? "#{@plan.title} - #{phase.title}" : @plan.title) %>

-
- <% Section.where(phase_id: phase.id, id: @a_s_ids).order(:number).each do |section| %> -

<%= section.title %>

- <% Question.where(section_id: section, id: @a_q_ids).order(:number).each do |question| %> -
- <% answer = @plan.answer(question.id, false) %> - <% if answer.nil? %> -

<%= _('Question not answered.') -%>

- <% else %> - <% q_format = question.question_format %> - <% if q_format.option_based? %> - - - <% if question.option_comment_display == true %> - <% if !answer.text.nil? %> - <%= raw answer.text.gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") %> - <% end %> - <% end %> - <% else %> - - <% if !answer.text.nil? %> - <%= raw answer.text.gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") %> - <% end %> - <% end %> - <% end %> -
- <% end %> - <% end %> - <% end %> - - + +<%= render partial: '/shared/export/plan', locals: local_assigns %> \ No newline at end of file diff --git a/app/views/shared/export/_plan.erb b/app/views/shared/export/_plan.erb new file mode 100644 index 0000000..283cee0 --- /dev/null +++ b/app/views/shared/export/_plan.erb @@ -0,0 +1,62 @@ + + + + + <%= @plan.title %> + <%= render partial: '/shared/export/plan_styling' %> + + + <% if @show_coversheet %> + <%= render partial: '/shared/export/plan_coversheet' %> + <% end %> + + <% @hash[:phases].each do |phase| %> +
+ +

+ <%= (@hash[:phases].length > 1 ? "#{@plan.title} - #{phase[:title]}" : @plan.title) %> +

+ +
+ <% phase[:sections].each do |section| %> + <% if @show_sections_questions %> +

<%= section[:title] %>

+ <% end %> + + <% section[:questions].each do |question| %> +
+ <% if @show_sections_questions %> + <%# text in this case is an array to accomodate for option_based %> + <% if question[:text].length > 1 %> + + <% else %> +

<%= raw question[:text][0].gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") %>

+ <% end %> +
+ <% end %> + + <% answer = @plan.answer(question[:id], false) %> + <% blank = answer.present? ? answer.text.gsub(/<\/?p>/, '').gsub(//, '').chomp.blank? : true %> + <% if blank && @show_unanswered %> +

<%= _('Question not answered.') -%>

+ <% else %> + <% if answer.question_options.length > 0 %> + + <% else %> +

<%= raw answer.text %>

+ <% end %> + <% end %> +
+ <% end %> + <% end %> + <% end %> + + \ No newline at end of file diff --git a/app/views/shared/export/_plan_coversheet.erb b/app/views/shared/export/_plan_coversheet.erb new file mode 100644 index 0000000..2111156 --- /dev/null +++ b/app/views/shared/export/_plan_coversheet.erb @@ -0,0 +1,34 @@ +
+
+

<%= @plan.title %>

+

+ <%= _("A Data Management Plan created using ") + Rails.configuration.branding[:application][:name] %> +

+
+
+
+

<%= @hash[:attribution].length > 1 ? _("Creators: ") : _('Creator:') %> <%= @hash[:attribution].join(', ') %>


+ +

<%= _("Affiliation: ") + @hash[:affiliation] %>


+ + <% if @hash[:funder].present? %> +

<%= _("Template: ") + @hash[:funder] %>


+ <% else %> +

<%= _("Template: ") + @hash[:template] + @hash[:customizer] %>


+ <% end %> + + <% if @plan.grant_number.present? %> +

<%= _("Grant number: ") + @plan.grant_number %>


+ <% end %> + + <% if @plan.description.present? %> +

<%= _("Project abstract: ") %>

+
<%= raw(@plan.description) %>

+ <% end %> + +

<%= _("Last modified: ") + l(@plan.updated_at.to_date, formats: :short) %>


+ +

<%= _("Copyright information:") %>

+

<%= _(" The above plan creator(s) have agreed that others may use as much of the text of this plan as they would like in their own plans, and customise it as necessary. You do not need to credit the creator(s) as the source of the language used, but using any of the plan's text does not imply that the creator(s) endorse, or have any relationship to, your project or proposal") %>

+
+
\ No newline at end of file diff --git a/app/views/shared/export/_plan_styling.erb b/app/views/shared/export/_plan_styling.erb new file mode 100644 index 0000000..c013651 --- /dev/null +++ b/app/views/shared/export/_plan_styling.erb @@ -0,0 +1,63 @@ + \ No newline at end of file diff --git a/test/functional/plans_controller_test.rb b/test/functional/plans_controller_test.rb index c5f137e..403340a 100644 --- a/test/functional/plans_controller_test.rb +++ b/test/functional/plans_controller_test.rb @@ -251,6 +251,8 @@ # GET /plans/:id/export (export_plan_path) # ---------------------------------------------------------- test "export the plan" do + Answer.create!(plan: @plan, question: @plan.template.phases.first.sections.first.questions.last, text: 'Test Answer') + # Should redirect user to the root path if they are not logged in! get export_plan_path(@plan), {'format': 'pdf'} assert_unauthorized_redirect_to_root_path