From 728dfbbf55205cda6763a8d80ab0f9bafdb2880c Mon Sep 17 00:00:00 2001 From: berliner Date: Wed, 17 Apr 2024 16:32:26 +0200 Subject: [PATCH 01/17] HPC-9466: Ground work for replacing the botton_figure_row paragraph type with 2 new types: top_figures and top_figures_small --- composer.json | 3 +- composer.lock | 97 +- ...ay.paragraph.bottom_figure_row.default.yml | 2 - ..._display.paragraph.top_figures.default.yml | 37 + ...ay.paragraph.top_figures_small.default.yml | 37 + ...ay.paragraph.bottom_figure_row.default.yml | 4 +- ...aragraph.bottom_figure_row.top_figures.yml | 2 - ..._display.paragraph.top_figures.default.yml | 23 + ...ay.paragraph.top_figures_small.default.yml | 23 + config/core.extension.yml | 1 + ...ld.field.node.article.field_paragraphs.yml | 57 +- ...bottom_figure_row.field_bottom_figures.yml | 39 - ...ld.paragraph.top_figures.field_figures.yml | 78 ++ ...agraph.top_figures_small.field_figures.yml | 78 ++ ...storage.paragraph.field_bottom_figures.yml | 32 - ...aphs.paragraphs_type.bottom_figure_row.yml | 6 +- ...paragraphs.paragraphs_type.top_figures.yml | 14 + ...aphs.paragraphs_type.top_figures_small.yml | 14 + .../custom/gho_fields/gho_fields.deploy.php | 111 ++ html/modules/custom/gho_figures/README.md | 8 - html/modules/custom/gho_figures/composer.json | 20 - .../custom/gho_figures/gho_figures.info.yml | 5 - .../gho_figures/gho_figures.links.action.yml | 5 - .../custom/gho_figures/gho_figures.module | 6 - .../gho_figures/gho_figures.permissions.yml | 3 - .../gho_figures/gho_figures.routing.yml | 7 - ...sImportNeedsAndRequirementsFiguresForm.php | 1200 ----------------- .../config/install/gho_figures.settings.yml | 4 - .../common_design_subtheme.libraries.yml | 5 + .../components/gho-top-figures/README.md | 4 + .../gho-top-figures/gho-top-figures.css | 66 + .../paragraph--top-figures-small.html.twig | 59 + ....twig => paragraph--top-figures.html.twig} | 2 +- 33 files changed, 675 insertions(+), 1377 deletions(-) create mode 100644 config/core.entity_form_display.paragraph.top_figures.default.yml create mode 100644 config/core.entity_form_display.paragraph.top_figures_small.default.yml create mode 100644 config/core.entity_view_display.paragraph.top_figures.default.yml create mode 100644 config/core.entity_view_display.paragraph.top_figures_small.default.yml delete mode 100755 config/field.field.paragraph.bottom_figure_row.field_bottom_figures.yml create mode 100644 config/field.field.paragraph.top_figures.field_figures.yml create mode 100644 config/field.field.paragraph.top_figures_small.field_figures.yml delete mode 100755 config/field.storage.paragraph.field_bottom_figures.yml create mode 100644 config/paragraphs.paragraphs_type.top_figures.yml create mode 100644 config/paragraphs.paragraphs_type.top_figures_small.yml delete mode 100644 html/modules/custom/gho_figures/README.md delete mode 100644 html/modules/custom/gho_figures/composer.json delete mode 100644 html/modules/custom/gho_figures/gho_figures.info.yml delete mode 100644 html/modules/custom/gho_figures/gho_figures.links.action.yml delete mode 100644 html/modules/custom/gho_figures/gho_figures.module delete mode 100644 html/modules/custom/gho_figures/gho_figures.permissions.yml delete mode 100644 html/modules/custom/gho_figures/gho_figures.routing.yml delete mode 100644 html/modules/custom/gho_figures/src/Form/GhoFiguresImportNeedsAndRequirementsFiguresForm.php delete mode 100644 html/modules/custom/gho_figures/src/config/install/gho_figures.settings.yml create mode 100644 html/themes/custom/common_design_subtheme/components/gho-top-figures/README.md create mode 100644 html/themes/custom/common_design_subtheme/components/gho-top-figures/gho-top-figures.css create mode 100644 html/themes/custom/common_design_subtheme/templates/paragraphs/paragraph--top-figures-small.html.twig rename html/themes/custom/common_design_subtheme/templates/paragraphs/{paragraph--needs-and-requirements.html.twig => paragraph--top-figures.html.twig} (97%) diff --git a/composer.json b/composer.json index b5eccad8..a7ace90f 100644 --- a/composer.json +++ b/composer.json @@ -58,13 +58,14 @@ "drupal/core-recommended": "^10", "drupal/csp": "^1.16", "drupal/ctools": "^4.0", - "drupal/custom_field": "^2.0@RC", + "drupal/custom_field": "^3", "drupal/diff": "^1.1", "drupal/double_field": "^4.0", "drupal/editor_advanced_link": "^2.0", "drupal/entity_browser": "^2.9", "drupal/entity_browser_table": "^1.4", "drupal/entity_usage": "^2.0@beta", + "drupal/field_config_cardinality": "^3.0", "drupal/field_group": "^3.4", "drupal/gin": "^3.0@RC", "drupal/google_tag": "^1.4", diff --git a/composer.lock b/composer.lock index bdefa7d0..61ac32fd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "12569a8c6834a7ab5add06011793900c", + "content-hash": "4f27d49a18c45b36b1b0efc31701439b", "packages": [ { "name": "asm89/stack-cors", @@ -3512,30 +3512,30 @@ }, { "name": "drupal/custom_field", - "version": "2.0.0-rc6", + "version": "3.0.0-rc1", "source": { "type": "git", "url": "https://git.drupalcode.org/project/custom_field.git", - "reference": "2.0.0-rc6" + "reference": "3.0.0-rc1" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/custom_field-2.0.0-rc6.zip", - "reference": "2.0.0-rc6", - "shasum": "53db8135daee8c9273006de3ca208ba9de4a74f0" + "url": "https://ftp.drupal.org/files/projects/custom_field-3.0.0-rc1.zip", + "reference": "3.0.0-rc1", + "shasum": "3aec02cf915a3177eb6d107f4eaebd1a4840282e" }, "require": { - "drupal/core": "^9.4 || ^10.1" + "drupal/core": "^10.2" }, "require-dev": { "drupal/feeds": "3.x-dev", - "drupal/linkit": "^6" + "drupal/linkit": "^6.1" }, "type": "drupal-module", "extra": { "drupal": { - "version": "2.0.0-rc6", - "datestamp": "1703275144", + "version": "3.0.0-rc1", + "datestamp": "1712345809", "security-coverage": { "status": "not-covered", "message": "RC releases are not covered by Drupal security advisories." @@ -4046,6 +4046,82 @@ "issues": "http://drupal.org/project/issues/entity_usage" } }, + { + "name": "drupal/field_config_cardinality", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/field_config_cardinality.git", + "reference": "3.0.1" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/field_config_cardinality-3.0.1.zip", + "reference": "3.0.1", + "shasum": "47a2d933514464bea620617eb2ec34988b60ce37" + }, + "require": { + "drupal/core": "^9 || ^10" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "3.0.1", + "datestamp": "1680866025", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "ayalon", + "homepage": "https://www.drupal.org/user/419226" + }, + { + "name": "brittany.huntzberry", + "homepage": "https://www.drupal.org/user/3552960" + }, + { + "name": "dealancer", + "homepage": "https://www.drupal.org/user/243418" + }, + { + "name": "devkinetic", + "homepage": "https://www.drupal.org/user/114708" + }, + { + "name": "e.bogatyrev", + "homepage": "https://www.drupal.org/user/2760527" + }, + { + "name": "eloivaque", + "homepage": "https://www.drupal.org/user/605038" + }, + { + "name": "Naveen Kumar Reddy", + "homepage": "https://www.drupal.org/user/3693535" + }, + { + "name": "Shruthi Shetty M", + "homepage": "https://www.drupal.org/user/3580970" + }, + { + "name": "stefan.korn", + "homepage": "https://www.drupal.org/user/1942204" + } + ], + "description": "Allow apply cardinality on Field Config (Cardinality Instance)", + "homepage": "https://www.drupal.org/project/field_config_cardinality", + "support": { + "source": "https://git.drupalcode.org/project/field_config_cardinality" + } + }, { "name": "drupal/field_group", "version": "3.4.0", @@ -16651,7 +16727,6 @@ "stability-flags": { "drupal/bigint": 20, "drupal/bulk_update_fields": 15, - "drupal/custom_field": 5, "drupal/entity_usage": 10, "drupal/gin": 5, "drupal/imageapi_optimize_binaries": 15, diff --git a/config/core.entity_form_display.paragraph.bottom_figure_row.default.yml b/config/core.entity_form_display.paragraph.bottom_figure_row.default.yml index 76ac6b89..4bf1c113 100755 --- a/config/core.entity_form_display.paragraph.bottom_figure_row.default.yml +++ b/config/core.entity_form_display.paragraph.bottom_figure_row.default.yml @@ -3,7 +3,6 @@ langcode: en status: true dependencies: config: - - field.field.paragraph.bottom_figure_row.field_bottom_figures - field.field.paragraph.bottom_figure_row.field_dataset - field.field.paragraph.bottom_figure_row.field_figures - paragraphs.paragraphs_type.bottom_figure_row @@ -94,5 +93,4 @@ content: third_party_settings: { } hidden: created: true - field_bottom_figures: true translation: true diff --git a/config/core.entity_form_display.paragraph.top_figures.default.yml b/config/core.entity_form_display.paragraph.top_figures.default.yml new file mode 100644 index 00000000..312acdf2 --- /dev/null +++ b/config/core.entity_form_display.paragraph.top_figures.default.yml @@ -0,0 +1,37 @@ +uuid: 382ed4e3-c4ba-4ba5-8d7f-12572773dbba +langcode: en +status: true +dependencies: + config: + - field.field.paragraph.top_figures.field_figures + - paragraphs.paragraphs_type.top_figures + module: + - custom_field +id: paragraph.top_figures.default +targetEntityType: paragraph +bundle: top_figures +mode: default +content: + field_figures: + type: custom_flex + weight: 0 + region: content + settings: + label: false + wrapper: div + open: true + columns: + label: auto + value: auto + footnote: auto + breakpoint: '' + third_party_settings: { } + status: + type: boolean_checkbox + weight: 1 + region: content + settings: + display_label: true + third_party_settings: { } +hidden: + created: true diff --git a/config/core.entity_form_display.paragraph.top_figures_small.default.yml b/config/core.entity_form_display.paragraph.top_figures_small.default.yml new file mode 100644 index 00000000..7ca4fa33 --- /dev/null +++ b/config/core.entity_form_display.paragraph.top_figures_small.default.yml @@ -0,0 +1,37 @@ +uuid: dd76a23b-79b9-4342-82df-f28128fb1780 +langcode: en +status: true +dependencies: + config: + - field.field.paragraph.top_figures_small.field_figures + - paragraphs.paragraphs_type.top_figures_small + module: + - custom_field +id: paragraph.top_figures_small.default +targetEntityType: paragraph +bundle: top_figures_small +mode: default +content: + field_figures: + type: custom_flex + weight: 0 + region: content + settings: + label: false + wrapper: div + open: true + columns: + label: auto + value: auto + footnote: auto + breakpoint: '' + third_party_settings: { } + status: + type: boolean_checkbox + weight: 1 + region: content + settings: + display_label: true + third_party_settings: { } +hidden: + created: true diff --git a/config/core.entity_view_display.paragraph.bottom_figure_row.default.yml b/config/core.entity_view_display.paragraph.bottom_figure_row.default.yml index 14a618f9..907cdc4d 100755 --- a/config/core.entity_view_display.paragraph.bottom_figure_row.default.yml +++ b/config/core.entity_view_display.paragraph.bottom_figure_row.default.yml @@ -3,7 +3,6 @@ langcode: en status: true dependencies: config: - - field.field.paragraph.bottom_figure_row.field_bottom_figures - field.field.paragraph.bottom_figure_row.field_dataset - field.field.paragraph.bottom_figure_row.field_figures - paragraphs.paragraphs_type.bottom_figure_row @@ -29,5 +28,4 @@ content: third_party_settings: { } weight: 0 region: content -hidden: - field_bottom_figures: true +hidden: { } diff --git a/config/core.entity_view_display.paragraph.bottom_figure_row.top_figures.yml b/config/core.entity_view_display.paragraph.bottom_figure_row.top_figures.yml index 5bb705e9..6465e2fb 100755 --- a/config/core.entity_view_display.paragraph.bottom_figure_row.top_figures.yml +++ b/config/core.entity_view_display.paragraph.bottom_figure_row.top_figures.yml @@ -4,7 +4,6 @@ status: true dependencies: config: - core.entity_view_mode.paragraph.top_figures - - field.field.paragraph.bottom_figure_row.field_bottom_figures - field.field.paragraph.bottom_figure_row.field_dataset - field.field.paragraph.bottom_figure_row.field_figures - paragraphs.paragraphs_type.bottom_figure_row @@ -29,5 +28,4 @@ content: weight: 0 region: content hidden: - field_bottom_figures: true field_dataset: true diff --git a/config/core.entity_view_display.paragraph.top_figures.default.yml b/config/core.entity_view_display.paragraph.top_figures.default.yml new file mode 100644 index 00000000..baeec5b2 --- /dev/null +++ b/config/core.entity_view_display.paragraph.top_figures.default.yml @@ -0,0 +1,23 @@ +uuid: 9aacccd4-0a89-4c5f-8a45-f9f19e47cd49 +langcode: en +status: true +dependencies: + config: + - field.field.paragraph.top_figures.field_figures + - paragraphs.paragraphs_type.top_figures + module: + - gho_fields +id: paragraph.top_figures.default +targetEntityType: paragraph +bundle: top_figures +mode: default +content: + field_figures: + type: gho_figures + label: hidden + settings: + format: large + third_party_settings: { } + weight: 0 + region: content +hidden: { } diff --git a/config/core.entity_view_display.paragraph.top_figures_small.default.yml b/config/core.entity_view_display.paragraph.top_figures_small.default.yml new file mode 100644 index 00000000..d15197b2 --- /dev/null +++ b/config/core.entity_view_display.paragraph.top_figures_small.default.yml @@ -0,0 +1,23 @@ +uuid: 21c9311d-5370-495e-bf19-2a75a324e4f2 +langcode: en +status: true +dependencies: + config: + - field.field.paragraph.top_figures_small.field_figures + - paragraphs.paragraphs_type.top_figures_small + module: + - gho_fields +id: paragraph.top_figures_small.default +targetEntityType: paragraph +bundle: top_figures_small +mode: default +content: + field_figures: + type: gho_figures + label: hidden + settings: + format: small + third_party_settings: { } + weight: 0 + region: content +hidden: { } diff --git a/config/core.extension.yml b/config/core.extension.yml index ec08109c..92f238f3 100755 --- a/config/core.extension.yml +++ b/config/core.extension.yml @@ -33,6 +33,7 @@ module: entity_browser_table: 0 entity_reference_revisions: 0 field: 0 + field_config_cardinality: 0 field_group: 0 field_ui: 0 file: 0 diff --git a/config/field.field.node.article.field_paragraphs.yml b/config/field.field.node.article.field_paragraphs.yml index 5ac43a0d..c12a1457 100755 --- a/config/field.field.node.article.field_paragraphs.yml +++ b/config/field.field.node.article.field_paragraphs.yml @@ -9,6 +9,14 @@ dependencies: - paragraphs.paragraphs_type.article_card_list module: - entity_reference_revisions + - field_config_cardinality +third_party_settings: + field_config_cardinality: + cardinality_config: '-1' + cardinality_label_config: false + unlimited_not_required: '' + limited_not_required: '' + limited_required: '' id: node.article.field_paragraphs field_name: field_paragraphs entity_type: node @@ -28,66 +36,69 @@ settings: negate: 1 target_bundles_drag_drop: achievement: - weight: 22 + weight: -35 enabled: true achievement_list: - weight: 21 + weight: -36 enabled: false article_card_list: - weight: 23 + weight: -34 enabled: true article_list: - weight: 24 + weight: -33 enabled: false bottom_figure_row: - weight: 8 + weight: -43 enabled: false document_articles: - weight: 28 + weight: -31 enabled: false document_chapter: - weight: 29 + weight: -29 enabled: false download_button: - weight: 26 + weight: -32 enabled: false facts_and_figures: - weight: 9 + weight: -42 enabled: false further_reading: - weight: 28 + weight: -30 enabled: false heading: - weight: 29 + weight: -28 enabled: false image_with_text: - weight: 12 + weight: -38 enabled: false infographic: - weight: 36 + weight: -26 enabled: false interactive_content: - weight: 31 + weight: -27 enabled: false interactive_content_2_columns: - weight: 39 + weight: -25 enabled: false - layout: - weight: 8 - enabled: true photo_gallery: - weight: 11 + weight: -39 enabled: false separator: - weight: 15 + weight: -37 enabled: false story: - weight: 10 + weight: -41 enabled: false sub_article: - weight: 39 + weight: -24 enabled: false text: - weight: 10 + weight: -40 + enabled: false + top_figures: + weight: -45 + enabled: false + top_figures_small: + weight: -44 enabled: false field_type: entity_reference_revisions diff --git a/config/field.field.paragraph.bottom_figure_row.field_bottom_figures.yml b/config/field.field.paragraph.bottom_figure_row.field_bottom_figures.yml deleted file mode 100755 index 041a3a1e..00000000 --- a/config/field.field.paragraph.bottom_figure_row.field_bottom_figures.yml +++ /dev/null @@ -1,39 +0,0 @@ -uuid: 25815140-0e56-4211-9be6-f89af6c289a8 -langcode: en -status: true -dependencies: - config: - - field.storage.paragraph.field_bottom_figures - - paragraphs.paragraphs_type.bottom_figure_row - module: - - double_field -id: paragraph.bottom_figure_row.field_bottom_figures -field_name: field_bottom_figures -entity_type: paragraph -bundle: bottom_figure_row -label: 'Figures (Deprecated)' -description: '' -required: false -translatable: true -default_value: { } -default_value_callback: '' -settings: - first: - label: Label - list: false - allowed_values: { } - max: null - min: null - required: true - on_label: 'On' - off_label: 'Off' - second: - label: Value - list: false - allowed_values: { } - max: null - min: null - required: true - on_label: 'On' - off_label: 'Off' -field_type: double_field diff --git a/config/field.field.paragraph.top_figures.field_figures.yml b/config/field.field.paragraph.top_figures.field_figures.yml new file mode 100644 index 00000000..cc8a9ea4 --- /dev/null +++ b/config/field.field.paragraph.top_figures.field_figures.yml @@ -0,0 +1,78 @@ +uuid: ef933458-7a8c-4ae0-8ff9-8060b5c446de +langcode: en +status: true +dependencies: + config: + - field.storage.paragraph.field_figures + - paragraphs.paragraphs_type.top_figures + module: + - custom_field + - field_config_cardinality +third_party_settings: + field_config_cardinality: + cardinality_config: '3' + cardinality_label_config: false + unlimited_not_required: '' + limited_not_required: '' + limited_required: '' +id: paragraph.top_figures.field_figures +field_name: field_figures +entity_type: paragraph +bundle: top_figures +label: 'Top figures' +description: 'You can add up to 3 top figures to this paragraph' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + field_settings: + label: + type: text + weight: 0 + check_empty: false + widget_settings: + label: Label + settings: + description: '' + description_display: after + size: 30 + placeholder: 'Ex: Population' + maxlength: 255 + maxlength_js: false + prefix: '' + suffix: '' + required: false + value: + type: text + weight: 0 + check_empty: false + widget_settings: + label: Value + settings: + description: '' + description_display: after + size: 30 + placeholder: 'Ex: 10.4 million' + maxlength: 255 + maxlength_js: false + prefix: '' + suffix: '' + required: false + footnote: + type: text + weight: 0 + check_empty: false + widget_settings: + label: Footnote + settings: + description: '' + description_display: after + size: 30 + placeholder: '' + maxlength: 255 + maxlength_js: false + prefix: '' + suffix: '' + required: false +field_type: custom diff --git a/config/field.field.paragraph.top_figures_small.field_figures.yml b/config/field.field.paragraph.top_figures_small.field_figures.yml new file mode 100644 index 00000000..57147e84 --- /dev/null +++ b/config/field.field.paragraph.top_figures_small.field_figures.yml @@ -0,0 +1,78 @@ +uuid: 94a317bf-feba-4285-b5c5-a43281ab9bcb +langcode: en +status: true +dependencies: + config: + - field.storage.paragraph.field_figures + - paragraphs.paragraphs_type.top_figures_small + module: + - custom_field + - field_config_cardinality +third_party_settings: + field_config_cardinality: + cardinality_config: '6' + cardinality_label_config: false + unlimited_not_required: '' + limited_not_required: '' + limited_required: '' +id: paragraph.top_figures_small.field_figures +field_name: field_figures +entity_type: paragraph +bundle: top_figures_small +label: Figures +description: 'You can add up to 6 figures to this paragraph' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + field_settings: + label: + type: text + weight: 0 + check_empty: false + widget_settings: + label: Label + settings: + description: '' + description_display: after + size: 30 + placeholder: 'Ex: Population' + maxlength: 255 + maxlength_js: false + prefix: '' + suffix: '' + required: false + value: + type: text + weight: 0 + check_empty: false + widget_settings: + label: Value + settings: + description: '' + description_display: after + size: 30 + placeholder: 'Ex: 10.4 million' + maxlength: 255 + maxlength_js: false + prefix: '' + suffix: '' + required: false + footnote: + type: text + weight: 0 + check_empty: false + widget_settings: + label: Footnote + settings: + description: '' + description_display: after + size: 30 + placeholder: '' + maxlength: 255 + maxlength_js: false + prefix: '' + suffix: '' + required: false +field_type: custom diff --git a/config/field.storage.paragraph.field_bottom_figures.yml b/config/field.storage.paragraph.field_bottom_figures.yml deleted file mode 100755 index 8b1628a1..00000000 --- a/config/field.storage.paragraph.field_bottom_figures.yml +++ /dev/null @@ -1,32 +0,0 @@ -uuid: b3bc588b-4076-4280-ab42-663b687b7571 -langcode: en -status: true -dependencies: - module: - - double_field - - paragraphs -id: paragraph.field_bottom_figures -field_name: field_bottom_figures -entity_type: paragraph -type: double_field -settings: - storage: - first: - type: string - maxlength: 255 - precision: 10 - scale: 2 - datetime_type: datetime - second: - type: string - maxlength: 255 - precision: 10 - scale: 2 - datetime_type: datetime -module: double_field -locked: false -cardinality: -1 -translatable: true -indexes: { } -persist_with_no_fields: false -custom_storage: false diff --git a/config/paragraphs.paragraphs_type.bottom_figure_row.yml b/config/paragraphs.paragraphs_type.bottom_figure_row.yml index facb9f2d..34ee6cf8 100755 --- a/config/paragraphs.paragraphs_type.bottom_figure_row.yml +++ b/config/paragraphs.paragraphs_type.bottom_figure_row.yml @@ -3,18 +3,14 @@ langcode: en status: true dependencies: module: - - layout_paragraphs - ncms_publisher - paragraphs_viewmode id: bottom_figure_row -label: 'Bottom figure row' +label: 'Bottom figure row (deprecated)' icon_uuid: null icon_default: null description: 'A row of textual figures with custom labels.' behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } paragraphs_viewmode_behavior: enabled: true override_mode: default diff --git a/config/paragraphs.paragraphs_type.top_figures.yml b/config/paragraphs.paragraphs_type.top_figures.yml new file mode 100644 index 00000000..52ecda8d --- /dev/null +++ b/config/paragraphs.paragraphs_type.top_figures.yml @@ -0,0 +1,14 @@ +uuid: 42ca3952-bcb5-40ed-89ec-39ff9ef0a4f0 +langcode: en +status: true +dependencies: + module: + - ncms_publisher +id: top_figures +label: 'Top figures' +icon_uuid: null +icon_default: null +description: 'A set of label/value pairs to show top figures' +behavior_plugins: + promoted_behavior: + enabled: true diff --git a/config/paragraphs.paragraphs_type.top_figures_small.yml b/config/paragraphs.paragraphs_type.top_figures_small.yml new file mode 100644 index 00000000..0a893aa5 --- /dev/null +++ b/config/paragraphs.paragraphs_type.top_figures_small.yml @@ -0,0 +1,14 @@ +uuid: 00352a35-f75b-43bb-aa8a-8dfb622949dc +langcode: en +status: true +dependencies: + module: + - ncms_publisher +id: top_figures_small +label: 'Top figures (small)' +icon_uuid: null +icon_default: null +description: 'A set of label/value pairs to show top figures in a less prominent way' +behavior_plugins: + promoted_behavior: + enabled: true diff --git a/html/modules/custom/gho_fields/gho_fields.deploy.php b/html/modules/custom/gho_fields/gho_fields.deploy.php index 0403c990..36700bc7 100644 --- a/html/modules/custom/gho_fields/gho_fields.deploy.php +++ b/html/modules/custom/gho_fields/gho_fields.deploy.php @@ -5,6 +5,8 @@ * Post update functions for GHO Fields. */ +use Drupal\paragraphs\ParagraphInterface; + /** * Copy data from old fields to new fields. */ @@ -79,3 +81,112 @@ function gho_fields_deploy_migrate_bottom_figure_rows() { } } } + +/** + * Migrate bottom figure row paragraph types to the new paragraph types. + */ +function gho_fields_deploy_migrate_bottom_figure_rows_paragraphs_to_top_figures() { + $entity_type_manager = \Drupal::entityTypeManager(); + $storage = $entity_type_manager->getStorage('paragraph'); + + /** @var \Drupal\paragraphs\ParagraphInterface[] $paragraphs */ + $paragraphs = $storage->loadByProperties([ + 'type' => ['bottom_figure_row'], + ]); + foreach ($paragraphs as $paragraph) { + if (!$paragraph->hasField('field_figures') || $paragraph->get('field_figures')->isEmpty()) { + $paragraph->delete(); + continue; + } + + $view_mode = $paragraph->getBehaviorSetting('paragraphs_viewmode_behavior', 'view_mode'); + $target_bundle = $view_mode == 'top_figures' ? 'top_figures' : 'top_figures_small'; + + $field_config = $entity_type_manager + ->getStorage('field_config') + ->load($paragraph->getEntityTypeId() . '.' . $target_bundle . '.field_figures'); + + $figures = $paragraph->get('field_figures'); + $max_figures = $field_config->getThirdPartySetting('field_config_cardinality', 'cardinality_config'); + if ($figures->count() > $max_figures) { + continue; + } + + foreach ($paragraph->getTranslationLanguages() as $language) { + if ($language->isDefault()) { + continue; + } + if (!$paragraph->hasTranslation($language->getId())) { + continue; + } + $translated_paragraph = \Drupal::service('entity.repository')->getTranslationFromContext($paragraph, $language->getId()); + $figures = $translated_paragraph->get('field_figures'); + if (!$figures || $figures->isEmpty()) { + $translated_paragraph->delete(); + continue; + } + + if ($figures->count() > $max_figures) { + continue; + } + + /** @var \Drupal\paragraphs\ParagraphInterface $top_figures_paragraph */ + $top_figures_paragraph = ghi_fields_create_top_figures_from_paragraph($translated_paragraph, $target_bundle); + if ($top_figures_paragraph) { + $translated_paragraph->delete(); + } + } + + $top_figures_paragraph = ghi_fields_create_top_figures_from_paragraph($paragraph, $target_bundle); + if ($top_figures_paragraph) { + $paragraph->delete(); + } + } +} + +/** + * Create a top figures paragraph from a bottom_figure_row paragraph. + * + * @param \Drupal\paragraphs\ParagraphInterface $paragraph + * The original bottom_figure_row paragraph. + * @param string $bundle + * The new paragraph type, either top_figures or top_figures_small. + * + * @return \Drupal\paragraphs\ParagraphInterface + * The newly created paragraph. + */ +function ghi_fields_create_top_figures_from_paragraph(ParagraphInterface $paragraph, $bundle) { + if ($paragraph->bundle() != 'bottom_figure_row') { + return NULL; + } + $entity_type_manager = \Drupal::entityTypeManager(); + $storage = $entity_type_manager->getStorage('paragraph'); + /** @var \Drupal\paragraphs\ParagraphInterface $top_figures_paragraph */ + $top_figures_paragraph = $storage->create([ + 'type' => $bundle, + 'status' => $paragraph->isPublished(), + 'parent_id' => $paragraph->get('parent_id')->value, + 'parent_type' => $paragraph->get('parent_type')->value, + 'parent_field_name' => $paragraph->get('parent_field_name')->value, + 'field_figures' => $paragraph->get('field_figures')->getValue(), + ]); + $top_figures_paragraph->setBehaviorSettings('promoted_behavior', $paragraph->getAllBehaviorSettings()['promoted_behavior'] ?? []); + $top_figures_paragraph->setSyncing(TRUE); + $top_figures_paragraph->save(); + + $parent_field_name = $paragraph->get('parent_field_name')->value; + $parent = $paragraph->getParentEntity(); + $values = $parent->get($parent_field_name)->getValue(); + + foreach ($values as &$item) { + if ($item['target_id'] != $paragraph->id()) { + continue; + } + $item['target_id'] = $top_figures_paragraph->id(); + $item['target_revision_id'] = $top_figures_paragraph->getRevisionId(); + } + $parent->get($parent_field_name)->setValue($values); + $parent->save(); + + return $top_figures_paragraph; +} diff --git a/html/modules/custom/gho_figures/README.md b/html/modules/custom/gho_figures/README.md deleted file mode 100644 index cb4618bc..00000000 --- a/html/modules/custom/gho_figures/README.md +++ /dev/null @@ -1,8 +0,0 @@ -HPC Content Module - Figures Module -============================================= - -This module provides a form (/admin/content/needs-and-requirements/import) to -import a spreadsheet with the "needs and requirements" figures (also known as -"top figures"). - -This requires the "Needs and requirements" vocabulary. diff --git a/html/modules/custom/gho_figures/composer.json b/html/modules/custom/gho_figures/composer.json deleted file mode 100644 index eba9bf6b..00000000 --- a/html/modules/custom/gho_figures/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "unocha/gho_figures", - "description": "HPC Content Module Figures Import.", - "type": "drupal-module", - "authors": [ - { - "name": "UNOCHA" - } - ], - "keywords": [ - "Drupal", - "Spreadsheet", - "Excel" - ], - "require": { - "phpoffice/phpspreadsheet": "^1.14.1" - }, - "minimum-stability": "dev", - "prefer-stable": true -} diff --git a/html/modules/custom/gho_figures/gho_figures.info.yml b/html/modules/custom/gho_figures/gho_figures.info.yml deleted file mode 100644 index ce1cd223..00000000 --- a/html/modules/custom/gho_figures/gho_figures.info.yml +++ /dev/null @@ -1,5 +0,0 @@ -type: module -name: GHO Figures -description: 'DEPRECATED Figures import handling (ex: needs and requirements).' -package: gho -core_version_requirement: ^9.3 || ^10 diff --git a/html/modules/custom/gho_figures/gho_figures.links.action.yml b/html/modules/custom/gho_figures/gho_figures.links.action.yml deleted file mode 100644 index 270a8b2f..00000000 --- a/html/modules/custom/gho_figures/gho_figures.links.action.yml +++ /dev/null @@ -1,5 +0,0 @@ -gho_figures.import_needs_and_requirements: - title: 'Import needs and requirements figures' - route_name: gho_figures.import_needs_and_requirements - appears_on: - - view.needs_and_requirements.page_needs_and_requirements diff --git a/html/modules/custom/gho_figures/gho_figures.module b/html/modules/custom/gho_figures/gho_figures.module deleted file mode 100644 index 1976e0aa..00000000 --- a/html/modules/custom/gho_figures/gho_figures.module +++ /dev/null @@ -1,6 +0,0 @@ - [ - 'mandatory' => TRUE, - 'field' => 'name', - 'label' => '', - ], - 'people in need' => [ - 'mandatory' => TRUE, - 'field' => 'field_people_in_need', - 'label' => 'People in need', - 'preprocess' => [ - '\Drupal\gho_figures\Form\GhoFiguresImportNeedsAndRequirementsFiguresForm::preprocessNumber', - ], - 'table_display' => [ - '\Drupal\gho_figures\Form\GhoFiguresImportNeedsAndRequirementsFiguresForm::displayNumber', - ], - ], - 'people targeted' => [ - 'mandatory' => TRUE, - 'field' => 'field_people_targeted', - 'label' => 'People targeted', - 'preprocess' => [ - '\Drupal\gho_figures\Form\GhoFiguresImportNeedsAndRequirementsFiguresForm::preprocessNumber', - ], - 'table_display' => [ - '\Drupal\gho_figures\Form\GhoFiguresImportNeedsAndRequirementsFiguresForm::displayNumber', - ], - ], - 'requirements (us$)' => [ - 'mandatory' => TRUE, - 'field' => 'field_requirements', - 'label' => 'Requirements (US$)', - 'preprocess' => [ - '\Drupal\gho_figures\Form\GhoFiguresImportNeedsAndRequirementsFiguresForm::preprocessNumber', - ], - 'table_display' => [ - '\Drupal\gho_figures\Form\GhoFiguresImportNeedsAndRequirementsFiguresForm::displayNumber', - ], - ], - ]; - - /** - * Database connection. - * - * @var \Drupal\Core\Database\Connection - */ - protected $database; - - /** - * File system service. - * - * @var \Drupal\Core\File\FileSystemInterface - */ - protected $fileSystem; - - /** - * Configuration Factory. - * - * @var \Drupal\Core\Config\ConfigFactory - */ - protected $configFactory; - - /** - * Entity type manager service. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface - */ - protected $entityTypeManager; - - /** - * Messenger service. - * - * @var \Drupal\Core\Messenger\MessengerInterface - */ - protected $messenger; - - /** - * Class constructor. - * - * @param \Drupal\Core\Database\Connection $database - * Database connection. - * @param \Drupal\Core\File\FileSystemInterface $file_system - * File system service. - * @param \Drupal\Core\Config\ConfigFactory $config_factory - * Config factory. - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager - * Entity type manager service. - * @param \Drupal\Core\Messenger\MessengerInterface $messenger - * Messenger service. - */ - public function __construct(Connection $database, FileSystemInterface $file_system, ConfigFactory $config_factory, EntityTypeManagerInterface $entity_type_manager, MessengerInterface $messenger) { - $this->database = $database; - $this->fileSystem = $file_system; - $this->configFactory = $config_factory; - $this->entityTypeManager = $entity_type_manager; - $this->messenger = $messenger; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('database'), - $container->get('file_system'), - $container->get('config.factory'), - $container->get('entity_type.manager'), - $container->get('messenger') - ); - } - - /** - * Get a setting for the gho_figures module. - * - * @param string $setting - * Setting name. - * @param mixed $default - * Default value for the setting. - * - * @return mixed - * Value for the setting. - */ - public function getSetting($setting, $default = NULL) { - static $settings; - if (!isset($settings)) { - $settings = $this->configFactory->get('gho_figures.settings'); - } - return $settings->get($setting) ?? $default; - } - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'gho_figures_import_needs_and_requirements_figures_form'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state) { - $form['actions'] = [ - '#type' => 'actions', - // Ensure it's at the bottom of the list. - '#weight' => 10, - ]; - - if ($form_state->has('step') && $form_state->get('step') == 2) { - return self::buildFormStepTwo($form, $form_state); - } - else { - return self::buildFormStepOne($form, $form_state); - } - } - - /** - * Build the first step of the form with the file upload. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - * - * @return array - * Modified form. - */ - public function buildFormStepOne(array $form, FormStateInterface $form_state) { - $form_state->set('step', 1); - - $form['description'] = [ - '#type' => 'item', - '#title' => $this->t('

Step 1: File Upload

'), - ]; - - $form['tips'] = [ - '#type' => 'item', - '#markup' => $this->t(' -

Tips

- - '), - ]; - - $form['file'] = [ - '#type' => 'managed_file', - '#title' => $this->t('Spreadsheet file'), - '#description' => $this->t('Spreadsheet file with the figures. Accepted formats are: xls, xlsx, ods, csv.'), - '#required' => TRUE, - '#default_value' => $form_state->getValue('file'), - '#upload_location' => 'temporary://figures-import', - '#upload_validators' => [ - 'file_validate_extensions' => ['xls xlsx ods csv'], - ], - ]; - - // Proceed to step 2. - $form['actions']['next'] = [ - '#type' => 'submit', - '#button_type' => 'primary', - '#value' => $this->t('Next'), - '#submit' => ['::submitFormStepOne'], - '#validate' => ['::validateFormStepOne'], - ]; - - return $form; - } - - /** - * Validate the first step of the form. - * - * Here we also parse the spreadsheet file and extract the figures. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - */ - public function validateFormStepOne(array &$form, FormStateInterface $form_state) { - $fids = $form_state->getValue('file'); - if (!empty($fids)) { - // Load the file object and get its real path as that's what - // PHPSpreadsheet expects. - $file = $this->entityTypeManager->getStorage('file')->load(reset($fids)); - $path = $this->fileSystem->realpath($file->getFileUri()); - - // Maximum number of rows that can be parsed to limit memory consumption. - // In our case, there will be probably be less than 100 rows. - $max_rows = $this->getSetting('max_rows', 9999); - - // Extract the figures from the spreadsheet. - $figures = static::parseSpreadsheet($path, $max_rows); - if (empty($figures['figures']) && !empty($figures['errors'])) { - $form_state->setErrorByName('file', $this->t("Unable to extract figures from the spreadsheet: \n@errors.", [ - '@errors' => static::formatList($figures['errors']), - ])); - } - else { - $form_state->set('figures', $figures); - } - } - } - - /** - * Submit the first step of the form. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - */ - public function submitFormStepOne(array &$form, FormStateInterface $form_state) { - $form_state - ->set('first_step_values', ['file' => $form_state->getValue('file')]) - ->set('step', 2) - ->setRebuild(TRUE); - } - - /** - * Build the second step of the form with the confirmation. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - * - * @return array - * Modified form. - */ - public function buildFormStepTwo(array $form, FormStateInterface $form_state) { - $form_state->set('step', 2); - - $form['description'] = [ - '#type' => 'item', - '#title' => $this->t('

Step 2: Confirmation

'), - ]; - - // Display the table with the list of figures to import. - $figures = $form_state->get('figures'); - if (!empty($figures['figures'])) { - // Maximum number of figures to show. - $count = count($figures['figures']); - $headers = static::getTableHeaders($figures['columns']); - $rows = static::getTableRows($headers, $figures['figures']); - - $form['figure-list'] = [ - '#type' => 'details', - '#title' => $this->formatPlural($count, - '@count figure to import', - '@count figures to import' - ), - 'table' => [ - '#type' => 'table', - '#header' => $headers, - '#rows' => $rows, - ], - ]; - } - - // Display the errors detected while parsing the spreadsheet. - if (!empty($figures['errors'])) { - $count = count($figures['errors']); - - $form['error-list'] = [ - '#type' => 'details', - '#title' => $this->formatPlural($count, - '@count parsing error', - '@count parsing errors' - ), - 'list' => [ - '#theme' => 'item_list', - '#list_type' => 'ul', - '#items' => $figures['errors'], - ], - ]; - } - - // Button to go back to the step 1. - $form['actions']['back'] = [ - '#type' => 'submit', - '#value' => $this->t('Back'), - '#submit' => ['::cancelFormStepTwo'], - // Prevent validation errors as we are going back and the values from - // the step 2 should be ignored then. - '#limit_validation_errors' => [], - ]; - - // Submit the form, actually replacing the figures. - // We default to base form submit and validation callbacks. - $form['actions']['submit'] = [ - '#type' => 'submit', - '#button_type' => 'primary', - '#value' => $this->t('Import figures'), - ]; - - return $form; - } - - /** - * Get the figures table headers to display in the import confirmation step. - * - * @param array $columns - * Array of Header columns with the names as keys. - * - * @return array - * List of table headers. - */ - public static function getTableHeaders(array $columns) { - $headers = []; - - foreach (static::$columns as $name => $definition) { - if (isset($columns[$name])) { - $headers[$name] = $definition['label']; - } - } - - return $headers; - } - - /** - * Get the figures table rows to display in the import confirmation step. - * - * @param array $headers - * List of header column names. - * @param array $figures - * List of figures data extracted from the spreadsheet. - * - * @return array - * Table rows. Each row contains cells for each column. Each cell can be - * either a string or a render array. - */ - public static function getTableRows(array $headers, array $figures) { - $rows = []; - foreach ($figures as $data) { - $row = []; - foreach ($headers as $name => $label) { - if (isset(static::$columns[$name]['table_display'])) { - $row[$name] = static::call(static::$columns[$name]['table_display'] + [ - 'name' => $name, - ], $data); - } - else { - $row[$name] = $data[$name] ?? ''; - } - } - $rows[] = $row; - } - return $rows; - } - - /** - * Return to the first step. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - */ - public function cancelFormStepTwo(array &$form, FormStateInterface $form_state) { - $form_state - ->setValues($form_state->get('first_step_values')) - ->set('step', 1) - ->setRebuild(TRUE); - } - - /** - * Validate the form (after step 2). - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - */ - public function validateForm(array &$form, FormStateInterface $form_state) { - // Nothing to validate. - } - - /** - * Submit the form (after step 2). - * - * Generate the batch to update the "needs and requirements" figures. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - $batch_size = $this->getSetting('batch_size', 50); - - // We will (optionally) delete the old figures and create the new ones - // in batches as that takes time. - $figures = $form_state->get('figures'); - if (!empty($figures['figures'])) { - $operations = []; - - // We log the parsing errors (if any) during the batch process because - // otherwise it slows down too much the parsing. Also it makes more sense - // to log them when actually proceeding with the import. - $operations[] = [ - __CLASS__ . '::logParsingInfo', - [$figures['path'], $figures['errors']], - ]; - - // Extract the name of the figures to update/create. - $names = array_map(function ($figure) { - return $figure['name']; - }, $figures['figures']); - - // Create batch steps to delete figure terms not present in the list. - $records = $this->entityTypeManager->getStorage('taxonomy_term')->getQuery() - ->condition('vid', 'needs_and_requirements') - ->condition('name', $names, 'NOT IN') - ->accessCheck(FALSE) - ->execute(); - - if (!empty($records)) { - foreach (array_chunk($records, $batch_size) as $ids) { - $operations[] = [__CLASS__ . '::deleteFigureTerms', [$ids]]; - } - } - - // Create batch steps to create the new figure terms. - foreach (array_chunk($figures['figures'], $batch_size) as $data) { - $operations[] = [__CLASS__ . '::createFigureTerms', [$data]]; - } - - $batch = [ - 'title' => $this->t('Importing figures...'), - 'operations' => $operations, - 'finished' => __CLASS__ . '::batchFinished', - ]; - batch_set($batch); - } - else { - $this->messenger->addWarning($this->t('No figures to import. Existing figures will not be updated.')); - } - } - - /** - * Log the parsing errors. - * - * @param string $path - * The path of the spreadsheet. - * @param array $errors - * The parsing errors. - * @param array $context - * The batch context. - */ - public static function logParsingInfo($path, array $errors, array &$context) { - // Log the filename to help make sense of the parsing errors. - static::log(new FormattableMarkup('Parsed spreadsheet: @path', [ - '@path' => $path, - ]), 'info'); - - // We log the errors as notices because they don't impact the whole site. - foreach ($errors as $error) { - static::log($error, 'notice'); - } - - // Set a message for the current batch and update the progress status. - $count = count($errors); - $context['message'] = \Drupal::translation()->formatPlural($count, - 'Logged @count parsing error.', - 'Logged @count parsing errors.' - ); - $context['results']['logged'][] = $count; - } - - /** - * Delete the "needs and requirements" figure terms for the given ids. - * - * Note: we don't actually delete the terms but wipe out their data and - * unpublish them so that we can preserve any references to them in article - * nodes for example, in case they are updated later on. - * - * @param array $ids - * Term ids. - * @param array $context - * The batch context. - */ - public static function deleteFigureTerms(array $ids, array &$context) { - $storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term'); - - // Unpublish the terms that are not used. - $terms = $storage->loadMultiple($ids); - foreach ($terms as $term) { - // Mark as unpublished. - $term->status = 0; - // Wipe out the values of the figures for the term. - foreach (static::$columns as $field => $definition) { - // The term name is a mandatory field that cannot be empty. It's also - // the field we use when matching figures before creating a new one. - if ($field !== 'name' && isset($definition['field'])) { - $term->set($definition['field'], NULL); - } - } - $term->save(); - } - - // Set a message for the current batch and update the progress status. - $count = count($ids); - $context['message'] = \Drupal::translation()->formatPlural($count, - 'Deleted @count old figure.', - 'Deleted @count old figures.' - ); - $context['results']['deleted'][] = $count; - } - - /** - * Create new "needs and requirements" figure terms. - * - * Note: if a term already exists, we simply update it. - * - * @param array $figures - * Figure data. - * @param array $context - * The batch context. - */ - public static function createFigureTerms(array $figures, array &$context) { - $storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term'); - - // Create a new term for each figure. - foreach ($figures as $figure) { - // Check if a term with the same name already exists and re-use it. - $terms = $storage->loadByProperties([ - 'vid' => 'needs_and_requirements', - 'name' => $figure['name'], - ]); - - if (!empty($terms)) { - $term = reset($terms); - } - else { - $term = $storage->create(['vid' => 'needs_and_requirements']); - } - - // Update the term fields. - static::updateFigureTerm($term, $figure); - // Mark as published. - $term->status = 1; - $term->save(); - } - - // Set a message for the current batch and update the progress status. - $count = count($figures); - $context['message'] = \Drupal::translation()->formatPlural($count, - 'Created/updated @count new figure.', - 'Created/updated @count new figures.' - ); - $context['results']['created'][] = $count; - } - - /** - * Update a "needs and requirements" figure term's fields with new data. - * - * @param \Drupal\taxonomy\Entity\Term $term - * Node to update. - * @param array $data - * New figure data. - * - * @todo review if we should get the node fields via ::getFields() and - * reset all the fields for which there is no corresponding data. - */ - public static function updateFigureTerm(Term $term, array $data) { - foreach (static::$columns as $name => $definition) { - if (!isset($definition['field'])) { - continue; - } - $field = $definition['field']; - $value = NULL; - if (isset($data[$name])) { - $value = $data[$name]; - - if (isset($definition['process'])) { - $value = static::call($definition['process'] + ['name' => $name], $value); - } - } - $term->set($field, $value); - } - } - - /** - * Display message after the batch import is finished. - * - * @param bool $success - * Whether the batch process succeeeded or not. - * @param array $results - * Batch results. - * @param array $operations - * List of batch operations. - */ - public static function batchFinished($success, array $results, array $operations) { - if ($success) { - if (!empty($results['deleted'])) { - \Drupal::messenger()->addStatus(t('Deleted %deleted old figures.', [ - '%deleted' => array_sum($results['deleted']), - ])); - } - if (!empty($results['created'])) { - \Drupal::messenger()->addStatus(t('Created/updated %created figures.', [ - '%created' => array_sum($results['created']), - ])); - } - } - else { - // @todo Show a more useful error message? - \Drupal::messenger()->addError(t('No figures to import. Old figures were not deleted.')); - } - } - - /** - * Get a taxonomy term ID. Create a new term if necessary. - * - * @param string $vocabulary - * Taxonomy vocabulary. - * @param string|array $name - * Taxonomy term name or array of names. - * - * @return int|null - * Id of the first term, newly created if doesn't already exists. - */ - public static function getTermId($vocabulary, $name) { - $multiple = is_array($name); - - $names = array_filter(array_map('trim', $multiple ? $name : [$name])); - - if (empty($names)) { - return NULL; - } - - $results = []; - foreach ($names as $name) { - $term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term'); - - // Sanitize and truncate the term if necessary. - $words = preg_split('/' . Unicode::PREG_CLASS_WORD_BOUNDARY . '/u', $name, -1, PREG_SPLIT_NO_EMPTY); - if (count($words) > 10) { - $name = implode(' ', array_slice($words, 0, 10)) . ' ...'; - } - - // Get any existing taxonomy term matching the given term name. - $terms = $term_storage->loadByProperties([ - 'vid' => $vocabulary, - 'name' => $name, - ]); - - // Get the first existing term or create one. - if (!empty($terms)) { - $term = reset($terms); - } - else { - $term = $term_storage->create([ - 'vid' => $vocabulary, - 'name' => $name, - ]); - $term->save(); - } - $results[] = $term->id(); - } - - return $multiple ? $results : reset($results); - } - - /** - * Extract figures from a spreadsheet. - * - * @param string $path - * File path. - * @param int $max_rows - * Maximum number rows to parse. - * - * @return array - * Associative array with the spreadsheet file path, the header columns, - * figure list and potential parsing errors. - */ - public static function parseSpreadsheet($path, $max_rows) { - $columns = []; - $figures = []; - $errors = []; - - // We wrap this code in a try...catch because PHPSpreadsheet can throw - // various exceptions when parsing a spreadsheet. - try { - // Get the worksheet to work with (pun intended). - $sheet = static::getWorksheet($path); - - // Get the row to which will stop the parsing. - $max_rows = min($sheet->getHighestDataRow(), $max_rows); - - // Parse the sheet, extracting figure data. - $header_row_found = FALSE; - foreach ($sheet->getRowIterator(1, $max_rows) as $row) { - // Parse the row to see if it's the header one. - if ($header_row_found === FALSE) { - $data = static::parseHeaderRow($sheet, $row); - // There are errors if the header row was found but some mandatory - // columns are missing. In that case we abort the parsing. - if (!empty($data['errors'])) { - $errors = array_merge($errors, $data['errors']); - break; - } - // Otherwise if the header row was found, we store the columns and - // make sure we can start parsing the data. If not, we continue - // looking for it. - elseif (!empty($data['columns'])) { - $columns = $data['columns']; - $header_row_found = TRUE; - } - } - // Parse a figure data row. - else { - $data = static::parseDataRow($columns, $sheet, $row); - if (!empty($data['data'])) { - // Log the errors only for "useful" rows with some data. - if (!empty($data['errors'])) { - $errors = array_merge($errors, $data['errors']); - } - // If there is an email, merge the data with the figure entry with - // the same email address if any, otherwise create a new entry if - // the data is "valid", meaning, it has all the mandatory fields. - if (!empty($data['data']['name'])) { - $figures[$data['data']['name']] = $data['data']; - } - } - } - } - } - catch (\Exception $exception) { - $errors[] = $exception->getMessage(); - } - - return [ - 'path' => $path, - 'columns' => $columns, - 'figures' => $figures, - 'errors' => $errors, - ]; - } - - /** - * Load a spreadsheet and return its first worksheet. - * - * @param string $path - * File path. - * - * @return \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet - * First worksheet. - */ - public static function getWorksheet($path) { - if (!file_exists($path)) { - throw new \Exception("The spreadsheet file doesn't exist."); - } - - // Get the spreadsheet type. - $filetype = IOFactory::identify($path); - - // Create the spreadsheet reader. - $reader = IOFactory::createReader($filetype); - - // Start reading the file. - $spreadsheet = $reader->load($path); - - // We only deal with the first sheet. - return $spreadsheet->getActiveSheet(); - } - - /** - * Parse a row, attempting to determine if its the header row. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $sheet - * Spreadsheet worksheet. - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Row $row - * Spreadsheet row. - * - * @return array - * Empty array if the row is not the header row, otherwise, return an - * associative array with the found columns which is an associative array - * with the column name (header) as value and the column letter as value. - * If the row is the header one but some mandatory columns are missing, the - * returnning array will have an `errors` key with the list of errors. - */ - public static function parseHeaderRow(Worksheet $sheet, Row $row) { - $columns = []; - foreach ($row->getCellIterator() as $index => $cell) { - $value = mb_strtolower(static::getCellValue($sheet, $cell->getCoordinate())); - // Fix malformed column names... - $value = trim(preg_replace('/\s+/u', ' ', $value)); - if (isset($value, static::$columns[$value]) && !isset($columns[$value])) { - $columns[$value] = $cell->getColumn(); - } - // If the first column doesn't have a name we assume it is the "name" - // column with the overviews and appeals. - elseif ($index === 'A') { - $columns['name'] = $cell->getColumn(); - } - } - - if (count($columns) < 2) { - return []; - } - - // Validate mandatory columns. - $errors = []; - foreach (static::$columns as $name => $definition) { - if (!static::checkMandatoryField($name, $definition, $columns)) { - // We use TranslatableMarkup so that the error can be displayed - // translated in the confirmation step. - $errors[] = new TranslatableMarkup('Missing @column column.', [ - '@column' => $name, - ]); - } - } - - return [ - 'columns' => $columns, - 'errors' => $errors, - ]; - } - - /** - * Parse a row with figure data. - * - * @todo Check if we need to use getCalculatedValue() instead of getValue(). - * - * @param array $columns - * Associative array of header columns with their associated column index. - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $sheet - * Spreadsheet worksheet. - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Row $row - * Spreadsheet row. - * - * @return array - * Associative array with the following keys: - * - data: associative array mapping the column names to their values, - * - errors: array with a list of parsing errors, - * - valid: a flag to indicate that the data contains all the mandatory - * fields. - */ - public static function parseDataRow(array $columns, Worksheet $sheet, Row $row) { - $index = $row->getRowIndex(); - $data = []; - $errors = []; - $valid = TRUE; - - // Get the data from the row foreach column with a recognized header. - foreach ($columns as $name => $column) { - $data[$name] = static::getCellValue($sheet, $column . $index); - } - - // Skip if there no valid cell beside the name one as it's probably - // not a data row. - $skip = count(array_filter($data, function ($value, $name) { - return $name !== 'name' && $value !== ''; - }, ARRAY_FILTER_USE_BOTH)) === 0; - - if ($skip) { - return []; - } - - // Process the row's data. - foreach (static::$columns as $name => $definition) { - if (isset($definition['preprocess'])) { - if (!static::call($definition['preprocess'] + ['name' => $name], $data)) { - $errors[$name] = new TranslatableMarkup('Invalid @column on row @row.', [ - '@column' => $name, - '@row' => $index, - ]); - } - } - } - - // Check mandatory fields. This is not done in the loop above because - // the data may change during preprocessing. - foreach (static::$columns as $name => $definition) { - if (!static::checkMandatoryField($name, $definition, $data)) { - // No need to add different error messages for the same field, for - // example if the field data was found invalid during preprocessing. - if (!isset($errors[$name])) { - $errors[$name] = new TranslatableMarkup('Missing @column on row @row.', [ - '@column' => $name, - '@row' => $index, - ]); - } - $valid = FALSE; - } - } - - return [ - 'data' => $data, - 'errors' => array_values($errors), - 'valid' => $valid, - ]; - } - - /** - * Get a cell value. - * - * This extracts the value of a cell. If the cell is merged with other cells - * we extract the combine value for the whole merge range. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $sheet - * Spreadsheet worksheet. - * @param string $reference - * Cell reference (ex: A1). - * - * @return string - * Extracted value, defaulting to an empty string. - */ - public static function getCellValue(Worksheet $sheet, $reference) { - static $references; - static $values; - - if (!isset($references, $values)) { - [$references, $values] = static::extractMergedCells($sheet); - } - - if (isset($references[$reference])) { - return $values[$references[$reference]]; - } - elseif ($sheet->getCellCollection()->has($reference)) { - return trim($sheet->getCellCollection()->get($reference)->getValue()); - } - return ''; - } - - /** - * Extract the values for the merged cells. - * - * We store the merged cells references and the merge range values so that - * we don't have to parse the merge ranges every time we try to get a cell - * value. This speeds tremendously the spreadsheet parsing at the cost of - * increased memory usage. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $sheet - * Worksheet from which to extract the merged cells data. - * - * @return array - * Array containing 2 elements: an associative array of the references of - * the cells included in merge ranges mapped to to the range reference, and - * an associative array mapping range references to their values. - */ - public static function extractMergedCells(Worksheet $sheet) { - $references = []; - $values = []; - - foreach ($sheet->getMergeCells() as $range) { - // Extract all the merged cell references and store their mapping to - // the merge range. We don't copy directly the merge range value to - // reduce memory usage. - foreach (Coordinate::extractAllCellReferencesInRange($range) as $index => $reference) { - // The first cell of the range is supposed to contain the value of the - // range. - // @see \PhpOffice\PhpSpreadsheet\Cell\Cell::isMergeRangeValueCell() - if ($index === 0) { - if ($sheet->getCellCollection()->has($reference)) { - $values[$range] = trim($sheet->getCellCollection()->get($reference)->getValue()); - } - else { - $values[$range] = ''; - } - } - $references[$reference] = $range; - } - } - return [$references, $values]; - } - - /** - * Preprocess the duty station country field. - * - * This extracts the countries from the `country` field or from the - * `duty station country` field if the former is empty. - * - * @param string $name - * Field name. - * @param array $data - * Row data. - * - * @return bool - * FALSE if the data was invalid, TRUE otherwise. - */ - public static function preprocessNumber($name, array &$data) { - if (isset($data[$name])) { - $value = $data[$name]; - if ($value <= 0 || $value === '-' || strtoupper($value) === 'TBC') { - $value = 0; - } - else { - $options = ['options' => ['min_range' => 0]]; - $value = filter_var($data[$name], FILTER_VALIDATE_INT, $options); - } - // Remove the value if it's not a positive integer so that the row - // will be removed and an error displayed. - if ($value === FALSE) { - unset($data[$name]); - return FALSE; - } - else { - $data[$name] = $value; - return TRUE; - } - } - return FALSE; - } - - /** - * Get a render array/string to display a formatted number (ex: 1.2 million). - * - * @param string $name - * Field name. - * @param array $data - * Figure data. - * - * @return array|string - * Render array or empty string. - */ - public static function displayNumber($name, array $data) { - if (!isset($data[$name])) { - return ''; - } - if ($data[$name] == 0) { - return 'TBC'; - } - // @todo use the same formatter as the one used to render the figures in - // the frontend (ex: 1.2 million). - return number_format($data[$name]); - } - - /** - * Helper method to display a list of items as a HTML list. - * - * @param array $items - * List of strings. - * - * @return \Drupal\Component\Render\FormattableMarkup - * FormattableMarkup object containing the HTML list that can be passed - * as placeholder replacement to `t()`. - */ - public static function formatList(array $items) { - $html = ''; - return new FormattableMarkup($html, []); - } - - /** - * Check of the given data contains the mandatory field data. - * - * @param string $name - * Column name. - * @param array $definition - * Column definition. - * @param array $data - * Field data. - * - * @return bool - * Whether the field is present or not. - */ - public static function checkMandatoryField($name, array $definition, array $data) { - return empty($definition['mandatory']) || isset($data[$name]); - } - - /** - * Call a function with the given arguments and data. - * - * @param array $arguments - * Array with the callable function/method as first element and with the - * rest as parameters to pass to the callable. - * @param mixed $data - * Additional data to pass to the callable. It is passed by reference and - * may be modified by the callable. - * - * @return mixed - * The result of the call. - */ - public static function call(array $arguments, &$data) { - $callable = array_shift($arguments); - $arguments[] = &$data; - return call_user_func_array($callable, array_values($arguments)); - } - - /** - * Log a message. - * - * @param mixed $message - * String-ish value. If the message is an instance of - * \Drupal\Core\StringTranslation\TranslatableMarkup then we build a non - * translated message as the logs are for internal information and it - * doesn't make sense to have them in the display language of the current - * user. - * @param string $level - * Log level. - */ - public static function log($message, $level = 'info') { - if ($message instanceof TranslatableMarkup) { - $message = new FormattableMarkup($message->getUntranslatedString(), $message->getArguments()); - } - \Drupal::logger('gho-figures-import')->log($level, $message); - } - -} diff --git a/html/modules/custom/gho_figures/src/config/install/gho_figures.settings.yml b/html/modules/custom/gho_figures/src/config/install/gho_figures.settings.yml deleted file mode 100644 index 69987793..00000000 --- a/html/modules/custom/gho_figures/src/config/install/gho_figures.settings.yml +++ /dev/null @@ -1,4 +0,0 @@ -# Maximum number of rows to parse in the spreadsheet. -max_rows: 9999 -# Number of figures to delete/create per batch. -batch_size: 50 diff --git a/html/themes/custom/common_design_subtheme/common_design_subtheme.libraries.yml b/html/themes/custom/common_design_subtheme/common_design_subtheme.libraries.yml index ac1b29c0..6719986e 100644 --- a/html/themes/custom/common_design_subtheme/common_design_subtheme.libraries.yml +++ b/html/themes/custom/common_design_subtheme/common_design_subtheme.libraries.yml @@ -37,6 +37,11 @@ gho-bottom-figure-row: theme: components/gho-bottom-figure-row/gho-bottom-figure-row.css: {} +gho-top-figures: + css: + theme: + components/gho-top-figures/gho-top-figures.css: {} + gho-download-button: css: theme: diff --git a/html/themes/custom/common_design_subtheme/components/gho-top-figures/README.md b/html/themes/custom/common_design_subtheme/components/gho-top-figures/README.md new file mode 100644 index 00000000..1476eaa8 --- /dev/null +++ b/html/themes/custom/common_design_subtheme/components/gho-top-figures/README.md @@ -0,0 +1,4 @@ +HPC Content Module - Top Figures Component +========================================================== + +Displays a horizontal row of manually-entered textual figures (label + figure). diff --git a/html/themes/custom/common_design_subtheme/components/gho-top-figures/gho-top-figures.css b/html/themes/custom/common_design_subtheme/components/gho-top-figures/gho-top-figures.css new file mode 100644 index 00000000..ad834d63 --- /dev/null +++ b/html/themes/custom/common_design_subtheme/components/gho-top-figures/gho-top-figures.css @@ -0,0 +1,66 @@ +/** + * Base field. + */ +.gho-top-figures { + margin-top: 2rem; + font-size: 0.875rem; + line-height: 1.25rem; +} +/* The border is not on the container itself due to eventual left/right padding + * values that can interfere. Ex: when it has the `content-width` class. */ +.gho-top-figures::before { + display: block; + padding-bottom: 2rem; + content: ""; + border-top: 1px solid #d8d8d8; +} +/* Hide the border when the figures follow a NaR paragraph. */ +.gho-needs-and-requirements-paragraph + .gho-top-figures::before { + content: none; +} + +.gho-top-figures .gho-figures { + display: flex; + overflow-x: hidden; + flex-flow: row wrap; + align-content: space-around; + justify-content: flex-start; + /* Compensate for the margins of the figure items. */ + margin: -1rem 0 0 0; + padding-bottom: 2rem; + border-bottom: 1px solid #d8d8d8; +} + +.gho-top-figures .gho-figure { + flex: 0 1 auto; + min-width: 140px; + margin: 1rem 0 0 0; +} +[dir="ltr"] .gho-top-figures .gho-figure { + margin-right: 0; + /* The left margin combined with the overflow-x: hidden of the parent + * ensures the left border and padding are hidden for the first item of each + * row. */ + margin-left: calc(-1rem - 1px); + padding: 0 2rem 0 1rem; + border-right: none; + border-left: 1px solid #d8d8d8; +} +[dir="rtl"] .gho-top-figures .gho-figure { + margin-right: calc(-1rem - 1px); + /* The right margin combined with the overflow-x: hidden of the parent + * ensures the right border and padding are hidden for the first item of each + * row. */ + margin-left: 0; + padding: 0 1rem 0 2rem; + border-right: 1px solid #d8d8d8; + border-left: none; +} + +.gho-top-figures .gho-figure__label, +.gho-top-figures .gho-figure__value { + display: block; +} +.gho-top-figures .gho-figure__label { + font-weight: 700; +} diff --git a/html/themes/custom/common_design_subtheme/templates/paragraphs/paragraph--top-figures-small.html.twig b/html/themes/custom/common_design_subtheme/templates/paragraphs/paragraph--top-figures-small.html.twig new file mode 100644 index 00000000..84b29572 --- /dev/null +++ b/html/themes/custom/common_design_subtheme/templates/paragraphs/paragraph--top-figures-small.html.twig @@ -0,0 +1,59 @@ +{# +/** + * @file + * Theme implementation for a Bottom Figure Row paragraph. + * + * Overrides paragraphs/templates/paragraph.html.twig. + * + * Available variables: + * - paragraph: Full paragraph entity. + * Only method names starting with "get", "has", or "is" and a few common + * methods such as "id", "label", and "bundle" are available. For example: + * - paragraph.getCreatedTime() will return the paragraph creation timestamp. + * - paragraph.id(): The paragraph ID. + * - paragraph.bundle(): The type of the paragraph, for example, "image" or "text". + * - paragraph.getOwnerId(): The user ID of the paragraph author. + * See Drupal\paragraphs\Entity\Paragraph for a full list of public properties + * and methods for the paragraph object. + * - content: All paragraph items. Use {{ content }} to print them all, + * or print a subset such as {{ content.field_example }}. Use + * {{ content|without('field_example') }} to temporarily suppress the printing + * of a given child element. + * - attributes: HTML attributes for the containing element. + * The attributes.class element may contain one or more of the following + * classes: + * - paragraphs: The current template type (also known as a "theming hook"). + * - paragraphs--type-[type]: The current paragraphs type. For example, if the paragraph is an + * "Image" it would result in "paragraphs--type--image". Note that the machine + * name will often be in a short form of the human readable label. + * - paragraphs--view-mode--[view_mode]: The View Mode of the paragraph; for example, a + * preview would result in: "paragraphs--view-mode--preview", and + * default: "paragraphs--view-mode--default". + * - view_mode: View mode; for example, "preview" or "full". + * - logged_in: Flag for authenticated user status. Will be true when the + * current user is a logged-in member. + * - is_admin: Flag for admin user status. Will be true when the current user + * is an administrator. + * + * @see template_preprocess_paragraph() + * + * @ingroup themeable + */ +#} +{{ attach_library('common_design_subtheme/gho-top-figures') }} +{% + set classes = [ + 'paragraph', + 'paragraph--type--' ~ paragraph.bundle|clean_class, + not paragraph.isPublished() ? 'paragraph--unpublished', + 'gho-top-figures', + 'content-width', + ] +%} +{% block paragraph %} + + {% block content %} + {{ content }} + {% endblock %} + +{% endblock paragraph %} diff --git a/html/themes/custom/common_design_subtheme/templates/paragraphs/paragraph--needs-and-requirements.html.twig b/html/themes/custom/common_design_subtheme/templates/paragraphs/paragraph--top-figures.html.twig similarity index 97% rename from html/themes/custom/common_design_subtheme/templates/paragraphs/paragraph--needs-and-requirements.html.twig rename to html/themes/custom/common_design_subtheme/templates/paragraphs/paragraph--top-figures.html.twig index 3b52d97a..84962867 100644 --- a/html/themes/custom/common_design_subtheme/templates/paragraphs/paragraph--needs-and-requirements.html.twig +++ b/html/themes/custom/common_design_subtheme/templates/paragraphs/paragraph--top-figures.html.twig @@ -1,7 +1,7 @@ {# /** * @file - * Theme implementation for a Needs and Requirements paragraph. + * Theme implementation for a Bottom Figure Row paragraph as "top figures". * * Overrides paragraphs/templates/paragraph.html.twig. * From 43b63bcf0b514f5286076fd02e1e743de3988c19 Mon Sep 17 00:00:00 2001 From: berliner Date: Thu, 18 Apr 2024 18:05:36 +0200 Subject: [PATCH 02/17] HPC-9466: Streamline paragraph form UI for editory, single place for status flag, always visible, move behavior plugin settings into horizontal tabs, this also removes the promoted behavior from a couple of paragraph types that don't need it, and it removes document_articles and document_chapter from article paragraphs --- ...lay.paragraph.achievement_list.default.yml | 68 ++++++++++++++++-- ...display.paragraph.article_list.default.yml | 39 ++++++++++- ...ay.paragraph.facts_and_figures.default.yml | 51 ++++++++++++-- ...play.paragraph.further_reading.default.yml | 36 +++++++++- ....paragraph.interactive_content.default.yml | 69 ++++++++++++++++--- ...ty_form_display.paragraph.text.default.yml | 39 ++++++++++- ..._display.paragraph.top_figures.default.yml | 62 ++++++++++++++++- ...ay.paragraph.top_figures_small.default.yml | 62 ++++++++++++++++- ..._display.paragraph.top_figures.default.yml | 8 +++ ...ay.paragraph.top_figures_small.default.yml | 8 +++ ...ld.field.node.article.field_paragraphs.yml | 8 ++- ...ld.paragraph.top_figures.field_dataset.yml | 31 +++++++++ ...agraph.top_figures_small.field_dataset.yml | 31 +++++++++ ...ragraphs.paragraphs_type.photo_gallery.yml | 12 +--- config/paragraphs.paragraphs_type.story.yml | 12 +--- ...paragraphs.paragraphs_type.sub_article.yml | 12 +--- config/views.view.trash_nodes.yml | 2 + .../paragraphs/Behavior/PromoteBehavior.php | 1 + .../css/edit-form/paragraph_edit_form.css | 5 ++ .../custom/ncms_ui/css/frontend_styles.css | 4 +- html/modules/custom/ncms_ui/css/ncms_gin.css | 1 + .../custom/ncms_ui/ncms_ui.libraries.yml | 4 ++ html/modules/custom/ncms_ui/ncms_ui.module | 24 ++++++- 23 files changed, 523 insertions(+), 66 deletions(-) create mode 100644 config/field.field.paragraph.top_figures.field_dataset.yml create mode 100644 config/field.field.paragraph.top_figures_small.field_dataset.yml create mode 100644 html/modules/custom/ncms_ui/css/edit-form/paragraph_edit_form.css diff --git a/config/core.entity_form_display.paragraph.achievement_list.default.yml b/config/core.entity_form_display.paragraph.achievement_list.default.yml index c085d6b8..2c0e0baa 100755 --- a/config/core.entity_form_display.paragraph.achievement_list.default.yml +++ b/config/core.entity_form_display.paragraph.achievement_list.default.yml @@ -7,28 +7,84 @@ dependencies: - field.field.paragraph.achievement_list.field_text - paragraphs.paragraphs_type.achievement_list module: + - field_group - paragraphs - text +third_party_settings: + field_group: + group_tabs: + children: + - group_achievements + - group_caption + label: Tabs + region: content + parent_name: '' + weight: 0 + format_type: tabs + format_settings: + classes: '' + show_empty_fields: false + id: '' + direction: horizontal + width_breakpoint: 640 + group_achievements: + children: + - field_achievements + label: Achievements + region: content + parent_name: group_tabs + weight: 6 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: open + description: '' + required_fields: true + group_caption: + children: + - field_text + label: Caption + region: content + parent_name: group_tabs + weight: 7 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: closed + description: '' + required_fields: true id: paragraph.achievement_list.default targetEntityType: paragraph bundle: achievement_list mode: default content: field_achievements: - type: entity_reference_paragraphs - weight: 0 + type: paragraphs + weight: 1 region: content settings: title: Paragraph title_plural: Paragraphs edit_mode: open + closed_mode: summary + autocollapse: none + closed_mode_threshold: 0 add_mode: dropdown form_display_mode: default - default_paragraph_type: '' + default_paragraph_type: achievement + features: + add_above: '0' + collapse_edit_all: collapse_edit_all + convert: '0' + duplicate: duplicate third_party_settings: { } field_text: type: text_textarea - weight: 1 + weight: 2 region: content settings: rows: 5 @@ -36,13 +92,13 @@ content: third_party_settings: { } status: type: boolean_checkbox - weight: 2 + weight: 3 region: content settings: display_label: true third_party_settings: { } translation: - weight: 3 + weight: 4 region: content settings: { } third_party_settings: { } diff --git a/config/core.entity_form_display.paragraph.article_list.default.yml b/config/core.entity_form_display.paragraph.article_list.default.yml index 28b933fb..12cdb53e 100755 --- a/config/core.entity_form_display.paragraph.article_list.default.yml +++ b/config/core.entity_form_display.paragraph.article_list.default.yml @@ -7,7 +7,40 @@ dependencies: - field.field.paragraph.article_list.field_title - paragraphs.paragraphs_type.article_list module: + - field_group - link +third_party_settings: + field_group: + group_tabs: + children: + - group_article_list + label: Tabs + region: content + parent_name: '' + weight: 0 + format_type: tabs + format_settings: + classes: '' + show_empty_fields: false + id: '' + direction: horizontal + width_breakpoint: 640 + group_article_list: + children: + - field_title + - field_links + label: 'Article list' + region: content + parent_name: group_tabs + weight: 6 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: open + description: '' + required_fields: true id: paragraph.article_list.default targetEntityType: paragraph bundle: article_list @@ -15,7 +48,7 @@ mode: default content: field_links: type: link_default - weight: 1 + weight: 8 region: content settings: placeholder_url: '' @@ -23,7 +56,7 @@ content: third_party_settings: { } field_title: type: string_textfield - weight: 0 + weight: 7 region: content settings: size: 60 @@ -31,7 +64,7 @@ content: third_party_settings: { } status: type: boolean_checkbox - weight: 2 + weight: 3 region: content settings: display_label: true diff --git a/config/core.entity_form_display.paragraph.facts_and_figures.default.yml b/config/core.entity_form_display.paragraph.facts_and_figures.default.yml index d279f3f8..b136b1da 100755 --- a/config/core.entity_form_display.paragraph.facts_and_figures.default.yml +++ b/config/core.entity_form_display.paragraph.facts_and_figures.default.yml @@ -7,27 +7,68 @@ dependencies: - field.field.paragraph.facts_and_figures.field_title - paragraphs.paragraphs_type.facts_and_figures module: + - field_group - paragraphs +third_party_settings: + field_group: + group_tabs: + children: + - group_facts_and_figures + label: Tabs + region: content + parent_name: '' + weight: 0 + format_type: tabs + format_settings: + classes: '' + show_empty_fields: false + id: '' + direction: horizontal + width_breakpoint: 640 + group_facts_and_figures: + children: + - field_title + - field_paragraphs + label: 'Facts and figures' + region: content + parent_name: group_tabs + weight: 6 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: open + description: '' + required_fields: true id: paragraph.facts_and_figures.default targetEntityType: paragraph bundle: facts_and_figures mode: default content: field_paragraphs: - type: entity_reference_paragraphs - weight: 1 + type: paragraphs + weight: 2 region: content settings: title: 'Fact and figure' title_plural: 'Facts and figures' - edit_mode: preview + edit_mode: closed + closed_mode: preview + autocollapse: none + closed_mode_threshold: 0 add_mode: button form_display_mode: default default_paragraph_type: image_with_text + features: + add_above: '0' + collapse_edit_all: collapse_edit_all + convert: '0' + duplicate: duplicate third_party_settings: { } field_title: type: string_textfield - weight: 0 + weight: 1 region: content settings: size: 60 @@ -35,7 +76,7 @@ content: third_party_settings: { } status: type: boolean_checkbox - weight: 2 + weight: 3 region: content settings: display_label: true diff --git a/config/core.entity_form_display.paragraph.further_reading.default.yml b/config/core.entity_form_display.paragraph.further_reading.default.yml index 3382f94a..de343120 100755 --- a/config/core.entity_form_display.paragraph.further_reading.default.yml +++ b/config/core.entity_form_display.paragraph.further_reading.default.yml @@ -6,7 +6,39 @@ dependencies: - field.field.paragraph.further_reading.field_links - paragraphs.paragraphs_type.further_reading module: + - field_group - gho_fields +third_party_settings: + field_group: + group_tabs: + children: + - group_links + label: Tabs + region: content + parent_name: '' + weight: 0 + format_type: tabs + format_settings: + classes: '' + show_empty_fields: false + id: '' + direction: horizontal + width_breakpoint: 640 + group_links: + children: + - field_links + label: Links + region: content + parent_name: group_tabs + weight: 5 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: open + description: '' + required_fields: true id: paragraph.further_reading.default targetEntityType: paragraph bundle: further_reading @@ -14,7 +46,7 @@ mode: default content: field_links: type: gho_further_reading_link - weight: 0 + weight: 1 region: content settings: placeholder_url: 'https://example.com' @@ -22,7 +54,7 @@ content: third_party_settings: { } status: type: boolean_checkbox - weight: 1 + weight: 2 region: content settings: display_label: true diff --git a/config/core.entity_form_display.paragraph.interactive_content.default.yml b/config/core.entity_form_display.paragraph.interactive_content.default.yml index 952496a8..122e0eb3 100755 --- a/config/core.entity_form_display.paragraph.interactive_content.default.yml +++ b/config/core.entity_form_display.paragraph.interactive_content.default.yml @@ -13,9 +13,62 @@ dependencies: - field.field.paragraph.interactive_content.field_type - paragraphs.paragraphs_type.interactive_content module: + - field_group - gho_fields - link - media_library +third_party_settings: + field_group: + group_tabs: + children: + - group_interactive_content + - group_source + label: Tabs + region: content + parent_name: '' + weight: 0 + format_type: tabs + format_settings: + classes: '' + show_empty_fields: false + id: '' + direction: horizontal + width_breakpoint: 640 + group_interactive_content: + children: + - field_full_width + - field_title + - field_link + - field_image + - field_embed_code + - field_show_interactive_content + label: 'Interactive content' + region: content + parent_name: group_tabs + weight: 12 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: open + description: '' + required_fields: true + group_source: + children: + - field_dataset + label: Source + region: content + parent_name: group_tabs + weight: 13 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: closed + description: '' + required_fields: true id: paragraph.interactive_content.default targetEntityType: paragraph bundle: interactive_content @@ -23,7 +76,7 @@ mode: default content: field_dataset: type: gho_dataset_link - weight: 6 + weight: 7 region: content settings: placeholder_url: 'Ex: https://humdata.org/dataset' @@ -31,7 +84,7 @@ content: third_party_settings: { } field_embed_code: type: gho_interactive_content - weight: 4 + weight: 5 region: content settings: rows: 5 @@ -39,21 +92,21 @@ content: third_party_settings: { } field_full_width: type: boolean_checkbox - weight: 0 + weight: 1 region: content settings: display_label: true third_party_settings: { } field_image: type: media_library_widget - weight: 3 + weight: 4 region: content settings: media_types: { } third_party_settings: { } field_link: type: link_default - weight: 2 + weight: 3 region: content settings: placeholder_url: 'Ex: https://datawrapper.de/content-page' @@ -61,14 +114,14 @@ content: third_party_settings: { } field_show_interactive_content: type: boolean_checkbox - weight: 5 + weight: 6 region: content settings: display_label: true third_party_settings: { } field_title: type: string_textfield - weight: 1 + weight: 2 region: content settings: size: 60 @@ -76,7 +129,7 @@ content: third_party_settings: { } status: type: boolean_checkbox - weight: 7 + weight: 8 region: content settings: display_label: true diff --git a/config/core.entity_form_display.paragraph.text.default.yml b/config/core.entity_form_display.paragraph.text.default.yml index 25c405ff..5947d89e 100755 --- a/config/core.entity_form_display.paragraph.text.default.yml +++ b/config/core.entity_form_display.paragraph.text.default.yml @@ -8,7 +8,40 @@ dependencies: - paragraphs.paragraphs_type.text module: - allowed_formats + - field_group - text +third_party_settings: + field_group: + group_tabs: + children: + - group_content + label: Tabs + region: content + parent_name: '' + weight: 0 + format_type: tabs + format_settings: + classes: '' + show_empty_fields: false + id: '' + direction: horizontal + width_breakpoint: 640 + group_content: + children: + - field_text + - field_footnotes + label: Content + region: content + parent_name: group_tabs + weight: 6 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: open + description: '' + required_fields: true id: paragraph.text.default targetEntityType: paragraph bundle: text @@ -16,7 +49,7 @@ mode: default content: field_footnotes: type: text_textarea - weight: 1 + weight: 2 region: content settings: rows: 5 @@ -27,7 +60,7 @@ content: hide_guidelines: '1' field_text: type: text_textarea - weight: 0 + weight: 1 region: content settings: rows: 5 @@ -38,7 +71,7 @@ content: hide_guidelines: '1' status: type: boolean_checkbox - weight: 2 + weight: 3 region: content settings: display_label: true diff --git a/config/core.entity_form_display.paragraph.top_figures.default.yml b/config/core.entity_form_display.paragraph.top_figures.default.yml index 312acdf2..91cca9e8 100644 --- a/config/core.entity_form_display.paragraph.top_figures.default.yml +++ b/config/core.entity_form_display.paragraph.top_figures.default.yml @@ -3,18 +3,76 @@ langcode: en status: true dependencies: config: + - field.field.paragraph.top_figures.field_dataset - field.field.paragraph.top_figures.field_figures - paragraphs.paragraphs_type.top_figures module: - custom_field + - field_group + - gho_fields +third_party_settings: + field_group: + group_tabs: + children: + - group_figures + - group_source + label: Tabs + region: content + parent_name: '' + weight: 0 + format_type: tabs + format_settings: + classes: '' + show_empty_fields: false + id: '' + direction: horizontal + width_breakpoint: 640 + group_figures: + children: + - field_figures + label: Figures + region: content + parent_name: group_tabs + weight: 3 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: open + description: '' + required_fields: true + group_source: + children: + - field_dataset + label: Source + region: content + parent_name: group_tabs + weight: 4 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: closed + description: '' + required_fields: true id: paragraph.top_figures.default targetEntityType: paragraph bundle: top_figures mode: default content: + field_dataset: + type: gho_dataset_link + weight: 1 + region: content + settings: + placeholder_url: 'https://example.com/link-to-data' + placeholder_title: 'Ex: Office for the Coordination of Humanitarian Affairs' + third_party_settings: { } field_figures: type: custom_flex - weight: 0 + weight: 2 region: content settings: label: false @@ -28,7 +86,7 @@ content: third_party_settings: { } status: type: boolean_checkbox - weight: 1 + weight: 2 region: content settings: display_label: true diff --git a/config/core.entity_form_display.paragraph.top_figures_small.default.yml b/config/core.entity_form_display.paragraph.top_figures_small.default.yml index 7ca4fa33..b1e57d9d 100644 --- a/config/core.entity_form_display.paragraph.top_figures_small.default.yml +++ b/config/core.entity_form_display.paragraph.top_figures_small.default.yml @@ -3,18 +3,76 @@ langcode: en status: true dependencies: config: + - field.field.paragraph.top_figures_small.field_dataset - field.field.paragraph.top_figures_small.field_figures - paragraphs.paragraphs_type.top_figures_small module: - custom_field + - field_group + - gho_fields +third_party_settings: + field_group: + group_tabs: + children: + - group_figures + - group_source + label: Tabs + region: content + parent_name: '' + weight: 0 + format_type: tabs + format_settings: + classes: '' + show_empty_fields: false + id: '' + direction: horizontal + width_breakpoint: 640 + group_figures: + children: + - field_figures + label: Figures + region: content + parent_name: group_tabs + weight: 5 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: open + description: '' + required_fields: true + group_source: + children: + - field_dataset + label: Source + region: content + parent_name: group_tabs + weight: 6 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: closed + description: '' + required_fields: true id: paragraph.top_figures_small.default targetEntityType: paragraph bundle: top_figures_small mode: default content: + field_dataset: + type: gho_dataset_link + weight: 3 + region: content + settings: + placeholder_url: 'https://example.com/link-to-data' + placeholder_title: 'Ex: Office for the Coordination of Humanitarian Affairs' + third_party_settings: { } field_figures: type: custom_flex - weight: 0 + weight: 1 region: content settings: label: false @@ -28,7 +86,7 @@ content: third_party_settings: { } status: type: boolean_checkbox - weight: 1 + weight: 2 region: content settings: display_label: true diff --git a/config/core.entity_view_display.paragraph.top_figures.default.yml b/config/core.entity_view_display.paragraph.top_figures.default.yml index baeec5b2..2872922b 100644 --- a/config/core.entity_view_display.paragraph.top_figures.default.yml +++ b/config/core.entity_view_display.paragraph.top_figures.default.yml @@ -3,6 +3,7 @@ langcode: en status: true dependencies: config: + - field.field.paragraph.top_figures.field_dataset - field.field.paragraph.top_figures.field_figures - paragraphs.paragraphs_type.top_figures module: @@ -12,6 +13,13 @@ targetEntityType: paragraph bundle: top_figures mode: default content: + field_dataset: + type: gho_dataset_link + label: hidden + settings: { } + third_party_settings: { } + weight: 1 + region: content field_figures: type: gho_figures label: hidden diff --git a/config/core.entity_view_display.paragraph.top_figures_small.default.yml b/config/core.entity_view_display.paragraph.top_figures_small.default.yml index d15197b2..6d1236c7 100644 --- a/config/core.entity_view_display.paragraph.top_figures_small.default.yml +++ b/config/core.entity_view_display.paragraph.top_figures_small.default.yml @@ -3,6 +3,7 @@ langcode: en status: true dependencies: config: + - field.field.paragraph.top_figures_small.field_dataset - field.field.paragraph.top_figures_small.field_figures - paragraphs.paragraphs_type.top_figures_small module: @@ -12,6 +13,13 @@ targetEntityType: paragraph bundle: top_figures_small mode: default content: + field_dataset: + type: gho_dataset_link + label: hidden + settings: { } + third_party_settings: { } + weight: 1 + region: content field_figures: type: gho_figures label: hidden diff --git a/config/field.field.node.article.field_paragraphs.yml b/config/field.field.node.article.field_paragraphs.yml index c12a1457..ab32fee4 100755 --- a/config/field.field.node.article.field_paragraphs.yml +++ b/config/field.field.node.article.field_paragraphs.yml @@ -7,6 +7,8 @@ dependencies: - node.type.article - paragraphs.paragraphs_type.achievement - paragraphs.paragraphs_type.article_card_list + - paragraphs.paragraphs_type.document_articles + - paragraphs.paragraphs_type.document_chapter module: - entity_reference_revisions - field_config_cardinality @@ -33,6 +35,8 @@ settings: target_bundles: achievement: achievement article_card_list: article_card_list + document_articles: document_articles + document_chapter: document_chapter negate: 1 target_bundles_drag_drop: achievement: @@ -52,10 +56,10 @@ settings: enabled: false document_articles: weight: -31 - enabled: false + enabled: true document_chapter: weight: -29 - enabled: false + enabled: true download_button: weight: -32 enabled: false diff --git a/config/field.field.paragraph.top_figures.field_dataset.yml b/config/field.field.paragraph.top_figures.field_dataset.yml new file mode 100644 index 00000000..e0624529 --- /dev/null +++ b/config/field.field.paragraph.top_figures.field_dataset.yml @@ -0,0 +1,31 @@ +uuid: 45ec8d00-c54d-4a33-89bf-b77225c91b7d +langcode: en +status: true +dependencies: + config: + - field.storage.paragraph.field_dataset + - paragraphs.paragraphs_type.top_figures + module: + - field_config_cardinality + - link +third_party_settings: + field_config_cardinality: + cardinality_config: '1' + cardinality_label_config: false + unlimited_not_required: '' + limited_not_required: '' + limited_required: '' +id: paragraph.top_figures.field_dataset +field_name: field_dataset +entity_type: paragraph +bundle: top_figures +label: Source +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + title: 2 + link_type: 16 +field_type: link diff --git a/config/field.field.paragraph.top_figures_small.field_dataset.yml b/config/field.field.paragraph.top_figures_small.field_dataset.yml new file mode 100644 index 00000000..504d73c4 --- /dev/null +++ b/config/field.field.paragraph.top_figures_small.field_dataset.yml @@ -0,0 +1,31 @@ +uuid: 65ea473d-f3d1-4de7-aa82-26fbea17572d +langcode: en +status: true +dependencies: + config: + - field.storage.paragraph.field_dataset + - paragraphs.paragraphs_type.top_figures_small + module: + - field_config_cardinality + - link +third_party_settings: + field_config_cardinality: + cardinality_config: '1' + cardinality_label_config: false + unlimited_not_required: '' + limited_not_required: '' + limited_required: '' +id: paragraph.top_figures_small.field_dataset +field_name: field_dataset +entity_type: paragraph +bundle: top_figures_small +label: Source +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + title: 2 + link_type: 16 +field_type: link diff --git a/config/paragraphs.paragraphs_type.photo_gallery.yml b/config/paragraphs.paragraphs_type.photo_gallery.yml index ff3eff6a..25a6d228 100755 --- a/config/paragraphs.paragraphs_type.photo_gallery.yml +++ b/config/paragraphs.paragraphs_type.photo_gallery.yml @@ -1,18 +1,10 @@ uuid: c5e5598e-d4e8-4eb7-a97a-79a1569b4030 langcode: en status: true -dependencies: - module: - - layout_paragraphs - - ncms_publisher +dependencies: { } id: photo_gallery label: 'Photo Gallery' icon_uuid: null icon_default: null description: 'A photo gallery with up to 4 images.' -behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } - promoted_behavior: - enabled: true +behavior_plugins: { } diff --git a/config/paragraphs.paragraphs_type.story.yml b/config/paragraphs.paragraphs_type.story.yml index de9909c9..71688ee7 100755 --- a/config/paragraphs.paragraphs_type.story.yml +++ b/config/paragraphs.paragraphs_type.story.yml @@ -1,18 +1,10 @@ uuid: e4c10110-fb80-4288-91ec-f08bdc4b0beb langcode: en status: true -dependencies: - module: - - layout_paragraphs - - ncms_publisher +dependencies: { } id: story label: Story icon_uuid: null icon_default: null description: 'A story (ex: a story from the field).' -behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } - promoted_behavior: - enabled: true +behavior_plugins: { } diff --git a/config/paragraphs.paragraphs_type.sub_article.yml b/config/paragraphs.paragraphs_type.sub_article.yml index b3d57b2e..b0384668 100755 --- a/config/paragraphs.paragraphs_type.sub_article.yml +++ b/config/paragraphs.paragraphs_type.sub_article.yml @@ -1,18 +1,10 @@ uuid: aad0adc9-ea9e-44ae-8690-5eaaf50002d3 langcode: en status: true -dependencies: - module: - - layout_paragraphs - - ncms_publisher +dependencies: { } id: sub_article label: Sub-article icon_uuid: null icon_default: null description: 'A paragraph showing a sub-article.' -behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } - promoted_behavior: - enabled: true +behavior_plugins: { } diff --git a/config/views.view.trash_nodes.yml b/config/views.view.trash_nodes.yml index 483aad6b..02c81010 100644 --- a/config/views.view.trash_nodes.yml +++ b/config/views.view.trash_nodes.yml @@ -719,6 +719,7 @@ display: - 'languages:language_interface' - url - url.query_args + - user - 'user.node_grants:view' - user.permissions tags: @@ -763,6 +764,7 @@ display: - 'languages:language_interface' - url - url.query_args + - user - 'user.node_grants:view' - user.permissions tags: diff --git a/html/modules/custom/ncms_publisher/src/Plugin/paragraphs/Behavior/PromoteBehavior.php b/html/modules/custom/ncms_publisher/src/Plugin/paragraphs/Behavior/PromoteBehavior.php index 0613fe6a..c856cb8f 100644 --- a/html/modules/custom/ncms_publisher/src/Plugin/paragraphs/Behavior/PromoteBehavior.php +++ b/html/modules/custom/ncms_publisher/src/Plugin/paragraphs/Behavior/PromoteBehavior.php @@ -37,6 +37,7 @@ public function buildBehaviorForm(ParagraphInterface $paragraph, array &$form, F $form['promoted'] = [ '#type' => 'checkbox', '#title' => $this->t('Promoted'), + '#description' => $this->t('If this is checked, this paragraph will be marked as promoted. This allows external consumers to present this content differently.'), '#default_value' => $paragraph->getBehaviorSetting($this->getPluginId(), 'promoted', FALSE), ]; return $form; diff --git a/html/modules/custom/ncms_ui/css/edit-form/paragraph_edit_form.css b/html/modules/custom/ncms_ui/css/edit-form/paragraph_edit_form.css new file mode 100644 index 00000000..9bcf915c --- /dev/null +++ b/html/modules/custom/ncms_ui/css/edit-form/paragraph_edit_form.css @@ -0,0 +1,5 @@ +form.layout-paragraphs-component-form > .field--name-status { + position: absolute; + bottom: 0; + z-index: 10; +} diff --git a/html/modules/custom/ncms_ui/css/frontend_styles.css b/html/modules/custom/ncms_ui/css/frontend_styles.css index 40da9ac2..53023d72 100644 --- a/html/modules/custom/ncms_ui/css/frontend_styles.css +++ b/html/modules/custom/ncms_ui/css/frontend_styles.css @@ -129,7 +129,7 @@ } /* the label, e.g. "Separator: full" */ -.field--name-field-separator-size { +.paragraph--view-mode--preview .field--name-field-separator-size { position: relative; text-align: center; z-index: 2; @@ -137,7 +137,7 @@ margin: 0 auto; background: #fcfcfa; } -.field--name-field-separator-size::before { +.paragraph--view-mode--preview .field--name-field-separator-size::before { content: 'Separator: '; } diff --git a/html/modules/custom/ncms_ui/css/ncms_gin.css b/html/modules/custom/ncms_ui/css/ncms_gin.css index 96427a2c..3da1a957 100644 --- a/html/modules/custom/ncms_ui/css/ncms_gin.css +++ b/html/modules/custom/ncms_ui/css/ncms_gin.css @@ -83,6 +83,7 @@ table { /* Improvements for the ui dialog (modal). */ .ui-dialog.lpb-dialog { width: auto !important; + min-width: 30rem !important; } .ui-dialog .ui-widget-content.ui-dialog-content { padding: 1rem; diff --git a/html/modules/custom/ncms_ui/ncms_ui.libraries.yml b/html/modules/custom/ncms_ui/ncms_ui.libraries.yml index e78b378b..fb58c851 100644 --- a/html/modules/custom/ncms_ui/ncms_ui.libraries.yml +++ b/html/modules/custom/ncms_ui/ncms_ui.libraries.yml @@ -27,6 +27,10 @@ document_edit_form: component: css/edit-form/content_edit_form.css: {} css/edit-form/document_edit_form.css: {} +paragraph_edit_form: + css: + component: + css/edit-form/paragraph_edit_form.css: {} revisions: css: component: diff --git a/html/modules/custom/ncms_ui/ncms_ui.module b/html/modules/custom/ncms_ui/ncms_ui.module index 615f6dde..1df1b0f9 100644 --- a/html/modules/custom/ncms_ui/ncms_ui.module +++ b/html/modules/custom/ncms_ui/ncms_ui.module @@ -338,8 +338,30 @@ function ncms_ui_form_node_preview_form_select_alter(&$form, FormStateInterface * Implements hook_form_FORM_ID_alter(). */ function ncms_ui_form_layout_paragraphs_component_form_alter(&$form, FormStateInterface $form_state) { - // Don't trigger "Required" messages when canceling the add/eddit form. + // Don't trigger "Required" messages when canceling the add/edit form. $form['actions']['cancel']['#limit_validation_errors'] = []; + + // Move the behavior plugin from a stand-alone fieldset into existing field + // groups. + if (!empty($form['#fieldgroups']) && !empty($form['behavior_plugins'])) { + /** @var \Drupal\layout_paragraphs\Form\EditComponentForm $form_object */ + $form_object = $form_state->getFormObject(); + $form['behavior_plugins']['#parents'] = ['behavior_plugins']; + $form['behavior_plugins'] = $form_object->behaviorPluginsForm($form['behavior_plugins'], $form_state, $form); + unset($form['behavior_plugins']['#process']); + + $group = clone end($form['#fieldgroups']); + $group->children = ['behavior_plugins']; + $group->label = t('Additional settings'); + $group->format_settings['formatter'] = 'close'; + $group->weight = $group->weight + 1; + $group->group_name = 'group_behaviors'; + $form['#fieldgroups'][$group->group_name] = $group; + $form['#group_children']['behavior_plugins'] = 'group_behaviors'; + } + + // Add our library to improve the display. + $form['#attached']['library'][] = 'ncms_ui/paragraph_edit_form'; } /** From 84987ae9b7db28a77b584a25ee9bd0c4ebf8f01f Mon Sep 17 00:00:00 2001 From: berliner Date: Mon, 22 Apr 2024 15:00:00 +0200 Subject: [PATCH 03/17] HPC-9466: Create third party settings for paragraphs, handle category placement and disabled control there, change the UI for paragraph selection to use a category-based screen, consolidate css styles for top figures to get rid of NaR at some point, mark migrated paragraphs as replacing the old ones so that HA can react to that and place the new elements automatically, some more minor changes, e.g. validate the embed code on interactive content paragraphs --- composer.patches.json | 4 + ...lay.paragraph.document_chapter.default.yml | 3 +- ....paragraph.interactive_content.default.yml | 48 +++++- config/core.extension.yml | 1 + ...h.interactive_content.field_full_width.yml | 11 +- ...agraph.interactive_content.field_image.yml | 12 +- ...raphs.paragraphs_type.achievement_list.yml | 9 +- ...aragraphs.paragraphs_type.article_list.yml | 9 +- ...aphs.paragraphs_type.bottom_figure_row.yml | 5 + ...graphs.paragraphs_type.download_button.yml | 11 +- ...aphs.paragraphs_type.facts_and_figures.yml | 9 +- ...graphs.paragraphs_type.further_reading.yml | 9 +- config/paragraphs.paragraphs_type.heading.yml | 11 +- ...graphs.paragraphs_type.image_with_text.yml | 11 +- ...paragraphs.paragraphs_type.infographic.yml | 11 +- ...hs.paragraphs_type.interactive_content.yml | 9 +- ...phs_type.interactive_content_2_columns.yml | 5 + ...ragraphs.paragraphs_type.photo_gallery.yml | 8 +- .../paragraphs.paragraphs_type.separator.yml | 11 +- config/paragraphs.paragraphs_type.story.yml | 8 +- ...paragraphs.paragraphs_type.sub_article.yml | 8 +- config/paragraphs.paragraphs_type.text.yml | 9 +- ...paragraphs.paragraphs_type.top_figures.yml | 5 + ...aphs.paragraphs_type.top_figures_small.yml | 5 + .../custom/gho_fields/gho_fields.deploy.php | 4 + .../DataProducer/EntityConfiguration.php | 3 + .../config/schema/ncms_paragraphs.schema.yml | 13 ++ .../ncms_paragraphs/css/component_menu.css | 16 ++ .../css/layout_paragraphs.css | 0 .../css/paragraph_edit_form.css | 41 +++++ .../ncms_paragraphs/ncms_paragraphs.info.yml | 8 + .../ncms_paragraphs.libraries.yml | 12 ++ .../ncms_paragraphs/ncms_paragraphs.module | 111 +++++++++++++ .../ncms_paragraphs.services.yml | 7 + .../ncms_paragraphs/src/ComponentMenu.php | 156 ++++++++++++++++++ .../src/Entity/NcmsParagraphBase.php | 18 ++ .../src/Entity/NcmsParagraphInterface.php | 18 ++ .../Entity/Paragraph/InteractiveContent.php | 36 ++++ .../src/Helpers/StringHelper.php | 34 ++++ .../src/ParagraphsTypeSettings.php | 87 ++++++++++ .../src/Traits/VerticalTabsTrait.php | 96 +++++++++++ ...aragraphs-builder-component-menu.html.twig | 15 ++ .../css/edit-form/paragraph_edit_form.css | 5 - .../custom/ncms_ui/css/gin_entity_browser.css | 7 + .../custom/ncms_ui/ncms_ui.libraries.yml | 4 - html/modules/custom/ncms_ui/ncms_ui.module | 50 ------ .../common_design_subtheme.libraries.yml | 1 + .../components/gho-top-figures/README.md | 5 + .../gho-top-figures--small.css | 78 +++++++++ .../gho-top-figures/gho-top-figures.css | 120 +++++++------- .../gho-figures-formatter--large.html.twig | 7 +- ...field-dataset--top-figures-small.html.twig | 5 + ...raph--field-dataset--top-figures.html.twig | 5 + .../paragraph--top-figures-small.html.twig | 1 + .../paragraph--top-figures.html.twig | 4 +- 55 files changed, 1013 insertions(+), 186 deletions(-) create mode 100644 html/modules/custom/ncms_paragraphs/config/schema/ncms_paragraphs.schema.yml create mode 100644 html/modules/custom/ncms_paragraphs/css/component_menu.css rename html/modules/custom/{ncms_ui => ncms_paragraphs}/css/layout_paragraphs.css (100%) create mode 100644 html/modules/custom/ncms_paragraphs/css/paragraph_edit_form.css create mode 100644 html/modules/custom/ncms_paragraphs/ncms_paragraphs.info.yml create mode 100644 html/modules/custom/ncms_paragraphs/ncms_paragraphs.libraries.yml create mode 100644 html/modules/custom/ncms_paragraphs/ncms_paragraphs.module create mode 100644 html/modules/custom/ncms_paragraphs/ncms_paragraphs.services.yml create mode 100644 html/modules/custom/ncms_paragraphs/src/ComponentMenu.php create mode 100644 html/modules/custom/ncms_paragraphs/src/Entity/NcmsParagraphBase.php create mode 100644 html/modules/custom/ncms_paragraphs/src/Entity/NcmsParagraphInterface.php create mode 100644 html/modules/custom/ncms_paragraphs/src/Entity/Paragraph/InteractiveContent.php create mode 100644 html/modules/custom/ncms_paragraphs/src/Helpers/StringHelper.php create mode 100644 html/modules/custom/ncms_paragraphs/src/ParagraphsTypeSettings.php create mode 100644 html/modules/custom/ncms_paragraphs/src/Traits/VerticalTabsTrait.php create mode 100644 html/modules/custom/ncms_paragraphs/templates/layout-paragraphs-builder-component-menu.html.twig delete mode 100644 html/modules/custom/ncms_ui/css/edit-form/paragraph_edit_form.css create mode 100644 html/themes/custom/common_design_subtheme/components/gho-top-figures/gho-top-figures--small.css create mode 100644 html/themes/custom/common_design_subtheme/templates/fields/field--paragraph--field-dataset--top-figures-small.html.twig create mode 100644 html/themes/custom/common_design_subtheme/templates/fields/field--paragraph--field-dataset--top-figures.html.twig diff --git a/composer.patches.json b/composer.patches.json index 2f132011..5401ef19 100644 --- a/composer.patches.json +++ b/composer.patches.json @@ -25,6 +25,10 @@ "drupal/gin": { "Issue #3342513: Ajax behaviour on entity edit form action buttons can break": "https://www.drupal.org/files/issues/2024-02-02/3342513-entity-edit-form-update-ajax-ids-12.patch" }, + "drupal/layout_paragraphs": { + "Issue #3265794: Modal form actions broken if subform has validation errors or certain AJAX callbacks": "https://git.drupalcode.org/project/layout_paragraphs/-/commit/9e6ce29756e7a44d1644f77a9606d3b598f67b5d.patch", + "Issue #3442062: Make selector less greedy, only look at the form actions": "https://git.drupalcode.org/project/layout_paragraphs/-/merge_requests/160.patch" + }, "drupal/maintenance200": { "Issue #3288424: Automated Drupal 10 compatibility fixes": "./patches/maintenance200-drupal-10-compatibility.patch" }, diff --git a/config/core.entity_form_display.paragraph.document_chapter.default.yml b/config/core.entity_form_display.paragraph.document_chapter.default.yml index e62db610..68f46b8f 100644 --- a/config/core.entity_form_display.paragraph.document_chapter.default.yml +++ b/config/core.entity_form_display.paragraph.document_chapter.default.yml @@ -51,7 +51,6 @@ third_party_settings: - group_title - field_summary - field_tags - - status - field_hide_from_navigation label: 'Chapter properties' region: content @@ -149,7 +148,7 @@ content: third_party_settings: { } status: type: boolean_checkbox - weight: 7 + weight: 1 region: content settings: display_label: true diff --git a/config/core.entity_form_display.paragraph.interactive_content.default.yml b/config/core.entity_form_display.paragraph.interactive_content.default.yml index 122e0eb3..90ca4278 100755 --- a/config/core.entity_form_display.paragraph.interactive_content.default.yml +++ b/config/core.entity_form_display.paragraph.interactive_content.default.yml @@ -22,7 +22,9 @@ third_party_settings: group_tabs: children: - group_interactive_content + - group_fallback_image - group_source + - group_settings label: Tabs region: content parent_name: '' @@ -36,12 +38,9 @@ third_party_settings: width_breakpoint: 640 group_interactive_content: children: - - field_full_width - field_title - field_link - - field_image - field_embed_code - - field_show_interactive_content label: 'Interactive content' region: content parent_name: group_tabs @@ -60,6 +59,22 @@ third_party_settings: label: Source region: content parent_name: group_tabs + weight: 14 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: closed + description: '' + required_fields: true + group_fallback_image: + children: + - field_show_interactive_content + - field_image + label: 'Fallback image' + region: content + parent_name: group_tabs weight: 13 format_type: tab format_settings: @@ -69,6 +84,21 @@ third_party_settings: formatter: closed description: '' required_fields: true + group_settings: + children: + - field_full_width + label: 'Additional settings' + region: content + parent_name: group_tabs + weight: 15 + format_type: tab + format_settings: + classes: '' + show_empty_fields: false + id: '' + formatter: closed + description: '' + required_fields: true id: paragraph.interactive_content.default targetEntityType: paragraph bundle: interactive_content @@ -76,7 +106,7 @@ mode: default content: field_dataset: type: gho_dataset_link - weight: 7 + weight: 14 region: content settings: placeholder_url: 'Ex: https://humdata.org/dataset' @@ -84,7 +114,7 @@ content: third_party_settings: { } field_embed_code: type: gho_interactive_content - weight: 5 + weight: 4 region: content settings: rows: 5 @@ -92,14 +122,14 @@ content: third_party_settings: { } field_full_width: type: boolean_checkbox - weight: 1 + weight: 15 region: content settings: display_label: true third_party_settings: { } field_image: type: media_library_widget - weight: 4 + weight: 6 region: content settings: media_types: { } @@ -114,7 +144,7 @@ content: third_party_settings: { } field_show_interactive_content: type: boolean_checkbox - weight: 6 + weight: 5 region: content settings: display_label: true @@ -129,7 +159,7 @@ content: third_party_settings: { } status: type: boolean_checkbox - weight: 8 + weight: 1 region: content settings: display_label: true diff --git a/config/core.extension.yml b/config/core.extension.yml index 92f238f3..b3d78eeb 100755 --- a/config/core.extension.yml +++ b/config/core.extension.yml @@ -72,6 +72,7 @@ module: monitoring: 0 mysql: 0 ncms_graphql: 0 + ncms_paragraphs: 0 ncms_publisher: 0 ncms_ui: 0 node: 0 diff --git a/config/field.field.paragraph.interactive_content.field_full_width.yml b/config/field.field.paragraph.interactive_content.field_full_width.yml index 896c58ef..13d039ff 100755 --- a/config/field.field.paragraph.interactive_content.field_full_width.yml +++ b/config/field.field.paragraph.interactive_content.field_full_width.yml @@ -5,12 +5,21 @@ dependencies: config: - field.storage.paragraph.field_full_width - paragraphs.paragraphs_type.interactive_content + module: + - field_config_cardinality +third_party_settings: + field_config_cardinality: + cardinality_config: '1' + cardinality_label_config: false + unlimited_not_required: '' + limited_not_required: '' + limited_required: '' id: paragraph.interactive_content.field_full_width field_name: field_full_width entity_type: paragraph bundle: interactive_content label: 'Display at full width?' -description: '' +description: 'If checked, the interactive content will be shown at full-width. Otherwise it will be restricted to same width as the surrounding text content.' required: false translatable: true default_value: diff --git a/config/field.field.paragraph.interactive_content.field_image.yml b/config/field.field.paragraph.interactive_content.field_image.yml index e0f1b3e6..ee2f6169 100755 --- a/config/field.field.paragraph.interactive_content.field_image.yml +++ b/config/field.field.paragraph.interactive_content.field_image.yml @@ -6,11 +6,20 @@ dependencies: - field.storage.paragraph.field_image - media.type.image - paragraphs.paragraphs_type.interactive_content + module: + - field_config_cardinality +third_party_settings: + field_config_cardinality: + cardinality_config: '1' + cardinality_label_config: false + unlimited_not_required: '' + limited_not_required: '' + limited_required: '' id: paragraph.interactive_content.field_image field_name: field_image entity_type: paragraph bundle: interactive_content -label: Thumbnail +label: 'Fallback image' description: "Image representing the content. When clicked it will redirect to the interactive content's page." required: false translatable: true @@ -23,6 +32,7 @@ settings: image: image sort: field: _none + direction: ASC auto_create: false auto_create_bundle: '' field_type: entity_reference diff --git a/config/paragraphs.paragraphs_type.achievement_list.yml b/config/paragraphs.paragraphs_type.achievement_list.yml index 3a6e5020..e61d80de 100755 --- a/config/paragraphs.paragraphs_type.achievement_list.yml +++ b/config/paragraphs.paragraphs_type.achievement_list.yml @@ -3,16 +3,17 @@ langcode: en status: true dependencies: module: - - layout_paragraphs + - ncms_paragraphs - ncms_publisher +third_party_settings: + ncms_paragraphs: + category: list + disabled: 0 id: achievement_list label: 'Achievement list' icon_uuid: null icon_default: null description: 'A list of Achievements with an optional caption.' behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } promoted_behavior: enabled: true diff --git a/config/paragraphs.paragraphs_type.article_list.yml b/config/paragraphs.paragraphs_type.article_list.yml index 576e365f..de997bff 100755 --- a/config/paragraphs.paragraphs_type.article_list.yml +++ b/config/paragraphs.paragraphs_type.article_list.yml @@ -3,16 +3,17 @@ langcode: en status: true dependencies: module: - - layout_paragraphs + - ncms_paragraphs - ncms_publisher +third_party_settings: + ncms_paragraphs: + category: list + disabled: 0 id: article_list label: 'Article list' icon_uuid: null icon_default: null description: 'A list of links to articles with an optional title.' behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } promoted_behavior: enabled: true diff --git a/config/paragraphs.paragraphs_type.bottom_figure_row.yml b/config/paragraphs.paragraphs_type.bottom_figure_row.yml index 34ee6cf8..13df9b4d 100755 --- a/config/paragraphs.paragraphs_type.bottom_figure_row.yml +++ b/config/paragraphs.paragraphs_type.bottom_figure_row.yml @@ -3,8 +3,13 @@ langcode: en status: true dependencies: module: + - ncms_paragraphs - ncms_publisher - paragraphs_viewmode +third_party_settings: + ncms_paragraphs: + category: content + disabled: 1 id: bottom_figure_row label: 'Bottom figure row (deprecated)' icon_uuid: null diff --git a/config/paragraphs.paragraphs_type.download_button.yml b/config/paragraphs.paragraphs_type.download_button.yml index fdba0d77..853c4702 100755 --- a/config/paragraphs.paragraphs_type.download_button.yml +++ b/config/paragraphs.paragraphs_type.download_button.yml @@ -3,13 +3,14 @@ langcode: en status: true dependencies: module: - - layout_paragraphs + - ncms_paragraphs +third_party_settings: + ncms_paragraphs: + category: misc + disabled: 0 id: download_button label: 'Download button' icon_uuid: null icon_default: null description: 'A button that points to a document somewhere on the internet.' -behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } +behavior_plugins: { } diff --git a/config/paragraphs.paragraphs_type.facts_and_figures.yml b/config/paragraphs.paragraphs_type.facts_and_figures.yml index a0f79857..fbb318b0 100755 --- a/config/paragraphs.paragraphs_type.facts_and_figures.yml +++ b/config/paragraphs.paragraphs_type.facts_and_figures.yml @@ -3,16 +3,17 @@ langcode: en status: true dependencies: module: - - layout_paragraphs + - ncms_paragraphs - ncms_publisher +third_party_settings: + ncms_paragraphs: + category: list + disabled: 0 id: facts_and_figures label: 'Facts and figures' icon_uuid: null icon_default: null description: 'A list of facts and figures composed of images with a text.' behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } promoted_behavior: enabled: true diff --git a/config/paragraphs.paragraphs_type.further_reading.yml b/config/paragraphs.paragraphs_type.further_reading.yml index fdc2d6e6..722c207e 100755 --- a/config/paragraphs.paragraphs_type.further_reading.yml +++ b/config/paragraphs.paragraphs_type.further_reading.yml @@ -3,16 +3,17 @@ langcode: en status: true dependencies: module: - - layout_paragraphs + - ncms_paragraphs - ncms_publisher +third_party_settings: + ncms_paragraphs: + category: misc + disabled: false id: further_reading label: 'Further reading' icon_uuid: null icon_default: null description: 'A list of links to futher information related to the topics of the article.' behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } promoted_behavior: enabled: true diff --git a/config/paragraphs.paragraphs_type.heading.yml b/config/paragraphs.paragraphs_type.heading.yml index 385be458..bc41c0e3 100755 --- a/config/paragraphs.paragraphs_type.heading.yml +++ b/config/paragraphs.paragraphs_type.heading.yml @@ -3,13 +3,14 @@ langcode: en status: true dependencies: module: - - layout_paragraphs + - ncms_paragraphs +third_party_settings: + ncms_paragraphs: + category: content + disabled: 0 id: heading label: Heading icon_uuid: null icon_default: null description: 'A generic heading.' -behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } +behavior_plugins: { } diff --git a/config/paragraphs.paragraphs_type.image_with_text.yml b/config/paragraphs.paragraphs_type.image_with_text.yml index 4635d01f..4c592163 100755 --- a/config/paragraphs.paragraphs_type.image_with_text.yml +++ b/config/paragraphs.paragraphs_type.image_with_text.yml @@ -3,13 +3,14 @@ langcode: en status: true dependencies: module: - - layout_paragraphs + - ncms_paragraphs +third_party_settings: + ncms_paragraphs: + category: media + disabled: 0 id: image_with_text label: 'Image with text' icon_uuid: null icon_default: null description: 'An image with a text.' -behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } +behavior_plugins: { } diff --git a/config/paragraphs.paragraphs_type.infographic.yml b/config/paragraphs.paragraphs_type.infographic.yml index 2bca4b61..d3ba2c73 100644 --- a/config/paragraphs.paragraphs_type.infographic.yml +++ b/config/paragraphs.paragraphs_type.infographic.yml @@ -3,13 +3,14 @@ langcode: en status: true dependencies: module: - - layout_paragraphs + - ncms_paragraphs +third_party_settings: + ncms_paragraphs: + category: media + disabled: 0 id: infographic label: Infographic icon_uuid: null icon_default: null description: 'An infographic media' -behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } +behavior_plugins: { } diff --git a/config/paragraphs.paragraphs_type.interactive_content.yml b/config/paragraphs.paragraphs_type.interactive_content.yml index fd57a7db..b3418e61 100755 --- a/config/paragraphs.paragraphs_type.interactive_content.yml +++ b/config/paragraphs.paragraphs_type.interactive_content.yml @@ -3,16 +3,17 @@ langcode: en status: true dependencies: module: - - layout_paragraphs + - ncms_paragraphs - ncms_publisher +third_party_settings: + ncms_paragraphs: + category: widget + disabled: 0 id: interactive_content label: 'Interactive content' icon_uuid: null icon_default: null description: 'Interactive content like an infographic, a map or a table.' behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } promoted_behavior: enabled: true diff --git a/config/paragraphs.paragraphs_type.interactive_content_2_columns.yml b/config/paragraphs.paragraphs_type.interactive_content_2_columns.yml index cafdf715..0238e291 100644 --- a/config/paragraphs.paragraphs_type.interactive_content_2_columns.yml +++ b/config/paragraphs.paragraphs_type.interactive_content_2_columns.yml @@ -4,6 +4,11 @@ status: true dependencies: module: - layout_paragraphs + - ncms_paragraphs +third_party_settings: + ncms_paragraphs: + category: widget + disabled: 0 id: interactive_content_2_columns label: 'Interactive content (2 columns)' icon_uuid: null diff --git a/config/paragraphs.paragraphs_type.photo_gallery.yml b/config/paragraphs.paragraphs_type.photo_gallery.yml index 25a6d228..c56ae89d 100755 --- a/config/paragraphs.paragraphs_type.photo_gallery.yml +++ b/config/paragraphs.paragraphs_type.photo_gallery.yml @@ -1,7 +1,13 @@ uuid: c5e5598e-d4e8-4eb7-a97a-79a1569b4030 langcode: en status: true -dependencies: { } +dependencies: + module: + - ncms_paragraphs +third_party_settings: + ncms_paragraphs: + category: media + disabled: 0 id: photo_gallery label: 'Photo Gallery' icon_uuid: null diff --git a/config/paragraphs.paragraphs_type.separator.yml b/config/paragraphs.paragraphs_type.separator.yml index a061d9cf..94917d32 100755 --- a/config/paragraphs.paragraphs_type.separator.yml +++ b/config/paragraphs.paragraphs_type.separator.yml @@ -3,13 +3,14 @@ langcode: en status: true dependencies: module: - - layout_paragraphs + - ncms_paragraphs +third_party_settings: + ncms_paragraphs: + category: misc + disabled: 0 id: separator label: Separator icon_uuid: null icon_default: null description: 'A horizontal rule separating two sections of content.' -behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } +behavior_plugins: { } diff --git a/config/paragraphs.paragraphs_type.story.yml b/config/paragraphs.paragraphs_type.story.yml index 71688ee7..8aeaec41 100755 --- a/config/paragraphs.paragraphs_type.story.yml +++ b/config/paragraphs.paragraphs_type.story.yml @@ -1,7 +1,13 @@ uuid: e4c10110-fb80-4288-91ec-f08bdc4b0beb langcode: en status: true -dependencies: { } +dependencies: + module: + - ncms_paragraphs +third_party_settings: + ncms_paragraphs: + category: embed + disabled: false id: story label: Story icon_uuid: null diff --git a/config/paragraphs.paragraphs_type.sub_article.yml b/config/paragraphs.paragraphs_type.sub_article.yml index b0384668..4bcd8fb3 100755 --- a/config/paragraphs.paragraphs_type.sub_article.yml +++ b/config/paragraphs.paragraphs_type.sub_article.yml @@ -1,7 +1,13 @@ uuid: aad0adc9-ea9e-44ae-8690-5eaaf50002d3 langcode: en status: true -dependencies: { } +dependencies: + module: + - ncms_paragraphs +third_party_settings: + ncms_paragraphs: + category: embed + disabled: false id: sub_article label: Sub-article icon_uuid: null diff --git a/config/paragraphs.paragraphs_type.text.yml b/config/paragraphs.paragraphs_type.text.yml index 7235869a..dc653dce 100755 --- a/config/paragraphs.paragraphs_type.text.yml +++ b/config/paragraphs.paragraphs_type.text.yml @@ -3,16 +3,17 @@ langcode: en status: true dependencies: module: - - layout_paragraphs + - ncms_paragraphs - ncms_publisher +third_party_settings: + ncms_paragraphs: + category: content + disabled: 0 id: text label: Text icon_uuid: null icon_default: null description: 'A text with a rich editor to allow basic formatting like bold, italic, list and links.' behavior_plugins: - layout_paragraphs: - enabled: false - available_layouts: { } promoted_behavior: enabled: true diff --git a/config/paragraphs.paragraphs_type.top_figures.yml b/config/paragraphs.paragraphs_type.top_figures.yml index 52ecda8d..91534569 100644 --- a/config/paragraphs.paragraphs_type.top_figures.yml +++ b/config/paragraphs.paragraphs_type.top_figures.yml @@ -3,7 +3,12 @@ langcode: en status: true dependencies: module: + - ncms_paragraphs - ncms_publisher +third_party_settings: + ncms_paragraphs: + category: content + disabled: 0 id: top_figures label: 'Top figures' icon_uuid: null diff --git a/config/paragraphs.paragraphs_type.top_figures_small.yml b/config/paragraphs.paragraphs_type.top_figures_small.yml index 0a893aa5..6bd04794 100644 --- a/config/paragraphs.paragraphs_type.top_figures_small.yml +++ b/config/paragraphs.paragraphs_type.top_figures_small.yml @@ -3,7 +3,12 @@ langcode: en status: true dependencies: module: + - ncms_paragraphs - ncms_publisher +third_party_settings: + ncms_paragraphs: + category: content + disabled: 0 id: top_figures_small label: 'Top figures (small)' icon_uuid: null diff --git a/html/modules/custom/gho_fields/gho_fields.deploy.php b/html/modules/custom/gho_fields/gho_fields.deploy.php index 36700bc7..2c9067d5 100644 --- a/html/modules/custom/gho_fields/gho_fields.deploy.php +++ b/html/modules/custom/gho_fields/gho_fields.deploy.php @@ -169,8 +169,12 @@ function ghi_fields_create_top_figures_from_paragraph(ParagraphInterface $paragr 'parent_type' => $paragraph->get('parent_type')->value, 'parent_field_name' => $paragraph->get('parent_field_name')->value, 'field_figures' => $paragraph->get('field_figures')->getValue(), + 'field_dataset' => $paragraph->get('field_dataset')->getValue(), ]); $top_figures_paragraph->setBehaviorSettings('promoted_behavior', $paragraph->getAllBehaviorSettings()['promoted_behavior'] ?? []); + $top_figures_paragraph->setBehaviorSettings('ncms_paragraphs', [ + 'replaces' => $paragraph->uuid(), + ]); $top_figures_paragraph->setSyncing(TRUE); $top_figures_paragraph->save(); diff --git a/html/modules/custom/ncms_graphql/src/Plugin/GraphQL/DataProducer/EntityConfiguration.php b/html/modules/custom/ncms_graphql/src/Plugin/GraphQL/DataProducer/EntityConfiguration.php index 59b5943b..c4752612 100644 --- a/html/modules/custom/ncms_graphql/src/Plugin/GraphQL/DataProducer/EntityConfiguration.php +++ b/html/modules/custom/ncms_graphql/src/Plugin/GraphQL/DataProducer/EntityConfiguration.php @@ -124,6 +124,9 @@ public function resolve(EntityInterface $entity, RefinableCacheableDependencyInt ]; } } + if ($original_uuid = $entity->getBehaviorSetting('ncms_paragraphs', 'replaces')) { + $config['replaces'] = $original_uuid; + } } return Yaml::encode($this->mapObjectsToString($config)); }); diff --git a/html/modules/custom/ncms_paragraphs/config/schema/ncms_paragraphs.schema.yml b/html/modules/custom/ncms_paragraphs/config/schema/ncms_paragraphs.schema.yml new file mode 100644 index 00000000..cc7d890f --- /dev/null +++ b/html/modules/custom/ncms_paragraphs/config/schema/ncms_paragraphs.schema.yml @@ -0,0 +1,13 @@ +paragraphs.paragraphs_type.*.third_party.ncms_paragraphs: + type: config_entity + label: "Paragraph settings" + mapping: + category: + type: text + label: "Category" + disabled: + type: boolean + label: "Disabled" + replaces: + type: text + label: "Replaces (UUID)" \ No newline at end of file diff --git a/html/modules/custom/ncms_paragraphs/css/component_menu.css b/html/modules/custom/ncms_paragraphs/css/component_menu.css new file mode 100644 index 00000000..b0157667 --- /dev/null +++ b/html/modules/custom/ncms_paragraphs/css/component_menu.css @@ -0,0 +1,16 @@ +.lpb-component-list { + padding-top: 0; + padding-bottom: 0; +} +.lpb-component-list__group--menu { + --vertical-tabs-menu-width: 10rem; + min-width: 40rem; +} +.lpb-component-list .paragraph-category-links ul { + display: flex; + flex-direction: column; + gap: 1rem; +} +.lpb-component-list .paragraph-category-links ul li { + list-style-type: none; +} \ No newline at end of file diff --git a/html/modules/custom/ncms_ui/css/layout_paragraphs.css b/html/modules/custom/ncms_paragraphs/css/layout_paragraphs.css similarity index 100% rename from html/modules/custom/ncms_ui/css/layout_paragraphs.css rename to html/modules/custom/ncms_paragraphs/css/layout_paragraphs.css diff --git a/html/modules/custom/ncms_paragraphs/css/paragraph_edit_form.css b/html/modules/custom/ncms_paragraphs/css/paragraph_edit_form.css new file mode 100644 index 00000000..fbb8f329 --- /dev/null +++ b/html/modules/custom/ncms_paragraphs/css/paragraph_edit_form.css @@ -0,0 +1,41 @@ +/* Move the status checkbox into the bottom pane of the modal. */ +form.layout-paragraphs-component-form > .field--name-status { + position: absolute; + bottom: 0; + z-index: 10; +} + +form.layout-paragraphs-component-form table.field-multiple-table tbody td.tabledrag-cell { + vertical-align: middle; +} + +/* Give the tab content some space. */ +form.layout-paragraphs-component-form .horizontal-tabs-panes { + margin-top: 1rem; +} + +/* Adjustments for some multi value fields that. */ +form.layout-paragraphs-component-form .details-wrapper > .form-wrapper > div > .form-item--multiple .tabledrag-toggle-weight-wrapper { + margin-top: -2rem; +} +form.layout-paragraphs-component-form .details-wrapper > .form-wrapper > div > .form-item--multiple > .field-actions { + align-items: center; +} +form.layout-paragraphs-component-form .details-wrapper > .form-wrapper > div > .form-item--multiple > .field-actions > em.placeholder { + padding-left: 0.2rem; +} + +form.layout-paragraphs-component-form .field--type-custom[data-drupal-selector='edit-field-figures-wrapper'] table.field-multiple-table tbody tr td:nth-child(2) { + display: flex; + justify-content: space-between; + align-items: center; +} +form.layout-paragraphs-component-form .field--type-custom[data-drupal-selector='edit-field-figures-wrapper'] table.field-multiple-table tbody tr td:nth-child(2) input.button { + height: fit-content; +} +form.layout-paragraphs-component-form .field--type-custom[data-drupal-selector='edit-field-figures-wrapper'] table.field-multiple-table th.tabledrag-hide { + display: none; +} +form.layout-paragraphs-component-form .field--type-custom[data-drupal-selector='edit-field-figures-wrapper'] table.field-multiple-table th.tabledrag-hide[style*="none"] ~ th { + display: none; +} \ No newline at end of file diff --git a/html/modules/custom/ncms_paragraphs/ncms_paragraphs.info.yml b/html/modules/custom/ncms_paragraphs/ncms_paragraphs.info.yml new file mode 100644 index 00000000..62141ba2 --- /dev/null +++ b/html/modules/custom/ncms_paragraphs/ncms_paragraphs.info.yml @@ -0,0 +1,8 @@ +name: Better paragraph support for the HPC Content Module +type: module +description: 'Provides entity classes for paragraphs.' +package: HPC Content Module +core_version_requirement: ^9.3 || ^10 +dependencies: + - paragraphs:paragraphs + - layout_paragraphs:layout_paragraphs \ No newline at end of file diff --git a/html/modules/custom/ncms_paragraphs/ncms_paragraphs.libraries.yml b/html/modules/custom/ncms_paragraphs/ncms_paragraphs.libraries.yml new file mode 100644 index 00000000..b1272413 --- /dev/null +++ b/html/modules/custom/ncms_paragraphs/ncms_paragraphs.libraries.yml @@ -0,0 +1,12 @@ +paragraph_edit_form: + css: + component: + css/paragraph_edit_form.css: {} +layout_paragraphs: + css: + component: + css/layout_paragraphs.css: {} +component_menu: + css: + component: + css/component_menu.css: {} \ No newline at end of file diff --git a/html/modules/custom/ncms_paragraphs/ncms_paragraphs.module b/html/modules/custom/ncms_paragraphs/ncms_paragraphs.module new file mode 100644 index 00000000..d7a6be4b --- /dev/null +++ b/html/modules/custom/ncms_paragraphs/ncms_paragraphs.module @@ -0,0 +1,111 @@ +getFormObject(); + $paragraph = $form_object->getParagraph(); + + if ($paragraph instanceof NcmsParagraphInterface) { + $paragraph->entityFormAlter($form, $form_state); + } + + // Don't trigger "Required" messages when canceling the add/edit form. + $form['actions']['cancel']['#limit_validation_errors'] = []; + + // Add our library to improve the display. + $form['#attached']['library'][] = 'ncms_paragraphs/paragraph_edit_form'; + + // Move the behavior plugin from a stand-alone fieldset into existing field + // groups. + if (!empty($form['#fieldgroups']) && !empty($form['behavior_plugins'])) { + + $form['behavior_plugins']['#parents'] = ['behavior_plugins']; + $form['behavior_plugins'] = $form_object->behaviorPluginsForm($form['behavior_plugins'], $form_state, $form); + unset($form['behavior_plugins']['#process']); + + $group_name = 'group_settings'; + if (empty($form['#fieldgroups'][$group_name])) { + $group = clone end($form['#fieldgroups']); + $group->children = ['behavior_plugins']; + $group->format_settings['formatter'] = 'close'; + $group->weight = $group->weight + 1; + $group->group_name = $group_name; + $form['#fieldgroups'][$group->group_name] = $group; + } + else { + $form['#fieldgroups'][$group_name]->children[] = 'behavior_plugins'; + } + $form['#fieldgroups'][$group_name]->label = t('Additional settings'); + $form['#group_children']['behavior_plugins'] = $group_name; + } + + if (!empty($form['field_dataset'])) { + // Hide the fieldset. + $form['field_dataset']['widget'][0]['#type'] = 'container'; + } + +} + +/** + * Implements hook_preprocess_layout_paragraphs_builder(). + */ +function ncms_paragraphs_preprocess_layout_paragraphs_builder(&$variables) { + $variables['#attached']['library'][] = 'ncms_paragraphs/layout_paragraphs'; +} + +/** + * Implements hook_layout_paragraphs_builder_component_menu(). + */ +function ncms_paragraphs_preprocess_layout_paragraphs_builder_component_menu(&$variables) { + /** @var \Drupal\ncms_paragraphs\ComponentMenu $component_menu */ + $component_menu = \Drupal::service('ncms_paragraphs.component_menu'); + $component_menu->preprocessTemplate($variables); +} + +/** + * Implements hook_theme_registry_alter(). + */ +function ncms_paragraphs_theme_registry_alter(&$theme_registry) { + // Override layout-paragraphs-builder-component-menu.html.twig to support + // the changes in \Drupal\ncms_paragraphs\ComponentMenu::preprocessTemplate. + $module_path = \Drupal::service('extension.path.resolver')->getPath('module', 'ncms_paragraphs'); + $theme_registry['layout_paragraphs_builder_component_menu']['theme path'] = $module_path; + $theme_registry['layout_paragraphs_builder_component_menu']['path'] = $module_path . '/templates'; +} + +/** + * Implements hook_form_FORM_ID_alter(). + * + * Implements our third-party settings into the paragraphs type form. + */ +function ncms_paragraphs_form_paragraphs_type_edit_form_alter(&$form, FormStateInterface $form_state) { + /** @var \Drupal\ncms_paragraphs\ParagraphsTypeSettings $paragraph_type_settings */ + $paragraph_type_settings = \Drupal::service('ncms_paragraphs.paragraph_type_settings'); + $paragraph_type_settings->paragraphsTypeFormAlter($form, $form_state); +} diff --git a/html/modules/custom/ncms_paragraphs/ncms_paragraphs.services.yml b/html/modules/custom/ncms_paragraphs/ncms_paragraphs.services.yml new file mode 100644 index 00000000..8913ed66 --- /dev/null +++ b/html/modules/custom/ncms_paragraphs/ncms_paragraphs.services.yml @@ -0,0 +1,7 @@ +services: + ncms_paragraphs.component_menu: + class: Drupal\ncms_paragraphs\ComponentMenu + arguments: ['@entity_type.manager', '@ncms_paragraphs.paragraph_type_settings'] + ncms_paragraphs.paragraph_type_settings: + class: Drupal\ncms_paragraphs\ParagraphsTypeSettings + arguments: [] \ No newline at end of file diff --git a/html/modules/custom/ncms_paragraphs/src/ComponentMenu.php b/html/modules/custom/ncms_paragraphs/src/ComponentMenu.php new file mode 100644 index 00000000..a5a82925 --- /dev/null +++ b/html/modules/custom/ncms_paragraphs/src/ComponentMenu.php @@ -0,0 +1,156 @@ +entityTypeManager = $entityTypeManager; + $this->paragraphsTypeSettings = $paragraph_type_settings; + } + + /** + * Preprocess the layout paragraphs component menu template. + * + * This turns the 2-category list into a custom list with multiple + * categories using verticala tabs. + * + * @param array $variables + * The template variables to alter. + * + * @see ncms_paragraphs_theme_registry_alter() + */ + public function preprocessTemplate(&$variables) { + $items = array_merge($variables['types']['content'], $variables['types']['layout']); + uasort($items, function ($a, $b) { + return SortArray::sortByKeyString($a, $b, 'label'); + }); + + // Prepare a pseudo form where we can attach vertical tabs. + $form_state = new FormState(); + $complete_form = []; + $form = []; + $form['category_header'] = [ + '#type' => 'html_tag', + '#tag' => 'h5', + '#value' => $this->t('Choose a paragraph type from the following categories:'), + ]; + $form['tabs'] = [ + '#type' => 'vertical_tabs', + '#parents' => ['tabs'], + ]; + + $category_ids = array_keys($this->paragraphsTypeSettings->getCategoryOptions()); + $categories = []; + foreach ($items as $item) { + /** @var \Drupal\paragraphs\Entity\ParagraphsType $entity */ + $entity = $this->entityTypeManager->getStorage('paragraphs_type')->load($item['id']); + $settings = $entity->getThirdPartySettings('ncms_paragraphs'); + if (!empty($settings['disabled'])) { + continue; + } + + $category = $settings['category'] ?? NULL; + if (!$category) { + continue; + } + $category_label = $this->paragraphsTypeSettings->getCategoryLabel($category) ?? $this->t('Unknown'); + + $category_build = $categories[$category] ?? [ + '#title' => $category_label, + 'links' => [ + '#theme' => 'links', + '#links' => [], + ], + ]; + $category_build['links']['#links'][] = [ + 'title' => (string) $item['label'], + 'url' => $item['url_object'], + 'attributes' => [ + 'class' => ['use-ajax'], + 'data-dialog-type' => 'dialog', + 'data-dialog-renderer' => 'off_canvas', + ], + ]; + $categories[$category] = $category_build; + } + + $form['paragraph_categories'] = array_combine($category_ids, array_map(function ($category_id) use ($categories) { + return $categories[$category_id]; + }, $category_ids)); + $form['paragraph_categories']['#tree'] = TRUE; + + // Let the tab element set itself up. + VerticalTabs::processVerticalTabs($form['tabs'], $form_state, $complete_form); + RenderElement::processGroup($form['tabs']['group'], $form_state, $complete_form); + + // Default tab is the first one. We have to set #value instead of the + // #default_value, because this is not a real form and the normal form + // processing doesn't work. + $form['tabs']['tabs__active_tab']['#value'] = reset($categories); + + // Now go over the block categories, add some required properties and + // run the process callback. + $form['paragraph_categories']['#parents'] = ['paragraph_categories']; + foreach (Element::children($form['paragraph_categories']) as $element_key) { + $form['paragraph_categories'][$element_key]['#type'] = 'details'; + $form['paragraph_categories'][$element_key]['#group'] = 'tabs'; + $form['paragraph_categories'][$element_key]['#id'] = Html::getId($element_key); + $form['paragraph_categories'][$element_key]['#parents'] = [ + 'paragraph_categories', + $element_key, + ]; + $form['paragraph_categories'][$element_key]['#attributes'] = [ + 'class' => [ + 'paragraph-category-links', + ], + ]; + RenderElement::processGroup($form['paragraph_categories'][$element_key], $form_state, $complete_form); + } + $this->processVerticalTabs($form, $form_state); + + $variables['menu'] = $form; + $variables['#attached']['library'][] = 'ncms_paragraphs/component_menu'; + } + +} diff --git a/html/modules/custom/ncms_paragraphs/src/Entity/NcmsParagraphBase.php b/html/modules/custom/ncms_paragraphs/src/Entity/NcmsParagraphBase.php new file mode 100644 index 00000000..4d386452 --- /dev/null +++ b/html/modules/custom/ncms_paragraphs/src/Entity/NcmsParagraphBase.php @@ -0,0 +1,18 @@ +getValue($element_parents); + if (strpos($embed_code, '