From f9e92cc90d2c8fb35240389781caa6817ae9f087 Mon Sep 17 00:00:00 2001 From: davidtrussler Date: Fri, 8 Nov 2024 09:36:24 +0000 Subject: [PATCH] Add update content type functionality to admin tab Allows the user to update the content type of a document - Add Design System UI - Update edition_edit_test - Update controller to add duplicate method - Update editions_controller test - Update editions_helper --- app/controllers/editions_controller.rb | 23 ++++- app/helpers/editions_helper.rb | 23 ++++- .../secondary_nav_tabs/_admin.html.erb | 36 +++++-- app/views/shared/_clone_buttons.html.erb | 2 +- config/routes.rb | 1 + test/functional/editions_controller_test.rb | 44 +++++++++ test/integration/edition_edit_test.rb | 93 +++++++++++++++++++ 7 files changed, 213 insertions(+), 9 deletions(-) diff --git a/app/controllers/editions_controller.rb b/app/controllers/editions_controller.rb index 9c79a8258..36597110a 100644 --- a/app/controllers/editions_controller.rb +++ b/app/controllers/editions_controller.rb @@ -30,7 +30,6 @@ def index def show @artefact = @resource.artefact - render action: "show" end @@ -38,6 +37,28 @@ def show alias_method :unpublish, :show alias_method :admin, :show + def duplicate + command = EditionDuplicator.new(@resource, current_user) + target_edition_class_name = "#{params[:to]}_edition".classify if params[:to] + + if !@resource.can_create_new_edition? + flash[:warning] = "Another person has created a newer edition" + redirect_to edition_path(resource) + elsif command.duplicate(target_edition_class_name, current_user) + new_edition = command.new_edition + UpdateWorker.perform_async(new_edition.id.to_s) + flash[:success] = "New edition created" + redirect_to edition_path(new_edition) + else + flash[:danger] = command.error_message + redirect_to edition_path(resource) + end + rescue StandardError => e + Rails.logger.error "Error #{e.class} #{e.message}" + @resource.errors.add(:show, "Due to a service problem, the edition couldn't be duplicated") + render "show" + end + def update @resource.assign_attributes(permitted_update_params) diff --git a/app/helpers/editions_helper.rb b/app/helpers/editions_helper.rb index cb15433f8..5bfd15844 100644 --- a/app/helpers/editions_helper.rb +++ b/app/helpers/editions_helper.rb @@ -19,11 +19,32 @@ def legacy_resource_form(resource, &form_definition) nested_form_for resource, as: :edition, url: edition_path(resource), html: html_options, &form_definition end - def format_conversion_select_options(edition) + def legacy_format_conversion_select_options(edition) possible_target_formats = Edition.convertible_formats - [edition.artefact.kind] possible_target_formats.map { |format_name| [format_name.humanize, format_name] } end + def conversion_items(edition) + radio_options_hints = { + "answer" => "One page guidance", + "completed_transaction" => "Done page for end of a service", + "guide" => "Multi-page guidance", + "help_page" => "Info about GOV.UK website, for example Privacy", + "place" => "Postcode look-up for places/services near you", + "simple_smart_answer" => "Simple questions and answers that route users to relevant outcomes", + "transaction" => "Start page for a service", + } + + possible_target_formats = Edition.convertible_formats - [edition.artefact.kind] + possible_target_formats.map do |format_name| + { + text: format_name.humanize, + value: format_name, + hint_text: radio_options_hints[format_name], + } + end + end + def legacy_format_filter_selection_options [%w[All edition]] + Artefact::FORMATS_BY_DEFAULT_OWNING_APP["publisher"].map do |format_name| diff --git a/app/views/editions/secondary_nav_tabs/_admin.html.erb b/app/views/editions/secondary_nav_tabs/_admin.html.erb index c0755ffc4..07e54db54 100644 --- a/app/views/editions/secondary_nav_tabs/_admin.html.erb +++ b/app/views/editions/secondary_nav_tabs/_admin.html.erb @@ -1,12 +1,36 @@ <% @edition = @resource %> +
<%= header_for("Admin") %> - <%= render "govuk_publishing_components/components/list", { - items: [ - ( primary_button_for(@edition, skip_fact_check_edition_path(@edition), "Skip fact check") if @edition.fact_check? ), - ( link_to("Delete edition #{@edition.version_number}", confirm_destroy_edition_path(@resource), class: "govuk-link gem-link--destructive") if @edition.can_destroy? ), - ], - } %> + + <% if @edition.can_create_new_edition? && @edition.published? %> +

