diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index ff04bfa48..0a6375e47 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -30,7 +30,9 @@ $govuk-page-width: 1140px; @import 'govuk_publishing_components/components/table'; @import 'govuk_publishing_components/components/textarea'; @import 'govuk_publishing_components/components/title'; +@import "tags"; @import "downtimes"; @import "publications"; @import "summary-card"; @import "popular_links"; +@import "editions"; diff --git a/app/assets/stylesheets/editions.scss b/app/assets/stylesheets/editions.scss new file mode 100644 index 000000000..de95ca73f --- /dev/null +++ b/app/assets/stylesheets/editions.scss @@ -0,0 +1,5 @@ +.editions__edit { + .govuk-tag { + display: inline; + } +} diff --git a/app/assets/stylesheets/publications.scss b/app/assets/stylesheets/publications.scss index 8a069114d..329fc76d6 100644 --- a/app/assets/stylesheets/publications.scss +++ b/app/assets/stylesheets/publications.scss @@ -32,44 +32,6 @@ $width: 40; font-size: 1.1875rem; } - .govuk-tag { - &--amends_needed { - @extend .govuk-tag--red; // stylelint-disable-line scss/at-extend-no-missing-placeholder - } - - &--archived { - @extend .govuk-tag--blue; // stylelint-disable-line scss/at-extend-no-missing-placeholder - } - - &--draft { - @extend .govuk-tag--yellow; // stylelint-disable-line scss/at-extend-no-missing-placeholder - } - - &--fact_check { - @extend .govuk-tag--purple; // stylelint-disable-line scss/at-extend-no-missing-placeholder - } - - &--fact_check_received { - @extend .govuk-tag--pink; // stylelint-disable-line scss/at-extend-no-missing-placeholder - } - - &--ready { - @extend .govuk-tag--green; // stylelint-disable-line scss/at-extend-no-missing-placeholder - } - - &--scheduled_for_publishing { - @extend .govuk-tag--turquoise; // stylelint-disable-line scss/at-extend-no-missing-placeholder - } - - &--published { - @extend .govuk-tag--orange; // stylelint-disable-line scss/at-extend-no-missing-placeholder - } - - &--in_review { - @extend .govuk-tag--grey; // stylelint-disable-line scss/at-extend-no-missing-placeholder - } - } - .govuk-table { border-top: 2px solid $govuk-text-colour; } diff --git a/app/assets/stylesheets/tags.scss b/app/assets/stylesheets/tags.scss new file mode 100644 index 000000000..d9168c65e --- /dev/null +++ b/app/assets/stylesheets/tags.scss @@ -0,0 +1,37 @@ +.govuk-tag { + &--amends_needed { + @extend .govuk-tag--red; // stylelint-disable-line scss/at-extend-no-missing-placeholder + } + + &--archived { + @extend .govuk-tag--blue; // stylelint-disable-line scss/at-extend-no-missing-placeholder + } + + &--draft { + @extend .govuk-tag--yellow; // stylelint-disable-line scss/at-extend-no-missing-placeholder + } + + &--fact_check { + @extend .govuk-tag--purple; // stylelint-disable-line scss/at-extend-no-missing-placeholder + } + + &--fact_check_received { + @extend .govuk-tag--pink; // stylelint-disable-line scss/at-extend-no-missing-placeholder + } + + &--ready { + @extend .govuk-tag--green; // stylelint-disable-line scss/at-extend-no-missing-placeholder + } + + &--scheduled_for_publishing { + @extend .govuk-tag--turquoise; // stylelint-disable-line scss/at-extend-no-missing-placeholder + } + + &--published { + @extend .govuk-tag--orange; // stylelint-disable-line scss/at-extend-no-missing-placeholder + } + + &--in_review { + @extend .govuk-tag--grey; // stylelint-disable-line scss/at-extend-no-missing-placeholder + } +} diff --git a/app/controllers/editions_controller.rb b/app/controllers/editions_controller.rb index 84bc77b88..9dca5cede 100644 --- a/app/controllers/editions_controller.rb +++ b/app/controllers/editions_controller.rb @@ -2,429 +2,30 @@ require "edition_progressor" class EditionsController < InheritedResources::Base - actions :create, :update, :destroy + layout "design_system" + defaults resource_class: Edition, collection_name: "editions", instance_name: "resource" - before_action :setup_view_paths, except: %i[index new create] - before_action only: %i[update duplicate progress review destroy admin] do - require_editor_permissions - end - before_action only: %i[unpublish process_unpublish] do - require_govuk_editor(redirect_path: edition_path(resource)) - end - after_action :report_state_counts, only: %i[create duplicate progress destroy] + before_action :setup_view_paths, except: %i[index] def index redirect_to root_path end def show - @linkables = Tagging::Linkables.new - - if @resource.is_a?(Parted) - @ordered_parts = @resource.parts.in_order - end - - if @resource.is_a?(Varianted) - @ordered_variants = @resource.variants.in_order - end - - @tagging_update = tagging_update_form - @artefact = @resource.artefact - render action: "show" - end - - alias_method :metadata, :show - alias_method :history, :show - alias_method :admin, :show - alias_method :unpublish, :show - - def new - @publication = build_resource - setup_view_paths_for(@publication) - end - - def create - class_identifier = params[:edition].delete(:kind).to_sym - create_params = permitted_params(subtype: :"#{class_identifier}_edition") - @publication = current_user.create_edition(class_identifier, create_params[:edition]) - - if @publication.persisted? - UpdateWorker.perform_async(@publication.id.to_s) - - flash[:success] = "#{description(@publication)} successfully created" - redirect_to edition_path(@publication) - else - setup_view_paths_for(@publication) - render template: "new" - end - end - - 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) - - return_to = params[:return_to] || edition_path(new_edition) - flash[:success] = "New edition created" - redirect_to return_to - else - flash[:danger] = command.error_message - redirect_to edition_path(resource) - end - end - - def update - # We have to call this before updating as it removes any assigned_to_id - # parameter from the request, preventing us from inadvertently changing - # it at the wrong time. - assign_to = new_assignee - - activity_params = attempted_activity_params - remove_activity_params - - # update! is from the Inherited Resources gem - # https://github.com/josevalim/inherited_resources/blob/master/lib/inherited_resources/actions.rb#L42 - update! do |success, failure| - success.html do - if attempted_activity - if progress_edition(resource, activity_params) - flash[:success] = @command.status_message - else - flash[:danger] = @command.status_message - end - end - - update_assignment resource, assign_to - - UpdateWorker.perform_async(resource.id.to_s, update_action_is_publish?) - - return_to = params[:return_to] || edition_path(resource) - redirect_to return_to - end - failure.html do - @resource = resource - @tagging_update = tagging_update_form - @linkables = Tagging::Linkables.new - @artefact = @resource.artefact - render action: "show" - end - success.json do - progress_edition(resource, activity_params) if attempted_activity - - update_assignment resource, assign_to - - UpdateWorker.perform_async(resource.id.to_s, update_action_is_publish?) - - render json: resource - end - failure.json { render json: resource.errors, status: :not_acceptable } - end - end - - def linking - @linkables = Tagging::Linkables.new - @tagging_update = tagging_update_form @artefact = @resource.artefact render action: "show" end - def update_tagging - form = Tagging::TaggingUpdateForm.new(tagging_update_form_params) - if form.valid? - form.publish! - flash[:success] = "Tags have been updated!" - else - flash[:danger] = form.errors.full_messages.join("\n") - end - redirect_to tagging_edition_path - rescue GdsApi::HTTPConflict - redirect_to tagging_edition_path, - flash: { - danger: "Somebody changed the tags before you could. Your changes have not been saved.", - } - end - - def update_related_external_links - artefact = resource.artefact - if params.key?("artefact") - external_links = params.require(:artefact).permit(external_links_attributes: %i[title url id _destroy]) - artefact.external_links_attributes = external_links[:external_links_attributes].to_h - - if artefact.save - flash[:success] = "External links have been saved. They will be visible the next time this publication is published." - else - flash[:danger] = artefact.errors.full_messages.join("\n") - end - else - flash[:danger] = "There aren't any external related links yet" - end - - redirect_back(fallback_location: related_external_links_edition_path(resource.id)) - end - - def review - if resource.reviewer.present? - flash[:danger] = "#{resource.reviewer} has already claimed this 2i" - redirect_to edition_path(resource) - return - end - - resource.reviewer = params[:edition][:reviewer] - if resource.save - flash[:success] = "You are the reviewer of this #{description(resource).downcase}." - else - flash[:danger] = "Something went wrong when attempting to claim 2i." - end - redirect_to edition_path(resource) - end - - def destroy - if resource.can_destroy? - destroy! do - flash[:success] = "Edition deleted" - redirect_to root_url - return - end - else - flash[:danger] = "Cannot delete a #{description(resource).downcase} that has ever been published." - redirect_to edition_path(resource) - nil - end - end - - def progress - if progress_edition(resource, params[:edition][:activity].permit(:comment, :request_type, :publish_at)) - PublishWorker.perform_async(resource.id.to_s) if progress_action_is_publish? - - flash[:success] = @command.status_message - else - flash[:danger] = @command.status_message - end - redirect_to edition_path(resource) - end - - def diff - @resource = resource - @comparison = @resource.previous_siblings.last - end - - def process_unpublish - edition = Edition.find(params[:id]) - artefact = edition.artefact - - if validate_redirect(redirect_url) || redirect_url.blank? - success = UnpublishService.call(artefact, current_user, redirect_url) - else - flash[:danger] = "Redirect path is invalid. #{description(resource)} has not been unpublished." - end - - if success - notice = "Content unpublished" - notice << " and redirected" if redirect_url.present? - flash[:notice] = notice - redirect_to root_path - else - flash[:alert] = "Due to a service problem, the edition couldn't be unpublished" - redirect_to unpublish_edition_path(edition) - end - end - - def diagram - # [MT] TODO: What's the best way to handle requests for a diagram for a non-simple smart answer? - if @resource.format != "SimpleSmartAnswer" - render plain: "404 Not Found", status: :not_found - end - end - protected - def permitted_params(subtype: nil) - subtype = @resource.class.to_s.underscore.to_sym if subtype.nil? - params.permit(edition: type_specific_params(subtype) + common_params) - end - - def type_specific_params(subtype) - case subtype - when :guide_edition - [ - :hide_chapter_navigation, - { parts_attributes: %i[title body slug order id _destroy] }, - ] - when :local_transaction_edition - [ - :lgsl_code, - :lgil_code, - :introduction, - :more_information, - :need_to_know, - { scotland_availability_attributes: %i[type alternative_url] }, - { wales_availability_attributes: %i[type alternative_url] }, - { northern_ireland_availability_attributes: %i[type alternative_url] }, - ] - when :place_edition - %i[ - place_type - introduction - more_information - need_to_know - ] - when :simple_smart_answer_edition - [ - :body, - :start_button_text, - { nodes_attributes: [ - :slug, - :title, - :body, - :order, - :kind, - :id, - :_destroy, - { options_attributes: %i[label next_node id _destroy] }, - ] }, - ] - when :transaction_edition - [ - :introduction, - :start_button_text, - :will_continue_on, - :link, - :more_information, - :alternate_methods, - :need_to_know, - { variants_attributes: %i[title slug introduction link more_information alternate_methods order id _destroy] }, - ] - when :completed_transaction_edition - %i[ - body - promotion_choice - promotion_choice_url - promotion_choice_opt_in_url - promotion_choice_opt_out_url - ] - else - # answer_edition, help_page_edition - [ - :body, - ] - end - end - - def common_params - %i[ - assigned_to_id - reviewer - panopticon_id - slug - change_note - major_change - title - in_beta - overview - ] - end - - def new_assignee - assignee_id = (params[:edition] || {}).delete(:assigned_to_id) - User.find(assignee_id) if assignee_id.present? - end - - def update_assignment(edition, assignee) - return if edition.assigned_to == assignee - - if !assignee - current_user.unassign(edition) - elsif assignee.has_editor_permissions?(resource) - current_user.assign(edition, assignee) - else - flash[:danger] = "Chosen assignee does not have correct editor permissions." - end - end - def setup_view_paths setup_view_paths_for(resource) end - def description(resource) - resource.format.underscore.humanize - end - private - def redirect_url - make_govuk_url_relative params["redirect_url"] - end - - def make_govuk_url_relative(url = "") - url.sub(%r{^(https?://)?(www\.)?gov\.uk/}, "/") - end - - def validate_redirect(redirect_url) - regex = /(\/([a-z0-9]+-)*[a-z0-9]+)+/ - redirect_url =~ regex - end - - def tagging_update_form - Tagging::TaggingUpdateForm.build_from_publishing_api( - @resource.artefact.content_id, - @resource.artefact.language, - ) - end - - def attempted_activity_params - return unless attempted_activity - - params[:edition]["activity_#{attempted_activity}_attributes"].permit( - :request_type, :email_addresses, :customised_message, :comment, :publish_at - ) - end - - def remove_activity_params - params.fetch(:edition, {}).delete_if { |attributes, _| attributes =~ /\Aactivity_\w*_attributes\z/ } - end - - def tagging_update_form_params - params[:tagging_tagging_update_form].permit( - :content_id, - :previous_version, - :parent, - mainstream_browse_pages: [], - organisations: [], - meets_user_needs: [], - ordered_related_items: [], - ).to_h - end - - def progress_edition(resource, activity_params) - @command = EditionProgressor.new(resource, current_user) - @command.progress(squash_multiparameter_datetime_attributes(activity_params.to_h, %w[publish_at])) - end - - def report_state_counts - Publisher::Application.edition_state_count_reporter.report - end - - def update_action_is_publish? - attempted_activity == :publish - end - - def progress_action_is_publish? - progress_action_param == "publish" - end - - def progress_action_param - params[:edition][:activity][:request_type] - rescue StandardError - nil - end - - def attempted_activity - Edition::ACTIONS.invert[params[:commit]] + def setup_view_paths_for(publication) + prepend_view_path "app/views/editions" + prepend_view_path template_folder_for(publication) end end diff --git a/app/controllers/legacy_editions_controller.rb b/app/controllers/legacy_editions_controller.rb new file mode 100644 index 000000000..e5290ccbf --- /dev/null +++ b/app/controllers/legacy_editions_controller.rb @@ -0,0 +1,430 @@ +require "edition_duplicator" +require "edition_progressor" + +class LegacyEditionsController < InheritedResources::Base + actions :create, :update, :destroy + defaults resource_class: Edition, collection_name: "editions", instance_name: "resource" + before_action :setup_view_paths, except: %i[index new create] + before_action only: %i[update duplicate progress review destroy admin] do + require_editor_permissions + end + before_action only: %i[unpublish process_unpublish] do + require_govuk_editor(redirect_path: edition_path(resource)) + end + after_action :report_state_counts, only: %i[create duplicate progress destroy] + + def index + redirect_to root_path + end + + def show + @linkables = Tagging::Linkables.new + + if @resource.is_a?(Parted) + @ordered_parts = @resource.parts.in_order + end + + if @resource.is_a?(Varianted) + @ordered_variants = @resource.variants.in_order + end + + @tagging_update = tagging_update_form + @artefact = @resource.artefact + render action: "show" + end + + alias_method :metadata, :show + alias_method :history, :show + alias_method :admin, :show + alias_method :unpublish, :show + + def new + @publication = build_resource + setup_view_paths_for(@publication) + end + + def create + class_identifier = params[:edition].delete(:kind).to_sym + create_params = permitted_params(subtype: :"#{class_identifier}_edition") + @publication = current_user.create_edition(class_identifier, create_params[:edition]) + + if @publication.persisted? + UpdateWorker.perform_async(@publication.id.to_s) + + flash[:success] = "#{description(@publication)} successfully created" + redirect_to edition_path(@publication) + else + setup_view_paths_for(@publication) + render template: "new" + end + end + + 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) + + return_to = params[:return_to] || edition_path(new_edition) + flash[:success] = "New edition created" + redirect_to return_to + else + flash[:danger] = command.error_message + redirect_to edition_path(resource) + end + end + + def update + # We have to call this before updating as it removes any assigned_to_id + # parameter from the request, preventing us from inadvertently changing + # it at the wrong time. + assign_to = new_assignee + + activity_params = attempted_activity_params + remove_activity_params + + # update! is from the Inherited Resources gem + # https://github.com/josevalim/inherited_resources/blob/master/lib/inherited_resources/actions.rb#L42 + update! do |success, failure| + success.html do + if attempted_activity + if progress_edition(resource, activity_params) + flash[:success] = @command.status_message + else + flash[:danger] = @command.status_message + end + end + + update_assignment resource, assign_to + + UpdateWorker.perform_async(resource.id.to_s, update_action_is_publish?) + + return_to = params[:return_to] || edition_path(resource) + redirect_to return_to + end + failure.html do + @resource = resource + @tagging_update = tagging_update_form + @linkables = Tagging::Linkables.new + @artefact = @resource.artefact + render action: "show" + end + success.json do + progress_edition(resource, activity_params) if attempted_activity + + update_assignment resource, assign_to + + UpdateWorker.perform_async(resource.id.to_s, update_action_is_publish?) + + render json: resource + end + failure.json { render json: resource.errors, status: :not_acceptable } + end + end + + def linking + @linkables = Tagging::Linkables.new + @tagging_update = tagging_update_form + @artefact = @resource.artefact + render action: "show" + end + + def update_tagging + form = Tagging::TaggingUpdateForm.new(tagging_update_form_params) + if form.valid? + form.publish! + flash[:success] = "Tags have been updated!" + else + flash[:danger] = form.errors.full_messages.join("\n") + end + redirect_to tagging_edition_path + rescue GdsApi::HTTPConflict + redirect_to tagging_edition_path, + flash: { + danger: "Somebody changed the tags before you could. Your changes have not been saved.", + } + end + + def update_related_external_links + artefact = resource.artefact + if params.key?("artefact") + external_links = params.require(:artefact).permit(external_links_attributes: %i[title url id _destroy]) + artefact.external_links_attributes = external_links[:external_links_attributes].to_h + + if artefact.save + flash[:success] = "External links have been saved. They will be visible the next time this publication is published." + else + flash[:danger] = artefact.errors.full_messages.join("\n") + end + else + flash[:danger] = "There aren't any external related links yet" + end + + redirect_back(fallback_location: related_external_links_edition_path(resource.id)) + end + + def review + if resource.reviewer.present? + flash[:danger] = "#{resource.reviewer} has already claimed this 2i" + redirect_to edition_path(resource) + return + end + + resource.reviewer = params[:edition][:reviewer] + if resource.save + flash[:success] = "You are the reviewer of this #{description(resource).downcase}." + else + flash[:danger] = "Something went wrong when attempting to claim 2i." + end + redirect_to edition_path(resource) + end + + def destroy + if resource.can_destroy? + destroy! do + flash[:success] = "Edition deleted" + redirect_to root_url + return + end + else + flash[:danger] = "Cannot delete a #{description(resource).downcase} that has ever been published." + redirect_to edition_path(resource) + nil + end + end + + def progress + if progress_edition(resource, params[:edition][:activity].permit(:comment, :request_type, :publish_at)) + PublishWorker.perform_async(resource.id.to_s) if progress_action_is_publish? + + flash[:success] = @command.status_message + else + flash[:danger] = @command.status_message + end + redirect_to edition_path(resource) + end + + def diff + @resource = resource + @comparison = @resource.previous_siblings.last + end + + def process_unpublish + edition = Edition.find(params[:id]) + artefact = edition.artefact + + if validate_redirect(redirect_url) || redirect_url.blank? + success = UnpublishService.call(artefact, current_user, redirect_url) + else + flash[:danger] = "Redirect path is invalid. #{description(resource)} has not been unpublished." + end + + if success + notice = "Content unpublished" + notice << " and redirected" if redirect_url.present? + flash[:notice] = notice + redirect_to root_path + else + flash[:alert] = "Due to a service problem, the edition couldn't be unpublished" + redirect_to unpublish_edition_path(edition) + end + end + + def diagram + # [MT] TODO: What's the best way to handle requests for a diagram for a non-simple smart answer? + if @resource.format != "SimpleSmartAnswer" + render plain: "404 Not Found", status: :not_found + end + end + +protected + + def permitted_params(subtype: nil) + subtype = @resource.class.to_s.underscore.to_sym if subtype.nil? + params.permit(edition: type_specific_params(subtype) + common_params) + end + + def type_specific_params(subtype) + case subtype + when :guide_edition + [ + :hide_chapter_navigation, + { parts_attributes: %i[title body slug order id _destroy] }, + ] + when :local_transaction_edition + [ + :lgsl_code, + :lgil_code, + :introduction, + :more_information, + :need_to_know, + { scotland_availability_attributes: %i[type alternative_url] }, + { wales_availability_attributes: %i[type alternative_url] }, + { northern_ireland_availability_attributes: %i[type alternative_url] }, + ] + when :place_edition + %i[ + place_type + introduction + more_information + need_to_know + ] + when :simple_smart_answer_edition + [ + :body, + :start_button_text, + { nodes_attributes: [ + :slug, + :title, + :body, + :order, + :kind, + :id, + :_destroy, + { options_attributes: %i[label next_node id _destroy] }, + ] }, + ] + when :transaction_edition + [ + :introduction, + :start_button_text, + :will_continue_on, + :link, + :more_information, + :alternate_methods, + :need_to_know, + { variants_attributes: %i[title slug introduction link more_information alternate_methods order id _destroy] }, + ] + when :completed_transaction_edition + %i[ + body + promotion_choice + promotion_choice_url + promotion_choice_opt_in_url + promotion_choice_opt_out_url + ] + else + # answer_edition, help_page_edition + [ + :body, + ] + end + end + + def common_params + %i[ + assigned_to_id + reviewer + panopticon_id + slug + change_note + major_change + title + in_beta + overview + ] + end + + def new_assignee + assignee_id = (params[:edition] || {}).delete(:assigned_to_id) + User.find(assignee_id) if assignee_id.present? + end + + def update_assignment(edition, assignee) + return if edition.assigned_to == assignee + + if !assignee + current_user.unassign(edition) + elsif assignee.has_editor_permissions?(resource) + current_user.assign(edition, assignee) + else + flash[:danger] = "Chosen assignee does not have correct editor permissions." + end + end + + def setup_view_paths + setup_view_paths_for(resource) + end + + def description(resource) + resource.format.underscore.humanize + end + +private + + def redirect_url + make_govuk_url_relative params["redirect_url"] + end + + def make_govuk_url_relative(url = "") + url.sub(%r{^(https?://)?(www\.)?gov\.uk/}, "/") + end + + def validate_redirect(redirect_url) + regex = /(\/([a-z0-9]+-)*[a-z0-9]+)+/ + redirect_url =~ regex + end + + def tagging_update_form + Tagging::TaggingUpdateForm.build_from_publishing_api( + @resource.artefact.content_id, + @resource.artefact.language, + ) + end + + def attempted_activity_params + return unless attempted_activity + + params[:edition]["activity_#{attempted_activity}_attributes"].permit( + :request_type, :email_addresses, :customised_message, :comment, :publish_at + ) + end + + def remove_activity_params + params.fetch(:edition, {}).delete_if { |attributes, _| attributes =~ /\Aactivity_\w*_attributes\z/ } + end + + def tagging_update_form_params + params[:tagging_tagging_update_form].permit( + :content_id, + :previous_version, + :parent, + mainstream_browse_pages: [], + organisations: [], + meets_user_needs: [], + ordered_related_items: [], + ).to_h + end + + def progress_edition(resource, activity_params) + @command = EditionProgressor.new(resource, current_user) + @command.progress(squash_multiparameter_datetime_attributes(activity_params.to_h, %w[publish_at])) + end + + def report_state_counts + Publisher::Application.edition_state_count_reporter.report + end + + def update_action_is_publish? + attempted_activity == :publish + end + + def progress_action_is_publish? + progress_action_param == "publish" + end + + def progress_action_param + params[:edition][:activity][:request_type] + rescue StandardError + nil + end + + def attempted_activity + Edition::ACTIONS.invert[params[:commit]] + end +end diff --git a/app/views/editions/show.html.erb b/app/views/editions/show.html.erb index 343392388..e395fcdd9 100644 --- a/app/views/editions/show.html.erb +++ b/app/views/editions/show.html.erb @@ -1,69 +1,25 @@ <% @edition = @resource %> - <%= render "shared/edition_header" %> - - <% errors_hash = errors_to_display(@edition) %> - <% if !errors_hash.empty? %> -
-

There is a problem

- -
- <% end %> - -
- - -
-
- - - <%= resource_form(@resource) do |f| %> -
- <%= render resource_fields(@resource), f: f %> -
- - <%= edition_activities_fields(f, @resource) %> - <% end %> -
- <%= # cancel scheduled publishing doesn't require the edition to be saved when requesting an activity, - # because this action is triggered from a view where editing is not allowed. - edition_activities_forms(@resource, Edition::CANCEL_SCHEDULED_PUBLISHING_ACTION) %> - - <% tabs.reject {|t| t.name == "edit"}.each do |tab| %> -
-
- <%= render :partial => "/shared/#{tab.name}", :locals => {:publication => @resource} %> -
-
- <% end %> -
-
-<% content_for :page_title, "Editing #{@resource.title}" %> +<% content_for :page_title, @resource.title %> +<% content_for :title, @resource.title %> + +
+
+ <%= render "govuk_publishing_components/components/summary_list", { + items: [ + { + field: "Assigned to", + value: @resource.assigned_to, + }, + { + field: "Content type", + value: @resource.format.underscore.humanize, + }, + { + field: "Edition", + value: sanitize("#{@resource.version_number} #{@resource.status_text}"), + }, + ], + } %> +
+
diff --git a/app/views/editions/_major_change_fields.html.erb b/app/views/legacy_editions/_major_change_fields.html.erb similarity index 100% rename from app/views/editions/_major_change_fields.html.erb rename to app/views/legacy_editions/_major_change_fields.html.erb diff --git a/app/views/editions/_reviewer_field.html.erb b/app/views/legacy_editions/_reviewer_field.html.erb similarity index 100% rename from app/views/editions/_reviewer_field.html.erb rename to app/views/legacy_editions/_reviewer_field.html.erb diff --git a/app/views/editions/diagram.html.erb b/app/views/legacy_editions/diagram.html.erb similarity index 100% rename from app/views/editions/diagram.html.erb rename to app/views/legacy_editions/diagram.html.erb diff --git a/app/views/editions/diff.html.erb b/app/views/legacy_editions/diff.html.erb similarity index 100% rename from app/views/editions/diff.html.erb rename to app/views/legacy_editions/diff.html.erb diff --git a/app/views/editions/new.html.erb b/app/views/legacy_editions/new.html.erb similarity index 100% rename from app/views/editions/new.html.erb rename to app/views/legacy_editions/new.html.erb diff --git a/app/views/legacy_editions/show.html.erb b/app/views/legacy_editions/show.html.erb new file mode 100644 index 000000000..b9dae652e --- /dev/null +++ b/app/views/legacy_editions/show.html.erb @@ -0,0 +1,69 @@ +<% @edition = @resource %> + <%= render "shared/edition_header" %> + + <% errors_hash = errors_to_display(@edition) %> + <% if !errors_hash.empty? %> +
+

There is a problem

+ +
+ <% end %> + +
+ + +
+
+ + + <%= resource_form(@resource) do |f| %> +
+ <%= render resource_fields(@resource), f: f %> +
+ + <%= edition_activities_fields(f, @resource) %> + <% end %> +
+ <%= # cancel scheduled publishing doesn't require the edition to be saved when requesting an activity, + # because this action is triggered from a view where editing is not allowed. + edition_activities_forms(@resource, Edition::CANCEL_SCHEDULED_PUBLISHING_ACTION) %> + + <% tabs.reject {|t| t.name == "edit"}.each do |tab| %> +
+
+ <%= render :partial => "/shared/#{tab.name}", :locals => {:publication => @resource} %> +
+
+ <% end %> +
+
+<% content_for :page_title, "Editing #{@resource.title}" %> + diff --git a/config/features.rb b/config/features.rb index 9fe4b2a30..357a1fde6 100644 --- a/config/features.rb +++ b/config/features.rb @@ -12,4 +12,8 @@ feature :design_system_publications_filter, default: false, description: "Update the publications page to use the GOV.UK Design System" + + feature :design_system_edit, + default: false, + description: "Update the publications edit page to use the GOV.UK Design System" end diff --git a/config/routes.rb b/config/routes.rb index 3194bb031..342a257a5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -16,14 +16,20 @@ resources :artefacts, only: %i[new create update] - resources :editions do + constraints FeatureConstraint.new("design_system_edit") do + resources :editions, only: %i[show index] + end + + get "editions/:id" => "legacy_editions#show" + + resources :editions, controller: "legacy_editions" do member do get "diff" get "metadata" get "history" get "admin" - get "tagging", to: "editions#linking" - get "related_external_links", to: "editions#linking" + get "tagging", to: "legacy_editions#linking" + get "related_external_links", to: "legacy_editions#linking" get "unpublish" get "diagram" post "duplicate" @@ -33,7 +39,7 @@ post "progress" put "review" post "skip_fact_check", - to: "editions#progress", + to: "legacy_editions#progress", edition: { activity: { request_type: "skip_fact_check", @@ -66,6 +72,7 @@ constraints FeatureConstraint.new("design_system_publications_filter") do root to: "root#index" end + # The below "as: nil" is required to avoid a name clash with the constrained route, above, which causes an error root to: "legacy_root#index", as: nil diff --git a/test/functional/editions_controller_test.rb b/test/functional/editions_controller_test.rb index a3f4c61ae..6fee5ee07 100644 --- a/test/functional/editions_controller_test.rb +++ b/test/functional/editions_controller_test.rb @@ -7,76 +7,6 @@ class EditionsControllerTest < ActionController::TestCase stub_holidays_used_by_fact_check end - context "#create" do - setup do - @artefact = FactoryBot.create( - :artefact, - slug: "test", - kind: "answer", - name: "test", - owning_app: "publisher", - ) - end - - should "report publication counts on creation" do - Publisher::Application.edition_state_count_reporter.expects(:report) - post :create, - params: { - "edition" => { - "kind" => "answer", - "panopticon_id" => @artefact.id, - "title" => "a title", - }, - } - end - - should "update publishing API upon creation of new edition" do - UpdateWorker.expects(:perform_async) - - post :create, - params: { - "edition" => { - "kind" => "answer", - "panopticon_id" => @artefact.id, - "title" => "a title", - }, - } - end - - should "render the lgsl and lgil edit form successfully if creation fails" do - lgsl_code = 800 - FactoryBot.create( - :local_service, - lgsl_code:, - ) - artefact = FactoryBot.create(:artefact) - - post :create, - params: { - "edition" => { - "kind" => "local_transaction", - "lgsl_code" => lgsl_code, - "lgil_code" => 1, - "panopticon_id" => artefact.id, - "title" => "a title", - }, - } - assert_equal "302", response.code - - post :create, - params: { - "edition" => { - "kind" => "local_transaction", - "lgsl_code" => lgsl_code + 1, - "lgil_code" => 1, - "panopticon_id" => artefact.id, - "title" => "a title", - }, - } - assert_equal "200", response.code - end - end - context "#template_folder_for" do should "be able to create a view path for a given publication" do l = LocalTransactionEdition.new @@ -86,936 +16,6 @@ class EditionsControllerTest < ActionController::TestCase end end - context "#duplicate" do - context "Standard behaviour" do - setup do - @guide = FactoryBot.create(:guide_edition, panopticon_id: FactoryBot.create(:artefact).id) - EditionDuplicator.any_instance.expects(:duplicate).returns(true) - EditionDuplicator.any_instance.expects(:new_edition).returns(@guide) - end - - should "delegate complexity of duplication to appropriate collaborator" do - post :duplicate, params: { id: @guide.id } - assert_response :found - assert_equal "New edition created", flash[:success] - end - - should "update the publishing API upon duplication of an edition" do - UpdateWorker.expects(:perform_async).with(@guide.id.to_s) - post :duplicate, params: { id: @guide.id } - end - end - - context "Welsh editors" do - setup { login_as_welsh_editor } - - should "be able to duplicate Welsh editions" do - edition = FactoryBot.create(:guide_edition, :published, :welsh) - artefact = edition.artefact - - post :duplicate, params: { id: edition.id } - - assert_response :found - assert_redirected_to edition_path(artefact.latest_edition) - assert_not_equal edition, artefact.latest_edition - assert_equal "New edition created", flash[:success] - end - - should "not be able to duplicate non-Welsh editions" do - edition = FactoryBot.create(:guide_edition, :published) - artefact = edition.artefact - - post :duplicate, params: { id: edition.id } - - assert_response :found - assert_redirected_to edition_path(edition) - assert_equal edition, artefact.latest_edition - assert_equal "You do not have correct editor permissions for this action.", flash[:danger] - end - end - end - - context "#progress" do - setup do - @guide = FactoryBot.create(:guide_edition, panopticon_id: FactoryBot.create(:artefact).id) - end - - should "update status via progress and redirect to parent" do - EditionProgressor.any_instance.expects(:progress).returns(true) - EditionProgressor.any_instance.expects(:status_message).returns("Guide updated") - - post :progress, - params: { - id: @guide.id, - edition: { - activity: { - "request_type" => "send_fact_check", - "comment" => "Blah", - "email_addresses" => "user@example.com", - "customised_message" => "Hello", - }, - }, - } - - assert_redirected_to controller: "editions", action: "show", id: @guide.id - assert_equal "Guide updated", flash[:success] - end - - should "set an error message if it couldn't progress an edition" do - EditionProgressor.any_instance.expects(:progress).returns(false) - EditionProgressor.any_instance.expects(:status_message).returns("I failed") - - post :progress, - params: { - id: @guide.id.to_s, - edition: { - activity: { - "request_type" => "send_fact_check", - "email_addresses" => "", - }, - }, - } - assert_equal "I failed", flash[:danger] - end - - should "squash multiparameter attributes into a time field that has time-zone information" do - EditionProgressor.any_instance.expects(:progress).with(has_entry("publish_at", Time.zone.local(2014, 3, 4, 14, 47))) - - publish_at_params = { - "publish_at(1i)" => "2014", - "publish_at(2i)" => "3", - "publish_at(3i)" => "4", - "publish_at(4i)" => "14", - "publish_at(5i)" => "47", - } - - post :progress, - params: { - id: @guide.id.to_s, - edition: { - activity: { - "request_type" => "schedule_for_publishing", - }.merge(publish_at_params), - }, - } - end - - context "Welsh editors" do - setup do - login_as_welsh_editor - @artefact = FactoryBot.create(:artefact) - @edition = FactoryBot.create(:guide_edition, :scheduled_for_publishing, panopticon_id: @artefact.id) - @welsh_edition = FactoryBot.create(:guide_edition, :scheduled_for_publishing, :welsh) - end - - should "be able to cancel scheduled publishing for Welsh editions" do - ScheduledPublisher.expects(:cancel_scheduled_publishing).with(@welsh_edition.id.to_s).once - - post( - :progress, - params: { - id: @welsh_edition.id, - commit: "Cancel scheduled publishing", - edition: { - activity: { - request_type: "cancel_scheduled_publishing", - comment: "cancel this!", - }, - }, - }, - ) - - assert_redirected_to edition_path(@welsh_edition) - assert_equal flash[:success], "Guide updated" - @welsh_edition.reload - assert_equal @welsh_edition.state, "ready" - end - - should "not be able to cancel scheduled publishing for non-Welsh editions" do - ScheduledPublisher.expects(:cancel_scheduled_publishing).with(@edition.id.to_s).never - - post( - :progress, - params: { - id: @edition.id, - commit: "Cancel scheduled publishing", - edition: { - activity: { - request_type: "cancel_scheduled_publishing", - comment: "cancel this!", - }, - }, - }, - ) - - assert_redirected_to edition_path(@edition) - assert_equal flash[:danger], "You do not have correct editor permissions for this action." - @edition.reload - assert_equal @edition.state, "scheduled_for_publishing" - end - - should "be able to skip fact checks for Welsh editions" do - @welsh_edition.update!(state: "fact_check") - - post :progress, - params: { - id: @welsh_edition.id, - edition: { - activity: { - "request_type" => "skip_fact_check", - "comment" => "Fact check skipped by request.", - }, - }, - } - - assert_redirected_to edition_path(@welsh_edition) - @welsh_edition.reload - assert_equal flash[:success], "The fact check has been skipped for this publication." - assert_equal @welsh_edition.state, "ready" - end - - should "not be able to skip fact checks for non-Welsh editions" do - @edition.update!(state: "fact_check") - - post :progress, - params: { - id: @edition.id, - edition: { - activity: { - request_type: "skip_fact_check", - comment: "Fact check skipped by request.", - }, - }, - } - - assert_redirected_to edition_path(@edition) - @edition.reload - assert_equal @edition.state, "fact_check" - assert_equal flash[:danger], "You do not have correct editor permissions for this action." - end - end - end - - context "#update" do - setup do - @guide = FactoryBot.create(:guide_edition) - end - - should "update assignment" do - bob = FactoryBot.create(:user, :govuk_editor) - - post :update, - params: { - id: @guide.id, - edition: { assigned_to_id: bob.id }, - } - - @guide.reload - assert_equal bob, @guide.assigned_to - end - - should "clear assignment if no assignment is passed" do - post :update, - params: { - id: @guide.id, - edition: {}, - } - - @guide.reload - assert_nil @guide.assigned_to - end - - should "not create a new action if the assignment is unchanged" do - bob = FactoryBot.create(:user, :govuk_editor) - @user.assign(@guide, bob) - - post :update, - params: { - id: @guide.id, - edition: { assigned_to_id: bob.id }, - } - - @guide.reload - assert_equal 1, (@guide.actions.count { |a| a.request_type == Action::ASSIGN }) - end - - should "show the edit page again if updating fails" do - Edition.expects(:find).returns(@guide) - @guide.stubs(:update).returns(false) - @guide.errors.add(:title, "values") - - post :update, - params: { - id: @guide.id, - edition: { assigned_to_id: "" }, - } - assert_response :ok - end - - should "save the edition changes while performing an activity" do - post :update, - params: { - id: @guide.id, - commit: "Send to 2nd pair of eyes", - edition: { - title: "Updated title", - activity_request_review_attributes: { - request_type: "request_review", - comment: "Please review the updated title", - }, - }, - } - - @guide.reload - assert_equal "Updated title", @guide.title - assert_equal "in_review", @guide.state - assert_equal "Please review the updated title", @guide.actions.last.comment - end - - should "update the publishing API on successful update" do - UpdateWorker.expects(:perform_async).with(@guide.id.to_s, false) - - post :update, - params: { - id: @guide.id, - edition: { - title: "Updated title", - }, - } - end - - context "Welsh editors" do - setup do - login_as_welsh_editor - @edition = FactoryBot.create(:guide_edition, :ready) - @welsh_edition = FactoryBot.create(:guide_edition, :ready, :welsh) - end - - should "be able to update Welsh editions" do - post :update, - params: { - id: @welsh_edition.id, - edition: { - title: "Updated title", - }, - } - - assert_redirected_to edition_path(@welsh_edition) - @welsh_edition.reload - assert_equal @welsh_edition.title, "Updated title" - end - - should "not be able to update non-Welsh editions" do - post :update, - params: { - id: @edition.id, - edition: { - title: "Updated title", - }, - } - - assert_redirected_to edition_path(@edition) - @edition.reload - assert_not_equal @edition.title, "Updated title" - assert_equal "You do not have correct editor permissions for this action.", flash[:danger] - end - - should "be able to assign users to Welsh editions" do - assignees = [FactoryBot.create(:user, :welsh_editor), FactoryBot.create(:user, :govuk_editor)] - assignees.each do |assignee| - post :update, - params: { - id: @welsh_edition.id, - edition: { - assigned_to_id: assignee.id, - }, - } - - assert_redirected_to edition_path(@welsh_edition) - @welsh_edition.reload - assert_equal @welsh_edition.assigned_to, assignee - end - end - - should "not be able to assign users to non-Welsh editions" do - assignees = [FactoryBot.create(:user, :welsh_editor), FactoryBot.create(:user, :govuk_editor)] - assignees.each do |assignee| - post :update, - params: { - id: @edition.id, - edition: { - assigned_to_id: assignee.id, - }, - } - - assert_redirected_to edition_path(@edition) - assert_equal flash[:danger], "You do not have correct editor permissions for this action." - @edition.reload - assert_nil @edition.assigned_to - end - end - - should "not be able to be assigned to non-Welsh editions" do - login_as_govuk_editor - assignee = FactoryBot.create(:user, :welsh_editor) - - post :update, - params: { - id: @edition.id, - edition: { - assigned_to_id: assignee.id, - }, - } - - assert_redirected_to edition_path(@edition) - assert_equal flash[:danger], "Chosen assignee does not have correct editor permissions." - @edition.reload - assert_nil @edition.assigned_to - end - - should "be able to schedule publishing for Welsh editions" do - ScheduledPublisher.expects(:enqueue).with(@welsh_edition) - - post( - :update, - params: { - id: @welsh_edition.id, - edition: { - activity_schedule_for_publishing_attributes: { - request_type: "schedule_for_publishing", - "publish_at(1i)" => "2100", - "publish_at(2i)" => "12", - "publish_at(3i)" => "21", - "publish_at(4i)" => "10", - "publish_at(5i)" => "35", - }, - }, - commit: "Schedule for publishing", - }, - ) - - assert_redirected_to edition_path(@welsh_edition) - @welsh_edition.reload - assert_equal @welsh_edition.state, "scheduled_for_publishing" - assert_equal flash[:notice], "Guide edition was successfully updated." - end - - should "not be able to schedule publishing for non-Welsh editions" do - ScheduledPublisher.expects(:enqueue).with(@edition).never - - post( - :update, - params: { - id: @edition.id, - edition: { - activity_schedule_for_publishing_attributes: { - request_type: "schedule_for_publishing", - "publish_at(1i)" => "2020", - "publish_at(2i)" => "12", - "publish_at(3i)" => "21", - "publish_at(4i)" => "10", - "publish_at(5i)" => "35", - }, - }, - commit: "Schedule for publishing", - }, - ) - - assert_redirected_to edition_path(@edition) - @edition.reload - assert_equal @edition.state, "ready" - assert_equal flash[:danger], "You do not have correct editor permissions for this action." - end - - should "be able to publish a Welsh edition" do - UpdateWorker.expects(:perform_async).with(@welsh_edition.id.to_s, true) - - post :update, - params: { - id: @welsh_edition.id, - commit: "Send to publish", - edition: { - activity_publish_attributes: { - request_type: "publish", - comment: "Publish this!", - }, - }, - } - - assert_redirected_to edition_path(@welsh_edition) - @welsh_edition.reload - assert_equal @welsh_edition.state, "published" - assert_equal flash[:success], "Guide updated" - end - - should "not be able to publish a non-Welsh edition" do - UpdateWorker.expects(:perform_async).with(@edition.id.to_s, true).never - - post :update, - params: { - id: @edition.id, - commit: "Send to publish", - edition: { - activity_publish_attributes: { - request_type: "publish", - comment: "Publish this!", - }, - }, - } - - assert_redirected_to edition_path(@edition) - @edition.reload - assert_equal @edition.state, "ready" - assert_equal flash[:danger], "You do not have correct editor permissions for this action." - end - - should "be able to approve a review for Welsh editions" do - welsh_edition = FactoryBot.create(:guide_edition, :in_review, :welsh) - - UpdateWorker.expects(:perform_async).with(welsh_edition.id.to_s, false) - - post :update, - params: { - id: welsh_edition.id, - commit: "No changes needed", - edition: { - activity_approve_review_attributes: { - request_type: :approve_review, - comment: "LGTM", - }, - }, - } - - assert_redirected_to edition_path(welsh_edition) - welsh_edition.reload - assert_equal welsh_edition.state, "ready" - assert_equal flash[:success], "Guide updated" - end - - should "not be able to approve a review for non-Welsh editions" do - edition = FactoryBot.create(:guide_edition, :in_review) - - UpdateWorker.expects(:perform_async).with(edition.id.to_s, false).never - - post :update, - params: { - id: edition.id, - commit: "No changes needed", - edition: { - activity_approve_review_attributes: { - request_type: :approve_review, - comment: "LGTM", - }, - }, - } - - assert_redirected_to edition_path(edition) - edition.reload - assert_equal edition.state, "in_review" - assert_equal flash[:danger], "You do not have correct editor permissions for this action." - end - - should "be able to request a review for Welsh editions" do - welsh_edition = FactoryBot.create(:guide_edition, :draft, :welsh) - - UpdateWorker.expects(:perform_async).with(welsh_edition.id.to_s, false) - - post :update, - params: { - id: welsh_edition.id, - commit: "Send to 2nd pair of eyes", - edition: { - activity_request_review_attributes: { - request_type: :request_review, - comment: "Please review", - }, - }, - } - - assert_redirected_to edition_path(welsh_edition) - welsh_edition.reload - assert_equal welsh_edition.state, "in_review" - assert_equal flash[:success], "Guide updated" - end - - should "not be able to request a review for non-Welsh editions" do - edition = FactoryBot.create(:guide_edition, :draft) - - UpdateWorker.expects(:perform_async).with(edition.id.to_s, false).never - - post :update, - params: { - id: edition.id, - commit: "Send to 2nd pair of eyes", - edition: { - activity_request_review_attributes: { - request_type: :request_review, - comment: "Please review", - }, - }, - } - - assert_redirected_to edition_path(edition) - edition.reload - assert_equal edition.state, "draft" - assert_equal flash[:danger], "You do not have correct editor permissions for this action." - end - - should "be able to request amendments to a review for Welsh editions" do - UpdateWorker.expects(:perform_async).with(@welsh_edition.id.to_s, false) - - post :update, - params: { - id: @welsh_edition.id, - commit: "Request amendments", - edition: { - activity_request_amendments_attributes: { - request_type: :request_amendments, - comment: "Suggestion here", - }, - }, - } - - assert_redirected_to edition_path(@welsh_edition) - @welsh_edition.reload - assert_equal @welsh_edition.state, "amends_needed" - assert_equal flash[:success], "Guide updated" - end - - should "not be able to request amendments to a review for non-Welsh editions" do - UpdateWorker.expects(:perform_async).with(@edition.id.to_s, false).never - - post :update, - params: { - id: @edition.id, - commit: "Request amendments", - edition: { - activity_request_amendments_attributes: { - request_type: :request_amendments, - comment: "Suggestion here", - }, - }, - } - - assert_redirected_to edition_path(@edition) - @edition.reload - assert_equal @edition.state, "ready" - assert_equal flash[:danger], "You do not have correct editor permissions for this action." - end - - should "be able to request a fact check for Welsh editions" do - UpdateWorker.expects(:perform_async).with(@welsh_edition.id.to_s, false) - - post :update, - params: { - id: @welsh_edition.id, - commit: "Send to Fact check", - edition: { - activity_send_fact_check_attributes: { - request_type: "send_fact_check", - comment: "Blah", - email_addresses: "user@example.com", - customised_message: "Hello", - }, - }, - } - - assert_redirected_to edition_path(@welsh_edition) - @welsh_edition.reload - assert_equal flash[:success], "Guide updated" - assert_equal @welsh_edition.state, "fact_check" - end - - should "not be able to request a fact check for non-Welsh editions" do - UpdateWorker.expects(:perform_async).with(@edition.id.to_s, false).never - - post :update, - params: { - id: @edition.id, - commit: "Send to Fact check", - edition: { - activity_send_fact_check_attributes: { - request_type: "send_fact_check", - comment: "Blah", - email_addresses: "user@example.com", - customised_message: "Hello", - }, - }, - } - - assert_redirected_to edition_path(@edition) - @edition.reload - assert_equal @edition.state, "ready" - assert_equal flash[:danger], "You do not have correct editor permissions for this action." - end - - should "be able to resend fact check emails for Welsh editions" do - @welsh_edition.update!(state: "fact_check") - - previous_action = Action.new( - request_type: "send_fact_check", - email_addresses: "user@example.com", - comment: "Blah", - customised_message: "Hello", - edition: @welsh_edition, - ) - Edition.any_instance.stubs(:latest_status_action).returns(previous_action) - - UpdateWorker.expects(:perform_async).with(@welsh_edition.id.to_s, false) - - post :update, - params: { - id: @welsh_edition.id, - commit: "Resend fact check email", - edition: { - activity_resend_fact_check_attributes: { - request_type: "resend_fact_check", - comment: "Blah", - email_addresses: "user@example.com", - customised_message: "Hello", - }, - }, - } - - assert_redirected_to edition_path(@welsh_edition) - @welsh_edition.reload - assert_equal flash[:success], "Guide updated" - assert_equal @welsh_edition.state, "fact_check" - end - - should "not be able to resend fact check emails for non-Welsh editions" do - @edition.update!(state: "fact_check") - UpdateWorker.expects(:perform_async).with(@edition.id.to_s, false).never - - post :update, - params: { - id: @edition.id, - commit: "Resend fact check email", - edition: { - activity_resend_fact_check_attributes: { - request_type: "resend_fact_check", - comment: "Blah", - email_addresses: "user@example.com", - customised_message: "Hello", - }, - }, - } - - assert_redirected_to edition_path(@edition) - @edition.reload - assert_equal @edition.state, "fact_check" - assert_equal flash[:danger], "You do not have correct editor permissions for this action." - end - - should "be able to approve a fact check for Welsh editions" do - UpdateWorker.expects(:perform_async).with(@welsh_edition.id.to_s, false) - - post :update, - params: { - id: @welsh_edition.id, - commit: "Approve Fact check", - edition: { - activity_approve_fact_check_attributes: { - request_type: "approve_fact_check", - comment: "lgtm", - }, - }, - } - - assert_redirected_to edition_path(@welsh_edition) - @welsh_edition.reload - assert_equal flash[:notice], "Guide edition was successfully updated." - assert_equal @welsh_edition.state, "ready" - end - - should "not be able to approve a fact check for non-Welsh editions" do - @edition.update!(state: "fact_check_received") - UpdateWorker.expects(:perform_async).with(@edition.id.to_s, false).never - - post :update, - params: { - id: @edition.id, - commit: "Approve Fact check", - edition: { - activity_approve_fact_check_attributes: { - request_type: "approve_fact_check", - comment: "lgtm", - }, - }, - } - - assert_redirected_to edition_path(@edition) - @edition.reload - assert_equal @edition.state, "fact_check_received" - assert_equal flash[:danger], "You do not have correct editor permissions for this action." - end - end - end - - context "#review" do - setup do - artefact = FactoryBot.create(:artefact) - - @guide = FactoryBot.create( - :guide_edition, - state: "in_review", - review_requested_at: Time.zone.now, - panopticon_id: artefact.id, - ) - end - - should "update the reviewer" do - bob = FactoryBot.create(:user, name: "bob") - - put :review, - params: { - id: @guide.id, - edition: { reviewer: bob.name }, - } - - @guide.reload - assert_equal bob.name, @guide.reviewer - end - - should "not be able to update the reviewer when edition is scheduled for publishing" do - bob = FactoryBot.create(:user, name: "bob") - edition = FactoryBot.create(:edition, :scheduled_for_publishing) - - put :review, - params: { - id: edition.id, - edition: { reviewer: bob.name }, - } - - assert_response(:found) - assert_equal "Something went wrong when attempting to claim 2i.", flash[:danger] - end - - context "Welsh editors" do - setup do - @welsh_guide = FactoryBot.create(:guide_edition, :welsh, :in_review) - login_as_welsh_editor - @welsh_user = @user - end - - should "be able to claim a review for Welsh editions" do - put :review, - params: { - id: @welsh_guide.id, - edition: { reviewer: @welsh_user.name }, - } - - assert_redirected_to edition_path(@welsh_guide) - assert_equal "You are the reviewer of this guide.", flash[:success] - @welsh_guide.reload - assert_equal @welsh_user.name, @welsh_guide.reviewer - end - - should "not be able to claim a review for non-Welsh editions" do - put :review, - params: { - id: @guide.id, - edition: { reviewer: @welsh_user.name }, - } - - assert_redirected_to edition_path(@guide) - assert_equal "You do not have correct editor permissions for this action.", flash[:danger] - @guide.reload - assert_nil @guide.reviewer - end - end - end - - context "#destroy" do - setup do - artefact1 = FactoryBot.create( - :artefact, - slug: "test", - kind: "transaction", - name: "test", - owning_app: "publisher", - ) - @transaction = TransactionEdition.create!(title: "test", slug: "test", panopticon_id: artefact1.id) - - artefact2 = FactoryBot.create( - :artefact, - slug: "test2", - kind: "guide", - name: "test", - owning_app: "publisher", - ) - @guide = GuideEdition.create!(title: "test", slug: "test2", panopticon_id: artefact2.id) - - stub_request(:delete, "#{Plek.find('arbiter')}/slugs/test").to_return(status: 200) - end - - should "destroy transaction" do - assert @transaction.can_destroy? - assert_difference("TransactionEdition.count", -1) do - delete :destroy, params: { id: @transaction.id } - end - assert_redirected_to root_path - end - - should "can't destroy published transaction" do - @transaction.state = "ready" - stub_register_published_content - @transaction.publish - assert_not @transaction.can_destroy? - @transaction.save! - assert_difference("TransactionEdition.count", 0) do - delete :destroy, params: { id: @transaction.id } - end - end - - should "destroy guide" do - assert @guide.can_destroy? - assert_difference("GuideEdition.count", -1) do - delete :destroy, params: { id: @guide.id } - end - assert_redirected_to root_path - end - - should "can't destroy published guide" do - @guide.state = "ready" - @guide.save! - stub_register_published_content - @guide.publish - @guide.save! - assert @guide.published? - assert_not @guide.can_destroy? - - assert_difference("GuideEdition.count", 0) do - delete :destroy, params: { id: @guide.id } - end - end - - context "Welsh editors" do - setup do - login_as_welsh_editor - @welsh_guide = FactoryBot.create(:guide_edition, :welsh) - end - - should "not be able to destroy non-Welsh editions" do - assert_difference("GuideEdition.count", 0) do - delete :destroy, params: { id: @guide.id } - end - - assert_redirected_to edition_path(@guide) - assert_equal "You do not have correct editor permissions for this action.", flash[:danger] - end - - should "be able to destroy Welsh editions" do - assert_difference("GuideEdition.count", -1) do - delete :destroy, params: { id: @welsh_guide.id } - end - - assert_redirected_to root_path - assert_equal "Edition deleted", flash[:success] - end - end - end - context "#index" do should "editions index redirects to root" do get :index @@ -1046,262 +46,5 @@ class EditionsControllerTest < ActionController::TestCase assert_response :success assert_not_nil assigns(:resource) end - - should "render a link to the diagram when edition is a simple smart answer" do - simple_smart_answer_artefact = FactoryBot.create( - :artefact, - slug: "my-simple-smart-answer", - kind: "guide", - name: "test", - owning_app: "publisher", - ) - simple_smart_answer = SimpleSmartAnswerEdition.create!( - title: "test ssa", - panopticon_id: simple_smart_answer_artefact.id, - ) - - get :show, params: { id: simple_smart_answer.id } - - assert_select ".link-check-report p", { text: "View the flow diagram (opens in a new tab)" } do - assert_select "a[href=?]", diagram_edition_path(simple_smart_answer).to_s, - { count: 1, text: "flow diagram (opens in a new tab)" } - end - end - - should "not render a link to the diagram when edition is not a simple smart answer" do - get :show, params: { id: @guide.id } - assert_select "p", { count: 0, text: "View the flow diagram (opens in a new tab)" } - end - end - - context "#admin" do - setup do - @guide = FactoryBot.create(:guide_edition) - end - - should "show the admin page for the edition" do - get :admin, params: { id: @guide.id } - - assert_response :success - end - - context "Welsh editors" do - setup do - login_as_welsh_editor - @welsh_guide = FactoryBot.create(:guide_edition, :welsh) - end - - should "be able to see the admin page for Welsh editions" do - get :admin, params: { id: @welsh_guide.id } - - assert_response :success - end - - should "not be able to see the admin page for non-Welsh editions" do - get :admin, params: { id: @guide.id } - - assert_redirected_to edition_path(@guide) - assert_equal "You do not have correct editor permissions for this action.", flash[:danger] - end - end - end - - context "#diff" do - should "we can diff the last edition" do - first_edition = FactoryBot.create(:guide_edition, state: "published") - second_edition = first_edition.build_clone(GuideEdition) - second_edition.save! - second_edition.reload - - get :diff, params: { id: second_edition.id } - assert_response :success - end - end - - context "#unpublish" do - setup do - @guide = FactoryBot.create(:guide_edition, :published, panopticon_id: FactoryBot.create(:artefact).id) - @redirect_url = "https://www.example.com/somewhere_else" - end - - should "update publishing API upon unpublishing" do - UnpublishService.expects(:call).with(@guide.artefact, @user, @redirect_url) - - post :process_unpublish, - params: { - id: @guide.id, - redirect_url: @redirect_url, - } - end - - should "redirect and display success message after successful unpublish" do - UnpublishService.stubs(:call).with(@guide.artefact, @user, @redirect_url).returns(true) - - post :process_unpublish, - params: { - id: @guide.id, - redirect_url: @redirect_url, - } - - assert_redirected_to root_path - assert_equal "Content unpublished and redirected", flash[:notice] - end - - should "not be able to unpublish with invalid redirect url" do - post :process_unpublish, - params: { - id: @guide.id, - redirect_url: "invalid redirect url", - } - - assert_equal "Redirect path is invalid. Guide has not been unpublished.", flash[:danger] - end - - should "alert if unable to unpublish" do - UnpublishService.stubs(:call).with(@guide.artefact, @user, @redirect_url).returns(nil) - - post :process_unpublish, - params: { - id: @guide.id, - redirect_url: @redirect_url, - } - - assert_equal "Due to a service problem, the edition couldn't be unpublished", flash[:alert] - end - - context "Welsh editors" do - setup do - login_as_welsh_editor - @welsh_guide = FactoryBot.create(:guide_edition, :published, :welsh) - end - - should "not be able to access the unpublish page of non-Welsh editions" do - get :unpublish, params: { id: @guide.id } - - assert_redirected_to edition_path(@guide) - assert_equal "You do not have permission to see this page.", flash[:danger] - end - - should "not be able to access the unpublish page of Welsh editions" do - get :unpublish, params: { id: @welsh_guide.id } - - assert_redirected_to edition_path(@welsh_guide) - assert_equal "You do not have permission to see this page.", flash[:danger] - end - - should "not be allowed to unpublish a Welsh edition" do - UnpublishService.expects(:call).with(@welsh_guide.artefact, @user, @redirect_url).never - - post :process_unpublish, - params: { - id: @welsh_guide.id, - redirect_url: @redirect_url, - } - - assert_redirected_to edition_path(@welsh_guide) - @welsh_guide.reload - assert_equal @welsh_guide.state, "published" - assert_equal "You do not have permission to see this page.", flash[:danger] - end - - should "not be allowed to unpublish a non-Welsh edition" do - UnpublishService.expects(:call).with(@guide.artefact, @user, @redirect_url).never - - post :process_unpublish, - params: { - id: @guide.id, - redirect_url: @redirect_url, - } - - assert_redirected_to edition_path(@guide) - @guide.reload - assert_equal @guide.state, "published" - assert_equal "You do not have permission to see this page.", flash[:danger] - end - end - end - - context "given a simple smart answer" do - setup do - @artefact = FactoryBot.create(:artefact, slug: "foo", name: "Foo", kind: "simple_smart_answer", owning_app: "publisher") - @edition = FactoryBot.create(:simple_smart_answer_edition, body: "blah", state: "draft", slug: "foo", panopticon_id: @artefact.id) - @edition.nodes.build( - kind: "question", - slug: "question-1", - title: "Question One", - options_attributes: [ - { label: "Option One", next_node: "outcome-1" }, - { label: "Option Two", next_node: "outcome-2" }, - ], - ) - @edition.nodes.build(kind: "outcome", slug: "outcome-1", title: "Outcome One") - @edition.nodes.build(kind: "outcome", slug: "outcome-2", title: "Outcome Two") - @edition.save! - end - - should "remove an option and node from simple smart answer in single request" do - atts = { - nodes_attributes: { - "0" => { - "id" => @edition.nodes.all[0].id, - "options_attributes" => { - "0" => { "id" => @edition.nodes.first.options.all[0].id }, - "1" => { "id" => @edition.nodes.first.options.all[1].id, "_destroy" => "1" }, - }, - }, - "1" => { - "id" => @edition.nodes.all[1].id, - }, - "2" => { - "id" => @edition.nodes.all[2].id, - "_destroy" => "1", - }, - }, - } - put :update, - params: { - id: @edition.id, - edition: atts, - } - assert_redirected_to edition_path(@edition) - - @edition.reload - - assert_equal 2, @edition.nodes.count - assert_equal 1, @edition.nodes.where(kind: "question").count - assert_equal 1, @edition.nodes.where(kind: "outcome").count - - question = @edition.nodes.where(kind: "question").first - assert_equal 1, question.options.count - assert_equal "Option One", question.options.first.label - end - end - - context "#diagram" do - context "given a simple smart answer exists" do - setup do - @artefact = FactoryBot.create(:artefact, slug: "foo", name: "Foo", kind: "simple_smart_answer", owning_app: "publisher") - @edition = FactoryBot.create(:simple_smart_answer_edition, body: "blah", state: "draft", slug: "foo", panopticon_id: @artefact.id) - @edition.save! - end - - should "render a diagram page for it" do - get :diagram, params: { id: @edition.id } - - assert_response :success - assert_select "title", "Diagram for #{@edition.title} | GOV.UK Publisher" - end - end - - context "given a non-simple smart answer exists" do - setup do - @welsh_guide = FactoryBot.create(:guide_edition, :welsh, :in_review) - end - - should "return a 404" do - get :diagram, params: { id: @welsh_guide.id } - assert_response :not_found - end - end end end diff --git a/test/functional/legacy_editions_controller_test.rb b/test/functional/legacy_editions_controller_test.rb new file mode 100644 index 000000000..79169c096 --- /dev/null +++ b/test/functional/legacy_editions_controller_test.rb @@ -0,0 +1,1307 @@ +require "test_helper" + +class LegacyEditionsControllerTest < ActionController::TestCase + setup do + login_as_stub_user + stub_linkables + stub_holidays_used_by_fact_check + end + + context "#create" do + setup do + @artefact = FactoryBot.create( + :artefact, + slug: "test", + kind: "answer", + name: "test", + owning_app: "publisher", + ) + end + + should "report publication counts on creation" do + Publisher::Application.edition_state_count_reporter.expects(:report) + post :create, + params: { + "edition" => { + "kind" => "answer", + "panopticon_id" => @artefact.id, + "title" => "a title", + }, + } + end + + should "update publishing API upon creation of new edition" do + UpdateWorker.expects(:perform_async) + + post :create, + params: { + "edition" => { + "kind" => "answer", + "panopticon_id" => @artefact.id, + "title" => "a title", + }, + } + end + + should "render the lgsl and lgil edit form successfully if creation fails" do + lgsl_code = 800 + FactoryBot.create( + :local_service, + lgsl_code:, + ) + artefact = FactoryBot.create(:artefact) + + post :create, + params: { + "edition" => { + "kind" => "local_transaction", + "lgsl_code" => lgsl_code, + "lgil_code" => 1, + "panopticon_id" => artefact.id, + "title" => "a title", + }, + } + assert_equal "302", response.code + + post :create, + params: { + "edition" => { + "kind" => "local_transaction", + "lgsl_code" => lgsl_code + 1, + "lgil_code" => 1, + "panopticon_id" => artefact.id, + "title" => "a title", + }, + } + assert_equal "200", response.code + end + end + + context "#template_folder_for" do + should "be able to create a view path for a given publication" do + l = LocalTransactionEdition.new + assert_equal "app/views/local_transactions", @controller.template_folder_for(l) + g = GuideEdition.new + assert_equal "app/views/guides", @controller.template_folder_for(g) + end + end + + context "#duplicate" do + context "Standard behaviour" do + setup do + @guide = FactoryBot.create(:guide_edition, panopticon_id: FactoryBot.create(:artefact).id) + EditionDuplicator.any_instance.expects(:duplicate).returns(true) + EditionDuplicator.any_instance.expects(:new_edition).returns(@guide) + end + + should "delegate complexity of duplication to appropriate collaborator" do + post :duplicate, params: { id: @guide.id } + assert_response :found + assert_equal "New edition created", flash[:success] + end + + should "update the publishing API upon duplication of an edition" do + UpdateWorker.expects(:perform_async).with(@guide.id.to_s) + post :duplicate, params: { id: @guide.id } + end + end + + context "Welsh editors" do + setup { login_as_welsh_editor } + + should "be able to duplicate Welsh editions" do + edition = FactoryBot.create(:guide_edition, :published, :welsh) + artefact = edition.artefact + + post :duplicate, params: { id: edition.id } + + assert_response :found + assert_redirected_to edition_path(artefact.latest_edition) + assert_not_equal edition, artefact.latest_edition + assert_equal "New edition created", flash[:success] + end + + should "not be able to duplicate non-Welsh editions" do + edition = FactoryBot.create(:guide_edition, :published) + artefact = edition.artefact + + post :duplicate, params: { id: edition.id } + + assert_response :found + assert_redirected_to edition_path(edition) + assert_equal edition, artefact.latest_edition + assert_equal "You do not have correct editor permissions for this action.", flash[:danger] + end + end + end + + context "#progress" do + setup do + @guide = FactoryBot.create(:guide_edition, panopticon_id: FactoryBot.create(:artefact).id) + end + + should "update status via progress and redirect to parent" do + EditionProgressor.any_instance.expects(:progress).returns(true) + EditionProgressor.any_instance.expects(:status_message).returns("Guide updated") + + post :progress, + params: { + id: @guide.id, + edition: { + activity: { + "request_type" => "send_fact_check", + "comment" => "Blah", + "email_addresses" => "user@example.com", + "customised_message" => "Hello", + }, + }, + } + + assert_redirected_to controller: "editions", action: "show", id: @guide.id + assert_equal "Guide updated", flash[:success] + end + + should "set an error message if it couldn't progress an edition" do + EditionProgressor.any_instance.expects(:progress).returns(false) + EditionProgressor.any_instance.expects(:status_message).returns("I failed") + + post :progress, + params: { + id: @guide.id.to_s, + edition: { + activity: { + "request_type" => "send_fact_check", + "email_addresses" => "", + }, + }, + } + assert_equal "I failed", flash[:danger] + end + + should "squash multiparameter attributes into a time field that has time-zone information" do + EditionProgressor.any_instance.expects(:progress).with(has_entry("publish_at", Time.zone.local(2014, 3, 4, 14, 47))) + + publish_at_params = { + "publish_at(1i)" => "2014", + "publish_at(2i)" => "3", + "publish_at(3i)" => "4", + "publish_at(4i)" => "14", + "publish_at(5i)" => "47", + } + + post :progress, + params: { + id: @guide.id.to_s, + edition: { + activity: { + "request_type" => "schedule_for_publishing", + }.merge(publish_at_params), + }, + } + end + + context "Welsh editors" do + setup do + login_as_welsh_editor + @artefact = FactoryBot.create(:artefact) + @edition = FactoryBot.create(:guide_edition, :scheduled_for_publishing, panopticon_id: @artefact.id) + @welsh_edition = FactoryBot.create(:guide_edition, :scheduled_for_publishing, :welsh) + end + + should "be able to cancel scheduled publishing for Welsh editions" do + ScheduledPublisher.expects(:cancel_scheduled_publishing).with(@welsh_edition.id.to_s).once + + post( + :progress, + params: { + id: @welsh_edition.id, + commit: "Cancel scheduled publishing", + edition: { + activity: { + request_type: "cancel_scheduled_publishing", + comment: "cancel this!", + }, + }, + }, + ) + + assert_redirected_to edition_path(@welsh_edition) + assert_equal flash[:success], "Guide updated" + @welsh_edition.reload + assert_equal @welsh_edition.state, "ready" + end + + should "not be able to cancel scheduled publishing for non-Welsh editions" do + ScheduledPublisher.expects(:cancel_scheduled_publishing).with(@edition.id.to_s).never + + post( + :progress, + params: { + id: @edition.id, + commit: "Cancel scheduled publishing", + edition: { + activity: { + request_type: "cancel_scheduled_publishing", + comment: "cancel this!", + }, + }, + }, + ) + + assert_redirected_to edition_path(@edition) + assert_equal flash[:danger], "You do not have correct editor permissions for this action." + @edition.reload + assert_equal @edition.state, "scheduled_for_publishing" + end + + should "be able to skip fact checks for Welsh editions" do + @welsh_edition.update!(state: "fact_check") + + post :progress, + params: { + id: @welsh_edition.id, + edition: { + activity: { + "request_type" => "skip_fact_check", + "comment" => "Fact check skipped by request.", + }, + }, + } + + assert_redirected_to edition_path(@welsh_edition) + @welsh_edition.reload + assert_equal flash[:success], "The fact check has been skipped for this publication." + assert_equal @welsh_edition.state, "ready" + end + + should "not be able to skip fact checks for non-Welsh editions" do + @edition.update!(state: "fact_check") + + post :progress, + params: { + id: @edition.id, + edition: { + activity: { + request_type: "skip_fact_check", + comment: "Fact check skipped by request.", + }, + }, + } + + assert_redirected_to edition_path(@edition) + @edition.reload + assert_equal @edition.state, "fact_check" + assert_equal flash[:danger], "You do not have correct editor permissions for this action." + end + end + end + + context "#update" do + setup do + @guide = FactoryBot.create(:guide_edition) + end + + should "update assignment" do + bob = FactoryBot.create(:user, :govuk_editor) + + post :update, + params: { + id: @guide.id, + edition: { assigned_to_id: bob.id }, + } + + @guide.reload + assert_equal bob, @guide.assigned_to + end + + should "clear assignment if no assignment is passed" do + post :update, + params: { + id: @guide.id, + edition: {}, + } + + @guide.reload + assert_nil @guide.assigned_to + end + + should "not create a new action if the assignment is unchanged" do + bob = FactoryBot.create(:user, :govuk_editor) + @user.assign(@guide, bob) + + post :update, + params: { + id: @guide.id, + edition: { assigned_to_id: bob.id }, + } + + @guide.reload + assert_equal 1, (@guide.actions.count { |a| a.request_type == Action::ASSIGN }) + end + + should "show the edit page again if updating fails" do + Edition.expects(:find).returns(@guide) + @guide.stubs(:update).returns(false) + @guide.errors.add(:title, "values") + + post :update, + params: { + id: @guide.id, + edition: { assigned_to_id: "" }, + } + assert_response :ok + end + + should "save the edition changes while performing an activity" do + post :update, + params: { + id: @guide.id, + commit: "Send to 2nd pair of eyes", + edition: { + title: "Updated title", + activity_request_review_attributes: { + request_type: "request_review", + comment: "Please review the updated title", + }, + }, + } + + @guide.reload + assert_equal "Updated title", @guide.title + assert_equal "in_review", @guide.state + assert_equal "Please review the updated title", @guide.actions.last.comment + end + + should "update the publishing API on successful update" do + UpdateWorker.expects(:perform_async).with(@guide.id.to_s, false) + + post :update, + params: { + id: @guide.id, + edition: { + title: "Updated title", + }, + } + end + + context "Welsh editors" do + setup do + login_as_welsh_editor + @edition = FactoryBot.create(:guide_edition, :ready) + @welsh_edition = FactoryBot.create(:guide_edition, :ready, :welsh) + end + + should "be able to update Welsh editions" do + post :update, + params: { + id: @welsh_edition.id, + edition: { + title: "Updated title", + }, + } + + assert_redirected_to edition_path(@welsh_edition) + @welsh_edition.reload + assert_equal @welsh_edition.title, "Updated title" + end + + should "not be able to update non-Welsh editions" do + post :update, + params: { + id: @edition.id, + edition: { + title: "Updated title", + }, + } + + assert_redirected_to edition_path(@edition) + @edition.reload + assert_not_equal @edition.title, "Updated title" + assert_equal "You do not have correct editor permissions for this action.", flash[:danger] + end + + should "be able to assign users to Welsh editions" do + assignees = [FactoryBot.create(:user, :welsh_editor), FactoryBot.create(:user, :govuk_editor)] + assignees.each do |assignee| + post :update, + params: { + id: @welsh_edition.id, + edition: { + assigned_to_id: assignee.id, + }, + } + + assert_redirected_to edition_path(@welsh_edition) + @welsh_edition.reload + assert_equal @welsh_edition.assigned_to, assignee + end + end + + should "not be able to assign users to non-Welsh editions" do + assignees = [FactoryBot.create(:user, :welsh_editor), FactoryBot.create(:user, :govuk_editor)] + assignees.each do |assignee| + post :update, + params: { + id: @edition.id, + edition: { + assigned_to_id: assignee.id, + }, + } + + assert_redirected_to edition_path(@edition) + assert_equal flash[:danger], "You do not have correct editor permissions for this action." + @edition.reload + assert_nil @edition.assigned_to + end + end + + should "not be able to be assigned to non-Welsh editions" do + login_as_govuk_editor + assignee = FactoryBot.create(:user, :welsh_editor) + + post :update, + params: { + id: @edition.id, + edition: { + assigned_to_id: assignee.id, + }, + } + + assert_redirected_to edition_path(@edition) + assert_equal flash[:danger], "Chosen assignee does not have correct editor permissions." + @edition.reload + assert_nil @edition.assigned_to + end + + should "be able to schedule publishing for Welsh editions" do + ScheduledPublisher.expects(:enqueue).with(@welsh_edition) + + post( + :update, + params: { + id: @welsh_edition.id, + edition: { + activity_schedule_for_publishing_attributes: { + request_type: "schedule_for_publishing", + "publish_at(1i)" => "2100", + "publish_at(2i)" => "12", + "publish_at(3i)" => "21", + "publish_at(4i)" => "10", + "publish_at(5i)" => "35", + }, + }, + commit: "Schedule for publishing", + }, + ) + + assert_redirected_to edition_path(@welsh_edition) + @welsh_edition.reload + assert_equal @welsh_edition.state, "scheduled_for_publishing" + assert_equal flash[:notice], "Guide edition was successfully updated." + end + + should "not be able to schedule publishing for non-Welsh editions" do + ScheduledPublisher.expects(:enqueue).with(@edition).never + + post( + :update, + params: { + id: @edition.id, + edition: { + activity_schedule_for_publishing_attributes: { + request_type: "schedule_for_publishing", + "publish_at(1i)" => "2020", + "publish_at(2i)" => "12", + "publish_at(3i)" => "21", + "publish_at(4i)" => "10", + "publish_at(5i)" => "35", + }, + }, + commit: "Schedule for publishing", + }, + ) + + assert_redirected_to edition_path(@edition) + @edition.reload + assert_equal @edition.state, "ready" + assert_equal flash[:danger], "You do not have correct editor permissions for this action." + end + + should "be able to publish a Welsh edition" do + UpdateWorker.expects(:perform_async).with(@welsh_edition.id.to_s, true) + + post :update, + params: { + id: @welsh_edition.id, + commit: "Send to publish", + edition: { + activity_publish_attributes: { + request_type: "publish", + comment: "Publish this!", + }, + }, + } + + assert_redirected_to edition_path(@welsh_edition) + @welsh_edition.reload + assert_equal @welsh_edition.state, "published" + assert_equal flash[:success], "Guide updated" + end + + should "not be able to publish a non-Welsh edition" do + UpdateWorker.expects(:perform_async).with(@edition.id.to_s, true).never + + post :update, + params: { + id: @edition.id, + commit: "Send to publish", + edition: { + activity_publish_attributes: { + request_type: "publish", + comment: "Publish this!", + }, + }, + } + + assert_redirected_to edition_path(@edition) + @edition.reload + assert_equal @edition.state, "ready" + assert_equal flash[:danger], "You do not have correct editor permissions for this action." + end + + should "be able to approve a review for Welsh editions" do + welsh_edition = FactoryBot.create(:guide_edition, :in_review, :welsh) + + UpdateWorker.expects(:perform_async).with(welsh_edition.id.to_s, false) + + post :update, + params: { + id: welsh_edition.id, + commit: "No changes needed", + edition: { + activity_approve_review_attributes: { + request_type: :approve_review, + comment: "LGTM", + }, + }, + } + + assert_redirected_to edition_path(welsh_edition) + welsh_edition.reload + assert_equal welsh_edition.state, "ready" + assert_equal flash[:success], "Guide updated" + end + + should "not be able to approve a review for non-Welsh editions" do + edition = FactoryBot.create(:guide_edition, :in_review) + + UpdateWorker.expects(:perform_async).with(edition.id.to_s, false).never + + post :update, + params: { + id: edition.id, + commit: "No changes needed", + edition: { + activity_approve_review_attributes: { + request_type: :approve_review, + comment: "LGTM", + }, + }, + } + + assert_redirected_to edition_path(edition) + edition.reload + assert_equal edition.state, "in_review" + assert_equal flash[:danger], "You do not have correct editor permissions for this action." + end + + should "be able to request a review for Welsh editions" do + welsh_edition = FactoryBot.create(:guide_edition, :draft, :welsh) + + UpdateWorker.expects(:perform_async).with(welsh_edition.id.to_s, false) + + post :update, + params: { + id: welsh_edition.id, + commit: "Send to 2nd pair of eyes", + edition: { + activity_request_review_attributes: { + request_type: :request_review, + comment: "Please review", + }, + }, + } + + assert_redirected_to edition_path(welsh_edition) + welsh_edition.reload + assert_equal welsh_edition.state, "in_review" + assert_equal flash[:success], "Guide updated" + end + + should "not be able to request a review for non-Welsh editions" do + edition = FactoryBot.create(:guide_edition, :draft) + + UpdateWorker.expects(:perform_async).with(edition.id.to_s, false).never + + post :update, + params: { + id: edition.id, + commit: "Send to 2nd pair of eyes", + edition: { + activity_request_review_attributes: { + request_type: :request_review, + comment: "Please review", + }, + }, + } + + assert_redirected_to edition_path(edition) + edition.reload + assert_equal edition.state, "draft" + assert_equal flash[:danger], "You do not have correct editor permissions for this action." + end + + should "be able to request amendments to a review for Welsh editions" do + UpdateWorker.expects(:perform_async).with(@welsh_edition.id.to_s, false) + + post :update, + params: { + id: @welsh_edition.id, + commit: "Request amendments", + edition: { + activity_request_amendments_attributes: { + request_type: :request_amendments, + comment: "Suggestion here", + }, + }, + } + + assert_redirected_to edition_path(@welsh_edition) + @welsh_edition.reload + assert_equal @welsh_edition.state, "amends_needed" + assert_equal flash[:success], "Guide updated" + end + + should "not be able to request amendments to a review for non-Welsh editions" do + UpdateWorker.expects(:perform_async).with(@edition.id.to_s, false).never + + post :update, + params: { + id: @edition.id, + commit: "Request amendments", + edition: { + activity_request_amendments_attributes: { + request_type: :request_amendments, + comment: "Suggestion here", + }, + }, + } + + assert_redirected_to edition_path(@edition) + @edition.reload + assert_equal @edition.state, "ready" + assert_equal flash[:danger], "You do not have correct editor permissions for this action." + end + + should "be able to request a fact check for Welsh editions" do + UpdateWorker.expects(:perform_async).with(@welsh_edition.id.to_s, false) + + post :update, + params: { + id: @welsh_edition.id, + commit: "Send to Fact check", + edition: { + activity_send_fact_check_attributes: { + request_type: "send_fact_check", + comment: "Blah", + email_addresses: "user@example.com", + customised_message: "Hello", + }, + }, + } + + assert_redirected_to edition_path(@welsh_edition) + @welsh_edition.reload + assert_equal flash[:success], "Guide updated" + assert_equal @welsh_edition.state, "fact_check" + end + + should "not be able to request a fact check for non-Welsh editions" do + UpdateWorker.expects(:perform_async).with(@edition.id.to_s, false).never + + post :update, + params: { + id: @edition.id, + commit: "Send to Fact check", + edition: { + activity_send_fact_check_attributes: { + request_type: "send_fact_check", + comment: "Blah", + email_addresses: "user@example.com", + customised_message: "Hello", + }, + }, + } + + assert_redirected_to edition_path(@edition) + @edition.reload + assert_equal @edition.state, "ready" + assert_equal flash[:danger], "You do not have correct editor permissions for this action." + end + + should "be able to resend fact check emails for Welsh editions" do + @welsh_edition.update!(state: "fact_check") + + previous_action = Action.new( + request_type: "send_fact_check", + email_addresses: "user@example.com", + comment: "Blah", + customised_message: "Hello", + edition: @welsh_edition, + ) + Edition.any_instance.stubs(:latest_status_action).returns(previous_action) + + UpdateWorker.expects(:perform_async).with(@welsh_edition.id.to_s, false) + + post :update, + params: { + id: @welsh_edition.id, + commit: "Resend fact check email", + edition: { + activity_resend_fact_check_attributes: { + request_type: "resend_fact_check", + comment: "Blah", + email_addresses: "user@example.com", + customised_message: "Hello", + }, + }, + } + + assert_redirected_to edition_path(@welsh_edition) + @welsh_edition.reload + assert_equal flash[:success], "Guide updated" + assert_equal @welsh_edition.state, "fact_check" + end + + should "not be able to resend fact check emails for non-Welsh editions" do + @edition.update!(state: "fact_check") + UpdateWorker.expects(:perform_async).with(@edition.id.to_s, false).never + + post :update, + params: { + id: @edition.id, + commit: "Resend fact check email", + edition: { + activity_resend_fact_check_attributes: { + request_type: "resend_fact_check", + comment: "Blah", + email_addresses: "user@example.com", + customised_message: "Hello", + }, + }, + } + + assert_redirected_to edition_path(@edition) + @edition.reload + assert_equal @edition.state, "fact_check" + assert_equal flash[:danger], "You do not have correct editor permissions for this action." + end + + should "be able to approve a fact check for Welsh editions" do + UpdateWorker.expects(:perform_async).with(@welsh_edition.id.to_s, false) + + post :update, + params: { + id: @welsh_edition.id, + commit: "Approve Fact check", + edition: { + activity_approve_fact_check_attributes: { + request_type: "approve_fact_check", + comment: "lgtm", + }, + }, + } + + assert_redirected_to edition_path(@welsh_edition) + @welsh_edition.reload + assert_equal flash[:notice], "Guide edition was successfully updated." + assert_equal @welsh_edition.state, "ready" + end + + should "not be able to approve a fact check for non-Welsh editions" do + @edition.update!(state: "fact_check_received") + UpdateWorker.expects(:perform_async).with(@edition.id.to_s, false).never + + post :update, + params: { + id: @edition.id, + commit: "Approve Fact check", + edition: { + activity_approve_fact_check_attributes: { + request_type: "approve_fact_check", + comment: "lgtm", + }, + }, + } + + assert_redirected_to edition_path(@edition) + @edition.reload + assert_equal @edition.state, "fact_check_received" + assert_equal flash[:danger], "You do not have correct editor permissions for this action." + end + end + end + + context "#review" do + setup do + artefact = FactoryBot.create(:artefact) + + @guide = FactoryBot.create( + :guide_edition, + state: "in_review", + review_requested_at: Time.zone.now, + panopticon_id: artefact.id, + ) + end + + should "update the reviewer" do + bob = FactoryBot.create(:user, name: "bob") + + put :review, + params: { + id: @guide.id, + edition: { reviewer: bob.name }, + } + + @guide.reload + assert_equal bob.name, @guide.reviewer + end + + should "not be able to update the reviewer when edition is scheduled for publishing" do + bob = FactoryBot.create(:user, name: "bob") + edition = FactoryBot.create(:edition, :scheduled_for_publishing) + + put :review, + params: { + id: edition.id, + edition: { reviewer: bob.name }, + } + + assert_response(:found) + assert_equal "Something went wrong when attempting to claim 2i.", flash[:danger] + end + + context "Welsh editors" do + setup do + @welsh_guide = FactoryBot.create(:guide_edition, :welsh, :in_review) + login_as_welsh_editor + @welsh_user = @user + end + + should "be able to claim a review for Welsh editions" do + put :review, + params: { + id: @welsh_guide.id, + edition: { reviewer: @welsh_user.name }, + } + + assert_redirected_to edition_path(@welsh_guide) + assert_equal "You are the reviewer of this guide.", flash[:success] + @welsh_guide.reload + assert_equal @welsh_user.name, @welsh_guide.reviewer + end + + should "not be able to claim a review for non-Welsh editions" do + put :review, + params: { + id: @guide.id, + edition: { reviewer: @welsh_user.name }, + } + + assert_redirected_to edition_path(@guide) + assert_equal "You do not have correct editor permissions for this action.", flash[:danger] + @guide.reload + assert_nil @guide.reviewer + end + end + end + + context "#destroy" do + setup do + artefact1 = FactoryBot.create( + :artefact, + slug: "test", + kind: "transaction", + name: "test", + owning_app: "publisher", + ) + @transaction = TransactionEdition.create!(title: "test", slug: "test", panopticon_id: artefact1.id) + + artefact2 = FactoryBot.create( + :artefact, + slug: "test2", + kind: "guide", + name: "test", + owning_app: "publisher", + ) + @guide = GuideEdition.create!(title: "test", slug: "test2", panopticon_id: artefact2.id) + + stub_request(:delete, "#{Plek.find('arbiter')}/slugs/test").to_return(status: 200) + end + + should "destroy transaction" do + assert @transaction.can_destroy? + assert_difference("TransactionEdition.count", -1) do + delete :destroy, params: { id: @transaction.id } + end + assert_redirected_to root_path + end + + should "can't destroy published transaction" do + @transaction.state = "ready" + stub_register_published_content + @transaction.publish + assert_not @transaction.can_destroy? + @transaction.save! + assert_difference("TransactionEdition.count", 0) do + delete :destroy, params: { id: @transaction.id } + end + end + + should "destroy guide" do + assert @guide.can_destroy? + assert_difference("GuideEdition.count", -1) do + delete :destroy, params: { id: @guide.id } + end + assert_redirected_to root_path + end + + should "can't destroy published guide" do + @guide.state = "ready" + @guide.save! + stub_register_published_content + @guide.publish + @guide.save! + assert @guide.published? + assert_not @guide.can_destroy? + + assert_difference("GuideEdition.count", 0) do + delete :destroy, params: { id: @guide.id } + end + end + + context "Welsh editors" do + setup do + login_as_welsh_editor + @welsh_guide = FactoryBot.create(:guide_edition, :welsh) + end + + should "not be able to destroy non-Welsh editions" do + assert_difference("GuideEdition.count", 0) do + delete :destroy, params: { id: @guide.id } + end + + assert_redirected_to edition_path(@guide) + assert_equal "You do not have correct editor permissions for this action.", flash[:danger] + end + + should "be able to destroy Welsh editions" do + assert_difference("GuideEdition.count", -1) do + delete :destroy, params: { id: @welsh_guide.id } + end + + assert_redirected_to root_path + assert_equal "Edition deleted", flash[:success] + end + end + end + + context "#index" do + should "editions index redirects to root" do + get :index + assert_response :redirect + assert_redirected_to root_path + end + end + + context "#show" do + setup do + artefact2 = FactoryBot.create( + :artefact, + slug: "test2", + kind: "guide", + name: "test", + owning_app: "publisher", + ) + @guide = GuideEdition.create!(title: "test", slug: "test2", panopticon_id: artefact2.id) + end + + should "requesting a publication that doesn't exist returns a 404" do + get :show, params: { id: "4e663834e2ba80480a0000e6" } + assert_response :not_found + end + + should "we can view a guide" do + get :show, params: { id: @guide.id } + assert_response :success + assert_not_nil assigns(:resource) + end + + should "render a link to the diagram when edition is a simple smart answer" do + simple_smart_answer_artefact = FactoryBot.create( + :artefact, + slug: "my-simple-smart-answer", + kind: "guide", + name: "test", + owning_app: "publisher", + ) + simple_smart_answer = SimpleSmartAnswerEdition.create!( + title: "test ssa", + panopticon_id: simple_smart_answer_artefact.id, + ) + + get :show, params: { id: simple_smart_answer.id } + + assert_select ".link-check-report p", { text: "View the flow diagram (opens in a new tab)" } do + assert_select "a[href=?]", diagram_edition_path(simple_smart_answer).to_s, + { count: 1, text: "flow diagram (opens in a new tab)" } + end + end + + should "not render a link to the diagram when edition is not a simple smart answer" do + get :show, params: { id: @guide.id } + assert_select "p", { count: 0, text: "View the flow diagram (opens in a new tab)" } + end + end + + context "#admin" do + setup do + @guide = FactoryBot.create(:guide_edition) + end + + should "show the admin page for the edition" do + get :admin, params: { id: @guide.id } + + assert_response :success + end + + context "Welsh editors" do + setup do + login_as_welsh_editor + @welsh_guide = FactoryBot.create(:guide_edition, :welsh) + end + + should "be able to see the admin page for Welsh editions" do + get :admin, params: { id: @welsh_guide.id } + + assert_response :success + end + + should "not be able to see the admin page for non-Welsh editions" do + get :admin, params: { id: @guide.id } + + assert_redirected_to edition_path(@guide) + assert_equal "You do not have correct editor permissions for this action.", flash[:danger] + end + end + end + + context "#diff" do + should "we can diff the last edition" do + first_edition = FactoryBot.create(:guide_edition, state: "published") + second_edition = first_edition.build_clone(GuideEdition) + second_edition.save! + second_edition.reload + + get :diff, params: { id: second_edition.id } + assert_response :success + end + end + + context "#unpublish" do + setup do + @guide = FactoryBot.create(:guide_edition, :published, panopticon_id: FactoryBot.create(:artefact).id) + @redirect_url = "https://www.example.com/somewhere_else" + end + + should "update publishing API upon unpublishing" do + UnpublishService.expects(:call).with(@guide.artefact, @user, @redirect_url) + + post :process_unpublish, + params: { + id: @guide.id, + redirect_url: @redirect_url, + } + end + + should "redirect and display success message after successful unpublish" do + UnpublishService.stubs(:call).with(@guide.artefact, @user, @redirect_url).returns(true) + + post :process_unpublish, + params: { + id: @guide.id, + redirect_url: @redirect_url, + } + + assert_redirected_to root_path + assert_equal "Content unpublished and redirected", flash[:notice] + end + + should "not be able to unpublish with invalid redirect url" do + post :process_unpublish, + params: { + id: @guide.id, + redirect_url: "invalid redirect url", + } + + assert_equal "Redirect path is invalid. Guide has not been unpublished.", flash[:danger] + end + + should "alert if unable to unpublish" do + UnpublishService.stubs(:call).with(@guide.artefact, @user, @redirect_url).returns(nil) + + post :process_unpublish, + params: { + id: @guide.id, + redirect_url: @redirect_url, + } + + assert_equal "Due to a service problem, the edition couldn't be unpublished", flash[:alert] + end + + context "Welsh editors" do + setup do + login_as_welsh_editor + @welsh_guide = FactoryBot.create(:guide_edition, :published, :welsh) + end + + should "not be able to access the unpublish page of non-Welsh editions" do + get :unpublish, params: { id: @guide.id } + + assert_redirected_to edition_path(@guide) + assert_equal "You do not have permission to see this page.", flash[:danger] + end + + should "not be able to access the unpublish page of Welsh editions" do + get :unpublish, params: { id: @welsh_guide.id } + + assert_redirected_to edition_path(@welsh_guide) + assert_equal "You do not have permission to see this page.", flash[:danger] + end + + should "not be allowed to unpublish a Welsh edition" do + UnpublishService.expects(:call).with(@welsh_guide.artefact, @user, @redirect_url).never + + post :process_unpublish, + params: { + id: @welsh_guide.id, + redirect_url: @redirect_url, + } + + assert_redirected_to edition_path(@welsh_guide) + @welsh_guide.reload + assert_equal @welsh_guide.state, "published" + assert_equal "You do not have permission to see this page.", flash[:danger] + end + + should "not be allowed to unpublish a non-Welsh edition" do + UnpublishService.expects(:call).with(@guide.artefact, @user, @redirect_url).never + + post :process_unpublish, + params: { + id: @guide.id, + redirect_url: @redirect_url, + } + + assert_redirected_to edition_path(@guide) + @guide.reload + assert_equal @guide.state, "published" + assert_equal "You do not have permission to see this page.", flash[:danger] + end + end + end + + context "given a simple smart answer" do + setup do + @artefact = FactoryBot.create(:artefact, slug: "foo", name: "Foo", kind: "simple_smart_answer", owning_app: "publisher") + @edition = FactoryBot.create(:simple_smart_answer_edition, body: "blah", state: "draft", slug: "foo", panopticon_id: @artefact.id) + @edition.nodes.build( + kind: "question", + slug: "question-1", + title: "Question One", + options_attributes: [ + { label: "Option One", next_node: "outcome-1" }, + { label: "Option Two", next_node: "outcome-2" }, + ], + ) + @edition.nodes.build(kind: "outcome", slug: "outcome-1", title: "Outcome One") + @edition.nodes.build(kind: "outcome", slug: "outcome-2", title: "Outcome Two") + @edition.save! + end + + should "remove an option and node from simple smart answer in single request" do + atts = { + nodes_attributes: { + "0" => { + "id" => @edition.nodes.all[0].id, + "options_attributes" => { + "0" => { "id" => @edition.nodes.first.options.all[0].id }, + "1" => { "id" => @edition.nodes.first.options.all[1].id, "_destroy" => "1" }, + }, + }, + "1" => { + "id" => @edition.nodes.all[1].id, + }, + "2" => { + "id" => @edition.nodes.all[2].id, + "_destroy" => "1", + }, + }, + } + put :update, + params: { + id: @edition.id, + edition: atts, + } + assert_redirected_to edition_path(@edition) + + @edition.reload + + assert_equal 2, @edition.nodes.count + assert_equal 1, @edition.nodes.where(kind: "question").count + assert_equal 1, @edition.nodes.where(kind: "outcome").count + + question = @edition.nodes.where(kind: "question").first + assert_equal 1, question.options.count + assert_equal "Option One", question.options.first.label + end + end + + context "#diagram" do + context "given a simple smart answer exists" do + setup do + @artefact = FactoryBot.create(:artefact, slug: "foo", name: "Foo", kind: "simple_smart_answer", owning_app: "publisher") + @edition = FactoryBot.create(:simple_smart_answer_edition, body: "blah", state: "draft", slug: "foo", panopticon_id: @artefact.id) + @edition.save! + end + + should "render a diagram page for it" do + get :diagram, params: { id: @edition.id } + + assert_response :success + assert_select "title", "Diagram for #{@edition.title} | GOV.UK Publisher" + end + end + + context "given a non-simple smart answer exists" do + setup do + @welsh_guide = FactoryBot.create(:guide_edition, :welsh, :in_review) + end + + should "return a 404" do + get :diagram, params: { id: @welsh_guide.id } + assert_response :not_found + end + end + end +end diff --git a/test/integration/edition_edit_test.rb b/test/integration/edition_edit_test.rb new file mode 100644 index 000000000..7036763c3 --- /dev/null +++ b/test/integration/edition_edit_test.rb @@ -0,0 +1,25 @@ +require "integration_test_helper" + +class EditionEditTest < IntegrationTest + setup do + setup_users + test_strategy = Flipflop::FeatureSet.current.test! + test_strategy.switch!(:design_system_edit, true) + stub_linkables + end + + should "show document summary and title" do + edition = FactoryBot.create(:guide_edition, title: "Edit page title", state: "draft") + visit edition_path(edition) + + assert page.has_title?("Edit page title") + + row = find_all(".govuk-summary-list__row") + assert row[0].has_content?("Assigned to") + assert row[1].has_text?("Content type") + assert row[1].has_text?("Guide") + assert row[2].has_text?("Edition") + assert row[2].has_text?("1") + assert row[2].has_text?("Draft") + end +end diff --git a/test/integration/edition_workflow_test.rb b/test/integration/edition_workflow_test.rb index 07ea5d30e..376303fdf 100644 --- a/test/integration/edition_workflow_test.rb +++ b/test/integration/edition_workflow_test.rb @@ -18,6 +18,7 @@ class EditionWorkflowTest < LegacyJavascriptIntegrationTest test_strategy = Flipflop::FeatureSet.current.test! test_strategy.switch!(:design_system_publications_filter, false) + test_strategy.switch!(:design_system_edit, false) end teardown do