-
Notifications
You must be signed in to change notification settings - Fork 197
Validate each document tab form separately [WHIT-3214] #11436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
893262e
3e64244
e16a0c5
ec735be
7ea6e1d
4f2a8b2
dc778e7
eb62998
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -38,8 +38,26 @@ def apply_change_type | |||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def update | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| @edition.current_tab_context = @current_tab_context | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| super | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're moving away from calling whitehall/app/controllers/admin/editions_controller.rb Lines 124 to 148 in 52e5953
Calling Also currently
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (it may be that we want to call |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| @edition.assign_attributes(edition_params) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if @current_tab_context.present? | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| tab_form = StandardEdition::TabForm.new(@edition, @current_tab_context) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if tab_form.valid? | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| @edition.save_as(current_user, validate: false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| redirect_to redirect_param(fallback: show_or_edit_path), saved_confirmation_notice | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| apply_tab_errors_to_edition(tab_form) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| build_edition_dependencies | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fetch_version_and_remark_trails | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| construct_similar_slug_warning_error | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| render :edit | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+51
to
+54
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did wonder if it's worth pulling out where this is called in EditionsController, into its own |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # non-tabbed forms in standard edition, or non-tab pages like translations | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| @edition.current_tab_context = nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| super | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def features | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -65,6 +83,15 @@ def features | |||||||||||||||||||||||||||||||||||||||||||||||||||
| render :features | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def show | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| super | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type_instance = @edition.type_instance | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| @invalid_tab_forms = type_instance.form_keys.filter_map do |tab_key| | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| tab_form = StandardEdition::TabForm.new(@edition, tab_key) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| { tab_key:, label: type_instance.form(tab_key)["label"] || tab_key.humanize } unless tab_form.valid? | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| private | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def edition_class | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -89,6 +116,11 @@ def show_or_edit_path | |||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def apply_tab_errors_to_edition(tab_form) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| @edition.errors.clear | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| tab_form.errors.each { |e| @edition.errors.add(e.attribute, e.message) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def render_not_found | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| render "admin/errors/not_found", status: :not_found | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -100,8 +100,8 @@ def pre_publication? | |
| Edition::PRE_PUBLICATION_STATES.include?(state.to_s) | ||
| end | ||
|
|
||
| def save_as(user) | ||
| if save | ||
| def save_as(user, options = {}) | ||
| if save(**options) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd expect a corresponding unit test here |
||
| edition_authors.create!(user:) | ||
| recent_edition_openings.where(editor_id: user).delete_all | ||
| end | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,65 @@ | ||||||||||||||||||
| class StandardEdition::TabForm | ||||||||||||||||||
| include ActiveModel::Model | ||||||||||||||||||
|
|
||||||||||||||||||
| attr_reader :edition, :tab_key | ||||||||||||||||||
|
|
||||||||||||||||||
| validate :validate_block_content | ||||||||||||||||||
| validate :validate_edition_fields, if: -> { edition_attribute_keys.any? } | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd move the conditional inside |
||||||||||||||||||
|
|
||||||||||||||||||
| ADDITIONAL_DEFAULT_TAB_FIELDS = %w[title summary].freeze | ||||||||||||||||||
|
|
||||||||||||||||||
| def initialize(edition, tab_key) | ||||||||||||||||||
| @edition = edition | ||||||||||||||||||
| @tab_key = tab_key | ||||||||||||||||||
| end | ||||||||||||||||||
|
|
||||||||||||||||||
| private | ||||||||||||||||||
|
|
||||||||||||||||||
| def form_config | ||||||||||||||||||
| @form_config ||= edition.type_instance.form(tab_key).tap do |config| | ||||||||||||||||||
| raise ArgumentError, "Unknown tab key '#{tab_key}'" unless config | ||||||||||||||||||
| end | ||||||||||||||||||
| end | ||||||||||||||||||
|
|
||||||||||||||||||
| # Fields whose data lives inside block_content (e.g. "body", "social_media_links") | ||||||||||||||||||
| def block_content_field_keys | ||||||||||||||||||
| (form_config["fields"] || {}).filter_map do |_key, field| | ||||||||||||||||||
| attr_path = Array(field["attribute_path"]) | ||||||||||||||||||
| attr_path.last if attr_path.first == "block_content" | ||||||||||||||||||
| end | ||||||||||||||||||
| end | ||||||||||||||||||
|
|
||||||||||||||||||
| # Fields whose data lives directly on the edition (e.g. "lead_organisation_ids") | ||||||||||||||||||
| def edition_attribute_keys | ||||||||||||||||||
| keys = (form_config["fields"] || {}).filter_map do |_key, field| | ||||||||||||||||||
| attr_path = Array(field["attribute_path"]) | ||||||||||||||||||
| attr_path.first unless attr_path.first == "block_content" | ||||||||||||||||||
| end | ||||||||||||||||||
|
|
||||||||||||||||||
| tab_key == edition.default_tab ? keys + ADDITIONAL_DEFAULT_TAB_FIELDS : keys # Need to move these fields into document form configuration | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can drop the const as it's only used here.
Suggested change
|
||||||||||||||||||
| end | ||||||||||||||||||
|
|
||||||||||||||||||
| def scoped_block_content | ||||||||||||||||||
| schema = edition.type_instance.schema_for_fields(block_content_field_keys) | ||||||||||||||||||
| StandardEdition::BlockContent.new(schema).tap do |bc| | ||||||||||||||||||
| bc.assign_attributes(edition[:block_content] || {}) | ||||||||||||||||||
| end | ||||||||||||||||||
| end | ||||||||||||||||||
|
|
||||||||||||||||||
| def validate_block_content | ||||||||||||||||||
| block_content = scoped_block_content | ||||||||||||||||||
| return if block_content.valid?(validation_context) | ||||||||||||||||||
|
|
||||||||||||||||||
| block_content.errors.each { |error| errors.import(error, attribute: error.attribute.to_s) } | ||||||||||||||||||
| end | ||||||||||||||||||
|
|
||||||||||||||||||
| def validate_edition_fields | ||||||||||||||||||
| edition.current_tab_context = tab_key | ||||||||||||||||||
| edition.valid?(validation_context) | ||||||||||||||||||
| edition.current_tab_context = nil | ||||||||||||||||||
|
|
||||||||||||||||||
| edition_attribute_keys.each do |attr| | ||||||||||||||||||
| edition.errors.where(attr.to_sym).each { |error| errors.import(error, attribute: error.attribute.to_s) } | ||||||||||||||||||
| end | ||||||||||||||||||
| end | ||||||||||||||||||
| end | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,6 +103,10 @@ def form(key = nil) | |
| end | ||
| end | ||
|
|
||
| def form_keys | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add corresponding unit test please |
||
| @forms.keys | ||
| end | ||
|
|
||
| def presenter(key) | ||
| @presenters[key] | ||
| end | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -115,15 +115,18 @@ def group | |||||
| end | ||||||
|
|
||||||
| def organisation_association_enabled? | ||||||
| field_paths.include?(ConfigurableContentBlocks::Path.new("lead_organisation_ids")) | ||||||
| current_tab_context_includes_field?("lead_organisation_ids") && | ||||||
| field_paths.include?(ConfigurableContentBlocks::Path.new("lead_organisation_ids")) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit concerned this is in the wrong place in the stack. We want to be able to ask "for this type of StandardEdition, are organisations enabled?". That's a business logic / model decision. Whether or not to enforce validation on that association is more of a controller-level one, I'd say. Making the changes here risks introducing bugs in, say, the Edition Access Limited controller's I think really this sort of branching should move to: whitehall/app/models/concerns/edition/organisations.rb Lines 16 to 17 in 5521aee
Something like: |
||||||
| end | ||||||
|
|
||||||
| def worldwide_organisation_association_required? | ||||||
| required_field_paths.include?(ConfigurableContentBlocks::Path.new("worldwide_organisation_document_ids")) | ||||||
| current_tab_context_includes_field?("worldwide_organisation_document_ids") && | ||||||
| required_field_paths.include?(ConfigurableContentBlocks::Path.new("worldwide_organisation_document_ids")) | ||||||
| end | ||||||
|
|
||||||
| def world_location_association_required? | ||||||
| required_field_paths.include?(ConfigurableContentBlocks::Path.new("world_location_ids")) | ||||||
| current_tab_context_includes_field?("world_location_ids") && | ||||||
| required_field_paths.include?(ConfigurableContentBlocks::Path.new("world_location_ids")) | ||||||
| end | ||||||
|
|
||||||
| def is_in_valid_state_for_type_conversion? | ||||||
|
|
@@ -187,4 +190,15 @@ def required_field_paths | |||||
| def string_for_slug | ||||||
| title if primary_locale.to_sym == translation.locale | ||||||
| end | ||||||
|
|
||||||
| def current_tab_context_includes_field?(attribute_name) | ||||||
| return true if current_tab_context.blank? # assume we are checking against the entire edition | ||||||
|
|
||||||
| form = type_instance.form(current_tab_context) | ||||||
| return true if form.nil? | ||||||
|
|
||||||
| (form["fields"] || {}).any? do |_key, field| | ||||||
| Array(field["attribute_path"]).include?(attribute_name) | ||||||
| end | ||||||
| end | ||||||
| end | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,8 +34,7 @@ | |
| </section> | ||
|
|
||
| <%= render partial: "admin/editions/show/main_notices", locals: { edition: @edition } %> | ||
|
|
||
| <%= render Admin::Editions::Show::PreviewComponent.new(edition: @edition) %> | ||
| <%= render Admin::Editions::Show::PreviewComponent.new(edition: @edition, invalid_tab_forms: @invalid_tab_forms) %> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm this extra param is being passed, but doesn't appear to be being used anywhere? |
||
|
|
||
| <% if @edition.change_note_required? %> | ||
| <section class="app-view-summary__section"> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd move this above the preview link (above line 11) rather than having it sit between the main and the translated preview links.
I'd also get Sally to suggest a line of content. 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And a view test to ensure this message appears when a tab is invalid. (So probably worth pulling this whole thing into its own commit)