No content will be lost, but content in some fields might not make it into the new edition. If it can't be copied to a new content type it will still be available in the previous edition. Content in multiple fields might be combined into a single field.

+ + <% if @edition.respond_to?(:parts) %> +

All parts of Guide Editions will be copied across. If the format you are converting to doesn't have parts, the content of all the parts will be copied into the body, with the part title displayed as a top-level sub-heading.

+ <% end %> + + <%= form_for @resource, url: duplicate_edition_path(@resource), method: "post" do %> + <%= render "govuk_publishing_components/components/radio", { + heading: "Update content type", + heading_level: 0, + heading_size: "s", + name: "to", + items: conversion_items(@resource), + } %> + + <%= render "govuk_publishing_components/components/button", { + text: "Update", + } %> + <% end %> + <% else %> + <%= render "govuk_publishing_components/components/list", { + items: [ + ( primary_button_for(@edition, skip_fact_check_edition_path(@edition), "Skip fact check") if @edition.fact_check? ), + ( link_to("Delete edition #{@edition.version_number}", confirm_destroy_edition_path(@resource), class: "govuk-link gem-link--destructive") if @edition.can_destroy? ), + ], + } %> + <% end %>
diff --git a/app/views/shared/_clone_buttons.html.erb b/app/views/shared/_clone_buttons.html.erb index 807a613c8..cc9e7c005 100644 --- a/app/views/shared/_clone_buttons.html.erb +++ b/app/views/shared/_clone_buttons.html.erb @@ -8,7 +8,7 @@ <%= form_for @resource, url: duplicate_edition_path(@resource, from: edition_class.to_s), method: "post" do |f| %> <%= f.label :to, "Create as new", for: :to %> - <%= select_tag :to, options_for_select(format_conversion_select_options(@resource)), class: "form-control input-md-3 add-bottom-margin" %> + <%= select_tag :to, options_for_select(legacy_format_conversion_select_options(@resource)), class: "form-control input-md-3 add-bottom-margin" %> <%= f.submit "Change format", class: "btn btn-default" %> <% end %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index ca97f41f3..d667e58d2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -22,6 +22,7 @@ get "metadata" get "history" get "admin" + post "duplicate" get "related_external_links", to: "editions#linking" get "tagging", to: "editions#linking" get "unpublish" diff --git a/test/functional/editions_controller_test.rb b/test/functional/editions_controller_test.rb index 77461f92a..a447ef2b5 100644 --- a/test/functional/editions_controller_test.rb +++ b/test/functional/editions_controller_test.rb @@ -264,6 +264,50 @@ class EditionsControllerTest < ActionController::TestCase end end + context "#duplicate" do + setup do + @answer = FactoryBot.create(:answer_edition) + @help = FactoryBot.create(:help_page_edition) + end + + should "redirect to the edit tab of the newly created edition and show success message when user saves successfully" do + EditionDuplicator.any_instance.expects(:duplicate).returns(true) + EditionDuplicator.any_instance.expects(:new_edition).returns(@help) + + post :duplicate, params: { id: @edition.id, to: "help_page" } + + assert_redirected_to edition_path(@help) + assert_equal "New edition created", flash[:success] + end + + should "redirect to the edit tab and show a failure message when saving the new edition fails" do + EditionDuplicator.any_instance.expects(:duplicate).returns(false) + + post :duplicate, params: { id: @edition.id, to: "help_page" } + + assert_response :found + assert_equal "Failed to create new edition: couldn't initialise", flash[:danger] + end + + should "redirect to the edit tab and show a failure message when another user has already created a new edition" do + FactoryBot.create(:edition, panopticon_id: @edition.artefact.id) + + post :duplicate, params: { id: @edition.id, to: "help_page" } + + assert_response :found + assert_equal "Another person has created a newer edition", flash[:warning] + end + + should "render the edit tab and show a failure message when there is a service problem" do + EditionDuplicator.any_instance.expects(:duplicate).raises(StandardError) + + post :duplicate, params: { id: @edition.id, to: "help_page" } + + assert_template "show" + assert_select ".gem-c-error-summary__list-item", "Due to a service problem, the edition couldn't be duplicated" + end + end + context "#progress" do context "Welsh editor" do setup do diff --git a/test/integration/edition_edit_test.rb b/test/integration/edition_edit_test.rb index b0f0b433d..df6bf8602 100644 --- a/test/integration/edition_edit_test.rb +++ b/test/integration/edition_edit_test.rb @@ -6,6 +6,7 @@ class EditionEditTest < IntegrationTest login_as(@govuk_editor) test_strategy = Flipflop::FeatureSet.current.test! test_strategy.switch!(:design_system_edit, true) + stub_holidays_used_by_fact_check stub_linkables end @@ -165,6 +166,19 @@ class EditionEditTest < IntegrationTest end context "user has required permissions" do + %i[draft amends_needed in_review fact_check_received ready archived scheduled_for_publishing].each do |state| + context "when state is '#{state}'" do + setup do + send "visit_#{state}_edition" + click_link("Admin") + end + + should "not show the 'Update content type' form" do + assert page.has_no_text?("Update content type") + end + end + end + %i[published archived scheduled_for_publishing].each do |state| context "when state is '#{state}'" do setup do @@ -249,6 +263,56 @@ class EditionEditTest < IntegrationTest end end + context "when state is 'published'" do + context "content type is retired" do + setup do + visit_retired_edition_in_published + click_link("Admin") + end + + should "not show the 'Update content type' form" do + assert page.has_no_text?("Update content type") + end + end + + context "edition is not the latest version of a publication" do + setup do + visit_old_edition_of_published_edition + click_link("Admin") + end + + should "not show the 'Update content type' form" do + assert page.has_no_text?("Update content type") + end + end + + context "content type is not retired, edition is the latest version of a publication" do + setup do + visit_published_edition + click_link("Admin") + end + + should "show the 'Update content type' form" do + assert page.has_text?("Update content type") + end + + should "show radio buttons for all content types excluding current one (answer)" do + assert page.has_no_selector?(".gem-c-radio input[value='answer']") + assert page.has_selector?(".gem-c-radio input[value='completed_transaction']") + assert page.has_selector?(".gem-c-radio input[value='guide']") + assert page.has_selector?(".gem-c-radio input[value='help_page']") + assert page.has_selector?(".gem-c-radio input[value='place']") + assert page.has_selector?(".gem-c-radio input[value='simple_smart_answer']") + assert page.has_selector?(".gem-c-radio input[value='transaction']") + end + + should "show common explanatory text for all content types and not show explanatory text specific to Guides" do + assert page.has_text?("No content will be lost, but content in some fields might not make it into the new edition. If it can't be copied to a new content type it will still be available in the previous edition. Content in multiple fields might be combined into a single field.") + assert page.has_no_text?("All parts of Guide Editions will be copied across. If the format you are converting to doesn't have parts, the content of all the parts will be copied into the body, with the part title displayed as a top-level sub-heading.") + end + end + end + context "confirm delete" do setup do visit_draft_edition @@ -537,5 +601,34 @@ def create_published_edition state: "published", slug: "can-i-get-a-driving-licence", ) + visit edition_path(@published_edition) + end + + def visit_retired_edition_in_published + @published_edition = FactoryBot.create( + :campaign_edition, + state: "published", + ) + visit edition_path(@published_edition) + end + + def visit_old_edition_of_published_edition + published_edition = FactoryBot.create( + :edition, + panopticon_id: FactoryBot.create( + :artefact, + slug: "can-i-get-a-driving-licence", + ).id, + state: "published", + sibling_in_progress: 2, + ) + FactoryBot.create( + :edition, + panopticon_id: published_edition.artefact.id, + state: "draft", + version_number: 2, + change_note: "The change note", + ) + visit edition_path(published_edition) end end