diff --git a/config/install/page_manager.page.node_view.yml b/config/install/page_manager.page.node_view.yml index 08b12ee..49111c8 100644 --- a/config/install/page_manager.page.node_view.yml +++ b/config/install/page_manager.page.node_view.yml @@ -3,6 +3,7 @@ status: true dependencies: { } id: node_view label: 'Node view' +description: 'When enabled, this overrides the default Drupal behavior for displaying nodes at /node/{node}. If you add variants, you may use selection criteria such as node type or language or user access to provide different views of nodes. If no variant is selected, the default Drupal node view will be used. This page only affects nodes viewed as pages, it will not affect nodes viewed in lists or at other locations.' use_admin_theme: false path: '/node/{node}' access_logic: and diff --git a/config/schema/page_manager.schema.yml b/config/schema/page_manager.schema.yml index 8c6f225..649a25f 100644 --- a/config/schema/page_manager.schema.yml +++ b/config/schema/page_manager.schema.yml @@ -8,6 +8,9 @@ page_manager.page.*: label: type: label label: 'Label' + description: + type: text + label: 'Description' use_admin_theme: type: boolean label: 'Whether the page is displayed using the admin theme or not' @@ -61,6 +64,7 @@ page_manager.page_variant.*: label: 'Display variant' variant_settings: type: display_variant.plugin.[%parent.variant] + label: 'Variant settings' page: type: string label: 'Parent page' @@ -77,18 +81,8 @@ page_manager.page_variant.*: type: sequence label: Static context list sequence: - - type: mapping + - type: ctools.context label: 'Static context' - mapping: - label: - type: label - label: 'Label of the context' - type: - type: string - label: 'Context type' - value: - type: string - label: 'Context value' page_manager.block_plugin.*: type: block.settings.[id] diff --git a/page_manager.info.yml b/page_manager.info.yml index 84ab601..ea10936 100644 --- a/page_manager.info.yml +++ b/page_manager.info.yml @@ -4,5 +4,6 @@ description: 'Provides a way to place blocks on a custom page.' package: Layout core: 8.x dependencies: + - system (>=8.0.5) - block - ctools diff --git a/page_manager.install b/page_manager.install index 9ed552d..0e25f7b 100644 --- a/page_manager.install +++ b/page_manager.install @@ -6,6 +6,27 @@ */ /** + * Implements hook_requirements(). + * + * @todo: Remove this when https://www.drupal.org/node/2641658 is fixed. + */ +function page_manager_requirements($phase) { + $requirements = []; + + // Check that core actually is >= 8.0.5. + if (!version_compare(\Drupal::VERSION, '8.0.5', '>=')) { + $requirements['page_manager_core_version'] = [ + 'title' => t('Page manager Drupal core version'), + 'value' => \Drupal::VERSION, + 'description' => t('Page manager requires at least Drupal core 8.0.5.'), + 'severity' => REQUIREMENT_ERROR, + ]; + } + + return $requirements; +} + +/** * Install the Page Manager UI for existing sites. */ function page_manager_update_8001() { diff --git a/page_manager_ui/css/page-manager-ui.admin.css b/page_manager_ui/css/page-manager-ui.admin.css new file mode 100644 index 0000000..e82c377 --- /dev/null +++ b/page_manager_ui/css/page-manager-ui.admin.css @@ -0,0 +1,113 @@ +/** + * @file + * Styles for Page Manager admin. + */ + +/* Narrow screens */ + +.page-manager-wizard-tree, +.page-manager-wizard-form { + box-sizing: border-box; +} + +/** + * Wizard actions across the top. + */ +.page-manager-wizard-actions { + text-align: right; /* LTR */ +} +.page-manager-wizard-actions ul.inline, +.page-manager-wizard-actions ul.inline li { + display: inline-block; + margin: 0; +} +.page-manager-wizard-actions ul.inline { + border-top: 1px solid black; + border-left: 1px solid black; +} +.page-manager-wizard-actions ul.inline li { + border-right: 1px solid black; + padding: .5em; +} + +/** + * The tree of wizard steps. + */ +.page-manager-wizard-tree ul { + margin: 0; + padding: 0; + list-style: none; +} +.page-manager-wizard-tree ul > li > ul { + margin-left: 1em; +} +.page-manager-wizard-tree > ul { + border: 1px solid black; + padding-bottom: .5em; + margin-bottom: 20px; +} +.page-manager-wizard-tree li { + border-bottom: 1px solid black; + padding: .5em; + padding-right: 0; +} +.page-manager-wizard-tree li:last-child { + border-bottom: 0; + padding-bottom: 0; +} + +/** + * The wizard form. + */ +.page-manager-wizard-form { + border: 1px solid black; + padding: 1em; + margin-bottom: 20px; +} + +/* Wide screens */ +@media + screen and (min-width: 780px), + (orientation: landscape) and (min-device-height: 780px) { + + /** + * Overall layout. + */ + .page-manager-wizard-tree { + float: left; /* LTR */ + width: 20%; + } + .page-manager-wizard-form { + float: left; /* LTR */ + width: 80%; + } + .page-manager-wizard-form-actions { + margin-left: 20%; /* LTR */ + } + + /** + * Make the borders look nice. + */ + .page-manager-wizard-tree > ul { + border-right: 0; /* LTR */ + } + .page-manager-wizard-form { + min-height: 700px; + } + + /** + * Right-to-left support. + */ + [dir="rtl"] .page-manager-wizard-tree, + [dir="rtl"] .page-manager-wizard-form { + float: right; + } + [dir="rtl"] .page-manager-wizard-form-actions { + margin-left: 0; + margin-right: 20%; + } + [dir="rtl"] .page-manager-wizard-tree > ul { + border-right: 1px solid black; + border-left: 0; + } +} diff --git a/page_manager_ui/page_manager_ui.libraries.yml b/page_manager_ui/page_manager_ui.libraries.yml new file mode 100644 index 0000000..0a37c69 --- /dev/null +++ b/page_manager_ui/page_manager_ui.libraries.yml @@ -0,0 +1,5 @@ +admin: + version: VERSION + css: + layout: + css/page-manager-ui.admin.css: {} diff --git a/page_manager_ui/page_manager_ui.module b/page_manager_ui/page_manager_ui.module index 2c15495..4c0b650 100644 --- a/page_manager_ui/page_manager_ui.module +++ b/page_manager_ui/page_manager_ui.module @@ -6,12 +6,12 @@ */ use Drupal\page_manager_ui\Entity\PageListBuilder; -use Drupal\page_manager_ui\Form\PageAddForm; use Drupal\page_manager_ui\Form\PageDeleteForm; -use Drupal\page_manager_ui\Form\PageEditForm; -use Drupal\page_manager_ui\Form\PageVariantAddForm; -use Drupal\page_manager_ui\Form\PageVariantDeleteForm; -use Drupal\page_manager_ui\Form\PageVariantEditForm; +use Drupal\page_manager_ui\ConfigTranslation\PageConfigMapper; +use Drupal\page_manager_ui\ConfigTranslation\PageVariantConfigMapper; +use Drupal\page_manager_ui\Wizard\PageAddWizard; +use Drupal\page_manager_ui\Wizard\PageEditWizard; +use Drupal\page_manager_ui\Wizard\PageVariantAddWizard; /** * Implements hook_entity_type_build(). @@ -20,20 +20,118 @@ function page_manager_ui_entity_type_build(array &$entity_types) { /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */ $entity_types['page'] ->setListBuilderClass(PageListBuilder::class) - ->setFormClass('add', PageAddForm::class) - ->setFormClass('edit', PageEditForm::class) ->setFormClass('delete', PageDeleteForm::class) ->setLinkTemplate('collection', '/admin/structure/page_manager') ->setLinkTemplate('add-form', '/admin/structure/page_manager/add') - ->setLinkTemplate('edit-form', '/admin/structure/page_manager/manage/{page}') + ->setLinkTemplate('edit-form', '/admin/structure/page_manager/manage/{machine_name}/{step}') ->setLinkTemplate('delete-form', '/admin/structure/page_manager/manage/{page}/delete') ->setLinkTemplate('enable', '/admin/structure/page_manager/manage/{page}/enable') - ->setLinkTemplate('disable', '/admin/structure/page_manager/manage/{page}/disable'); - - $entity_types['page_variant'] - ->setFormClass('add', PageVariantAddForm::class) - ->setFormClass('edit', PageVariantEditForm::class) - ->setFormClass('delete', PageVariantDeleteForm::class) - ->setLinkTemplate('edit-form', '/admin/structure/page_manager/manage/{page}/variant/{page_variant}') - ->setLinkTemplate('delete-form', '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/delete'); + ->setLinkTemplate('disable', '/admin/structure/page_manager/manage/{page}/disable') + ->setHandlerClass('wizard', [ + 'add' => PageAddWizard::class, + 'edit' => PageEditWizard::class, + ]); + + $entity_types['page_variant'] + // The edit-form template is required by config_translation. + ->setLinkTemplate('edit-form', '/admin/structure/page_manager/manage/{machine_name}/{step}') + ->setHandlerClass('wizard', [ + 'add_variant' => PageVariantAddWizard::class, + ]); +} + +/** + * Implements hook_entity_type_alter(). + */ +function page_manager_ui_entity_type_alter(array &$entity_types) { + // Change the URL for page config translation overview to outside the wizard. + if ($entity_types['page']->hasLinkTemplate('config-translation-overview')) { + $entity_types['page']->setLinkTemplate('config-translation-overview', str_replace('manage/{machine_name}/{step}', '{page}', $entity_types['page']->getLinkTemplate('config-translation-overview'))); + } + + // Change the URL for page variant config translation overview to outside the + // wizard. + if ($entity_types['page_variant']->hasLinkTemplate('config-translation-overview')) { + $entity_types['page_variant']->setLinkTemplate('config-translation-overview', str_replace('manage/{machine_name}/{step}', '{page}/{page_variant}', $entity_types['page_variant']->getLinkTemplate('config-translation-overview'))); + } +} + +/** + * Implements hook_theme(). + */ +function page_manager_ui_theme() { + return [ + 'page_manager_wizard_form' => [ + 'render element' => 'form', + ], + 'page_manager_wizard_tree' => [ + 'variables' => [ + 'wizard' => NULL, + 'cached_values' => [], + 'tree' => [], + 'divider' => ' ยป ', + 'step' => NULL, + ], + ], + ]; +} + +/** + * Preprocess function for page-manager-wizard-tree.html.twig. + */ +function template_preprocess_page_manager_wizard_tree(&$variables) { + /** @var $wizard \Drupal\ctools\Wizard\FormWizardInterface|\Drupal\ctools\Wizard\EntityFormWizardInterface */ + $wizard = $variables['wizard']; + $cached_values = $variables['cached_values']; + $tree = $variables['tree']; + $variables['step'] = $wizard->getStep($cached_values); + + foreach ($wizard->getOperations($cached_values) as $step => $operation) { + $parameters = $wizard->getNextParameters($cached_values); + // Override step to be the step we want. + $parameters['step'] = $step; + + // Fill in parents if there are breadcrumbs. + $parent =& $tree; + if (isset($operation['breadcrumbs'])) { + foreach ($operation['breadcrumbs'] as $breadcrumb) { + $breadcrumb_string = (string) $breadcrumb; + if (!isset($parent[$breadcrumb_string])) { + $parent[$breadcrumb_string] = [ + 'title' => $breadcrumb, + 'children' => [], + ]; + } + $parent =& $parent[$breadcrumb_string]['children']; + } + } + + $parent[$step] = [ + 'title' => !empty($operation['title']) ? $operation['title'] : '', + 'url' => new \Drupal\Core\Url($wizard->getRouteName(), $parameters), + 'step' => $step, + ]; + } + + $variables['tree'] = $tree; +} + +/** + * Implements hook_config_translation_info_alter(). + */ +function page_manager_ui_config_translation_info_alter(&$info) { + // Alter page and page variant config translation classes. + $info['page']['class'] = PageConfigMapper::class; + $info['page_variant']['class'] = PageVariantConfigMapper::class; + $info['page_variant']['base_route_name'] = 'entity.page.edit_form'; +} + +/** + * Implements hook_local_tasks_alter(). + */ +function page_manager_ui_local_tasks_alter(&$local_tasks) { + // Remove local tasks for page and page variant config translation overview + // routes. + unset($local_tasks['config_translation.local_tasks:entity.page.config_translation_overview']); + unset($local_tasks['config_translation.local_tasks:entity.page_variant.config_translation_overview']); } diff --git a/page_manager_ui/page_manager_ui.routing.yml b/page_manager_ui/page_manager_ui.routing.yml index 00a9c3a..fe03acd 100644 --- a/page_manager_ui/page_manager_ui.routing.yml +++ b/page_manager_ui/page_manager_ui.routing.yml @@ -11,18 +11,34 @@ entity.page.collection: entity.page.add_form: path: '/admin/structure/page_manager/add' defaults: - _entity_form: 'page.add' + _entity_wizard: 'page.add' _title: 'Add new page' + tempstore_id: page_manager.page requirements: _entity_create_access: page +entity.page.add_step_form: + path: '/admin/structure/page_manager/add/{machine_name}/{step}' + defaults: + _entity_wizard: 'page.add' + _title: 'Add new page' + tempstore_id: page_manager.page + requirements: + _entity_create_access: page + entity.page.edit_form: - path: '/admin/structure/page_manager/manage/{page}' + path: '/admin/structure/page_manager/manage/{machine_name}/{step}' defaults: - _entity_form: 'page.edit' + _entity_wizard: 'page.edit' _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editPageTitle' + tempstore_id: page_manager.page + page: '{machine_name}' + options: + parameters: + page: + type: tempstore:page requirements: - _entity_access: page.update + _permission: 'administer pages' entity.page.delete_form: path: '/admin/structure/page_manager/manage/{page}/delete' @@ -30,7 +46,7 @@ entity.page.delete_form: _entity_form: 'page.delete' _title: 'Delete page' requirements: - _entity_access: page.delete + _permission: 'administer pages' entity.page.enable: path: '/admin/structure/page_manager/manage/{page}/enable' @@ -38,7 +54,7 @@ entity.page.enable: _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::performPageOperation' op: 'enable' requirements: - _entity_access: 'page.update' + _permission: 'administer pages' entity.page.disable: path: '/admin/structure/page_manager/manage/{page}/disable' @@ -46,174 +62,242 @@ entity.page.disable: _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::performPageOperation' op: 'disable' requirements: - _entity_access: 'page.update' - -#### Access Conditions + _permission: 'administer pages' -page_manager.access_condition_select: - path: '/admin/structure/page_manager/manage/{page}/access/select' +entity.page.reorder_variants_form: + path: '/admin/structure/page_manager/manage/{machine_name}/reorder_variants' defaults: - _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::selectAccessCondition' - _title: 'Select access condition' + _title: 'Reorder variants' + _form: '\Drupal\page_manager_ui\Form\PageReorderVariantsForm' requirements: - _entity_access: page.update + _permission: 'administer pages' + +#### Access Conditions -page_manager.access_condition_add: - path: '/admin/structure/page_manager/manage/{page}/access/add/{condition_id}' +entity.page.condition.add: + path: '/admin/structure/page_manager/manage/{machine_name}/access/add/{condition}' defaults: - _form: '\Drupal\page_manager_ui\Form\AccessConditionAddForm' - _title: 'Add new access condition' + _form: '\Drupal\page_manager_ui\Form\AccessConfigure' + _title: 'Add access condition' + tempstore_id: page_manager.page requirements: - _entity_access: page.update + _permission: 'administer pages' -page_manager.access_condition_edit: - path: '/admin/structure/page_manager/manage/{page}/access/edit/{condition_id}' +entity.page.condition.edit: + path: '/admin/structure/page_manager/manage/{machine_name}/access/edit/{condition}' defaults: - _form: '\Drupal\page_manager_ui\Form\AccessConditionEditForm' - _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editAccessConditionTitle' + _form: '\Drupal\page_manager_ui\Form\AccessConfigure' + _title: 'Edit access condition' + tempstore_id: page_manager.page requirements: - _entity_access: page.update + _permission: 'administer pages' -page_manager.access_condition_delete: - path: '/admin/structure/page_manager/manage/{page}/access/delete/{condition_id}' +entity.page.condition.delete: + path: '/admin/structure/page_manager/manage/{machine_name}/access/delete/{id}' defaults: - _form: '\Drupal\page_manager_ui\Form\AccessConditionDeleteForm' + _form: '\Drupal\page_manager_ui\Form\AccessDelete' _title: 'Delete access condition' + tempstore_id: page_manager.page requirements: - _entity_access: page.update + _permission: 'administer pages' #### Parameters -page_manager.parameter_edit: - path: '/admin/structure/page_manager/manage/{page}/parameter/edit/{name}' +page_manager.parameter.edit: + path: '/admin/structure/page_manager/manage/{machine_name}/parameter/edit/{name}' defaults: _form: '\Drupal\page_manager_ui\Form\ParameterEditForm' _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editParameterTitle' + tempstore_id: page_manager.page + page: '{machine_name}' + options: + parameters: + page: + type: tempstore:page requirements: _entity_access: page.update -#### Static Contexts +#### Variants -page_manager.static_context_add: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/context/add' +page_manager.variant_select: + path: '/admin/structure/page_manager/manage/{machine_name}/add' defaults: - _form: '\Drupal\page_manager_ui\Form\StaticContextAddForm' - _title: 'Add new static context' + _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::selectVariant' + _title: 'Select variant' requirements: - _entity_access: page_variant.update + _permission: 'administer pages' -page_manager.static_context_edit: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/context/edit/{name}' +entity.page_variant.add_form: + path: '/admin/structure/page_manager/manage/{page}/add_variant' defaults: - _form: '\Drupal\page_manager_ui\Form\StaticContextEditForm' - _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editStaticContextTitle' + _entity_wizard: 'page_variant.add_variant' + _title: 'Add page variant' + tempstore_id: page_manager.page_variant requirements: - _entity_access: page_variant.update + _permission: 'administer pages' -page_manager.static_context_delete: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/context/delete/{name}' +entity.page_variant.add_step_form: + path: '/admin/structure/page_manager/manage/{page}/add_variant/{machine_name}/{step}' defaults: - _form: '\Drupal\page_manager_ui\Form\StaticContextDeleteForm' - _title: 'Delete static context' + _entity_wizard: 'page_variant.add_variant' + _title: 'Add page variant' + tempstore_id: page_manager.page_variant requirements: - _entity_access: page_variant.update + _permission: 'administer pages' -#### Variants +entity.page_variant.add_step_form.condition.add: + path: '/admin/structure/page_manager/manage/{page}/add_variant/{machine_name}/selection/add/{condition}' + defaults: + _form: '\Drupal\page_manager_ui\Form\AddVariantSelectionConfigure' + _title: 'Add new selection condition' + tempstore_id: page_manager.page_variant + requirements: + _permission: 'administer pages' -page_manager.variant_select: - path: '/admin/structure/page_manager/manage/{page}/add' +entity.page_variant.add_step_form.condition.edit: + path: '/admin/structure/page_manager/manage/{page}/add_variant/{machine_name}/selection/edit/{condition}' defaults: - _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::selectVariant' - _title: 'Select variant' + _form: '\Drupal\page_manager_ui\Form\AddVariantSelectionConfigure' + _title: 'Add new selection condition' + tempstore_id: page_manager.page_variant requirements: - _entity_access: page.update + _permission: 'administer pages' -entity.page_variant.add_form: - path: '/admin/structure/page_manager/manage/{page}/add/{variant_plugin_id}' +entity.page_variant.add_step_form.condition.delete: + path: '/admin/structure/page_manager/manage/{page}/add_variant/{machine_name}/selection/delete/{id}' defaults: - _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::addPageVariantEntityForm' - _title: 'Add page variant' + _form: '\Drupal\page_manager_ui\Form\AddVariantSelectionDelete' + tempstore_id: page_manager.page_variant + _title: 'Delete selection condition' + requirements: + _permission: 'administer pages' + +entity.page_variant.add_step_form.context.add: + path: '/admin/structure/page_manager/manage/{page}/add_variant/{machine_name}/contexts/add/{context_id}' + defaults: + _form: '\Drupal\page_manager_ui\Form\AddVariantStaticContextConfigure' + _title: 'Add custom context' + tempstore_id: page_manager.page_variant + requirements: + _permission: 'administer pages' + +entity.page_variant.add_step_form.context.edit: + path: '/admin/structure/page_manager/manage/{page}/add_variant/{machine_name}/contexts/edit/{context_id}' + defaults: + _form: '\Drupal\page_manager_ui\Form\AddVariantStaticContextConfigure' + _title: 'Edit context' + tempstore_id: page_manager.page_variant requirements: - _entity_create_access: page_variant + _permission: 'administer pages' -entity.page_variant.edit_form: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}' +entity.page_variant.add_step_form.context.delete: + path: '/admin/structure/page_manager/manage/{page}/add_variant/{machine_name}/context/delete/{context_id}' defaults: - _entity_form: 'page_variant.edit' - _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editPageVariantTitle' + _form: '\Drupal\page_manager_ui\Form\AddVariantStaticContextDeleteForm' + _title: 'Delete static context' + tempstore_id: page_manager.page_variant requirements: - _entity_access: page_variant.update + _permission: 'administer pages' entity.page_variant.delete_form: - path: '/admin/structure/page_variant/variant/{page_variant}/delete' + path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/delete' defaults: - _entity_form: 'page_variant.delete' + _form: '\Drupal\page_manager_ui\Form\PageVariantDeleteForm' _title: 'Delete page variant' + tempstore_id: page_manager.page requirements: - _entity_access: page_variant.delete + _permission: 'administer pages' -page_manager.variant_select_block: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/block/select' +page_manager.block_display_select_block: + path: '/admin/structure/page_manager/block_display/{block_display}/select' defaults: _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::selectBlock' _title: 'Select block' + tempstore_id: 'page_manager.block_display' requirements: - _entity_access: page_variant.update + _ctools_access: 'block_display' -page_manager.variant_add_block: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/block/add/{block_id}' +page_manager.block_display_add_block: + path: '/admin/structure/page_manager/block_display/{block_display}/add/{block_id}' defaults: _form: '\Drupal\page_manager_ui\Form\VariantPluginAddBlockForm' - _title: 'Add block to variant' + _title: 'Add block' + tempstore_id: 'page_manager.block_display' requirements: - _entity_access: page_variant.update + _ctools_access: 'block_display' -page_manager.variant_edit_block: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/block/edit/{block_id}' +page_manager.block_display_edit_block: + path: '/admin/structure/page_manager/block_display/{block_display}/edit/{block_id}' defaults: _form: '\Drupal\page_manager_ui\Form\VariantPluginEditBlockForm' - _title: 'Edit block in variant' + _title: 'Edit block' + tempstore_id: 'page_manager.block_display' requirements: - _entity_access: page_variant.update + _ctools_access: 'block_display' -page_manager.variant_delete_block: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/block/delete/{block_id}' +page_manager.block_display_delete_block: + path: '/admin/structure/page_manager/block_display/{block_display}/delete/{block_id}' defaults: _form: '\Drupal\page_manager_ui\Form\VariantPluginDeleteBlockForm' - _title: 'Delete block in variant' + _title: 'Delete block' + tempstore_id: 'page_manager.block_display' requirements: - _entity_access: page_variant.update + _ctools_access: 'block_display' -#### Selection Conditions +#### Static Contexts + +entity.page_variant.context.add: + path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/contexts/add/{context_id}' + defaults: + _form: '\Drupal\page_manager_ui\Form\StaticContextConfigure' + _title: 'Add custom context' + tempstore_id: page_manager.page + requirements: + _permission: 'administer pages' -page_manager.selection_condition_select: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/selection/select' +entity.page_variant.context.edit: + path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/contexts/edit/{context_id}' defaults: - _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::selectSelectionCondition' - _title: 'Select selection condition' + _form: '\Drupal\page_manager_ui\Form\StaticContextConfigure' + _title: 'Edit context' + tempstore_id: page_manager.page requirements: - _entity_access: page_variant.update + _permission: 'administer pages' -page_manager.selection_condition_add: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/selection/add/{condition_id}' +entity.page_variant.context.delete: + path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/context/delete/{context_id}' defaults: - _form: '\Drupal\page_manager_ui\Form\SelectionConditionAddForm' + _form: '\Drupal\page_manager_ui\Form\StaticContextDeleteForm' + _title: 'Delete static context' + tempstore_id: page_manager.page + requirements: + _permission: 'administer pages' + +#### Selection Conditions + +entity.page_variant.condition.add: + path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/selection/add/{condition}' + defaults: + _form: '\Drupal\page_manager_ui\Form\SelectionConfigure' + tempstore_id: page_manager.page _title: 'Add new selection condition' requirements: - _entity_access: page_variant.update + _permission: 'administer pages' -page_manager.selection_condition_edit: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/selection/edit/{condition_id}' +entity.page_variant.condition.edit: + path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/selection/edit/{condition}' defaults: - _form: '\Drupal\page_manager_ui\Form\SelectionConditionEditForm' - _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editSelectionConditionTitle' + _form: '\Drupal\page_manager_ui\Form\SelectionConfigure' + tempstore_id: page_manager.page + _title: 'Edit selection condition' requirements: - _entity_access: page_variant.update + _permission: 'administer pages' -page_manager.selection_condition_delete: - path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/selection/delete/{condition_id}' +entity.page_variant.condition.delete: + path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/selection/delete/{id}' defaults: - _form: '\Drupal\page_manager_ui\Form\SelectionConditionDeleteForm' + _form: '\Drupal\page_manager_ui\Form\SelectionDelete' + tempstore_id: page_manager.page _title: 'Delete selection condition' requirements: - _entity_access: page_variant.update + _permission: 'administer pages' diff --git a/page_manager_ui/src/Access/PageManagerPluginAccess.php b/page_manager_ui/src/Access/PageManagerPluginAccess.php new file mode 100644 index 0000000..455857a --- /dev/null +++ b/page_manager_ui/src/Access/PageManagerPluginAccess.php @@ -0,0 +1,19 @@ +hasPermission('administer pages') ? AccessResult::allowed() : AccessResult::forbidden(); + } + +} diff --git a/page_manager_ui/src/ConfigTranslation/PageConfigMapper.php b/page_manager_ui/src/ConfigTranslation/PageConfigMapper.php new file mode 100644 index 0000000..916d53e --- /dev/null +++ b/page_manager_ui/src/ConfigTranslation/PageConfigMapper.php @@ -0,0 +1,40 @@ +getPath(); + $path = str_replace('manage/{machine_name}/{step}', '{page}', $path); + $route->setPath($path); + } + + /** + * {@inheritdoc} + */ + public function getBaseRouteParameters() { + $parameters = parent::getBaseRouteParameters(); + $parameters['step'] = 'general'; + $parameters['machine_name'] = $parameters['page']; + return $parameters; + } + +} diff --git a/page_manager_ui/src/ConfigTranslation/PageVariantConfigMapper.php b/page_manager_ui/src/ConfigTranslation/PageVariantConfigMapper.php new file mode 100644 index 0000000..da9947e --- /dev/null +++ b/page_manager_ui/src/ConfigTranslation/PageVariantConfigMapper.php @@ -0,0 +1,76 @@ +getPath(); + $path = str_replace('manage/{machine_name}/{step}', '{page}/{page_variant}', $path); + $route->setPath($path); + } + + /** + * {@inheritdoc} + */ + public function getBaseRouteParameters() { + $parameters = parent::getBaseRouteParameters(); + $parameters['page'] = $this->entity->get('page'); + $parameters['machine_name'] = $parameters['page']; + $parameters['step'] = 'page_variant__' . $parameters['page_variant'] . '__general'; + return $parameters; + } + + /** + * {@inheritdoc} + */ + public function getAddRouteName() { + return $this->alterRouteName(parent::getAddRouteName()); + } + + /** + * {@inheritdoc} + */ + public function getEditRouteName() { + return $this->alterRouteName(parent::getEditRouteName()); + } + + /** + * {@inheritdoc} + */ + public function getDeleteRouteName() { + return $this->alterRouteName(parent::getDeleteRouteName()); + } + + /** + * Alter the route name to be unique from page entity route names. + * + * @param string $name + * Route name for the mapper. + * + * @return string + * Altered route name for the mapper. + */ + protected function alterRouteName($name) { + return str_replace('page', 'page_variant', $name); + } + +} diff --git a/page_manager_ui/src/Controller/PageManagerController.php b/page_manager_ui/src/Controller/PageManagerController.php index 708d0e6..ebb08a7 100644 --- a/page_manager_ui/src/Controller/PageManagerController.php +++ b/page_manager_ui/src/Controller/PageManagerController.php @@ -10,11 +10,13 @@ namespace Drupal\page_manager_ui\Controller; use Drupal\Component\Plugin\PluginManagerInterface; use Drupal\Core\Block\BlockManagerInterface; use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\Plugin\Context\Context; use Drupal\Core\Plugin\Context\ContextHandlerInterface; use Drupal\Core\Url; use Drupal\ctools\Form\AjaxFormTrait; use Drupal\page_manager\PageInterface; use Drupal\page_manager\PageVariantInterface; +use Drupal\user\SharedTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -54,6 +56,13 @@ class PageManagerController extends ControllerBase { protected $contextHandler; /** + * Tempstore factory. + * + * @var \Drupal\user\SharedTempStoreFactory + */ + protected $tempstore; + + /** * Constructs a new VariantPluginEditForm. * * @param \Drupal\Core\Block\BlockManagerInterface $block_manager @@ -64,12 +73,15 @@ class PageManagerController extends ControllerBase { * The variant manager. * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler * The context handler. ++ * @param \Drupal\user\SharedTempStoreFactory $tempstore ++ * The tempstore factory. */ - public function __construct(BlockManagerInterface $block_manager, PluginManagerInterface $condition_manager, PluginManagerInterface $variant_manager, ContextHandlerInterface $context_handler) { + public function __construct(BlockManagerInterface $block_manager, PluginManagerInterface $condition_manager, PluginManagerInterface $variant_manager, ContextHandlerInterface $context_handler, SharedTempStoreFactory $tempstore) { $this->blockManager = $block_manager; $this->conditionManager = $condition_manager; $this->variantManager = $variant_manager; $this->contextHandler = $context_handler; + $this->tempstore = $tempstore; } /** @@ -80,20 +92,26 @@ class PageManagerController extends ControllerBase { $container->get('plugin.manager.block'), $container->get('plugin.manager.condition'), $container->get('plugin.manager.display_variant'), - $container->get('context.handler') + $container->get('context.handler'), + $container->get('user.shared_tempstore') ); } /** * Route title callback. * - * @param \Drupal\page_manager\PageInterface $page - * The page entity. + * @param string $machine_name + * The page's machine_name. + * @param string $tempstore_id + * The temporary store identifier. * * @return string * The title for the page edit form. */ - public function editPageTitle(PageInterface $page) { + public function editPageTitle($machine_name, $tempstore_id) { + $cached_values = $this->tempstore->get($tempstore_id)->get($machine_name); + /** @var \Drupal\page_manager\PageInterface $page */ + $page = $cached_values['page']; return $this->t('Edit %label page', ['%label' => $page->label()]); } @@ -145,22 +163,6 @@ class PageManagerController extends ControllerBase { /** * Route title callback. * - * @param \Drupal\page_manager\PageVariantInterface $page_variant - * The page variant entity. - * @param string $name - * The static context name. - * - * @return string - * The title for the static context edit form. - */ - public function editStaticContextTitle(PageVariantInterface $page_variant, $name) { - $static_context = $page_variant->getStaticContext($name); - return $this->t('Edit @label static context', ['@label' => $static_context['label']]); - } - - /** - * Route title callback. - * * @param \Drupal\page_manager\PageInterface $page * The page entity. * @param string $name @@ -294,13 +296,30 @@ class PageManagerController extends ControllerBase { * * @param \Symfony\Component\HttpFoundation\Request $request * The current request. - * @param \Drupal\page_manager\PageVariantInterface $page_variant - * The page entity. + * @param string $block_display + * The identifier of the block display variant. + * @param string $tempstore_id + * The identifier of the temporary store. * * @return array * The block selection page. */ - public function selectBlock(Request $request, PageVariantInterface $page_variant) { + public function selectBlock(Request $request, $block_display, $tempstore_id) { + $cached_values = $this->tempstore->get($tempstore_id)->get($block_display); + /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */ + $variant_plugin = $cached_values['plugin']; + + // Rehydrate the contexts on this end. + $contexts = []; + /** + * @var string $context_name + * @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface $context_definition + */ + foreach ($cached_values['contexts'] as $context_name => $context_definition) { + $contexts[$context_name] = new Context($context_definition); + } + $variant_plugin->setContexts($contexts); + // Add a section containing the available blocks to be added to the variant. $build = [ '#type' => 'container', @@ -310,7 +329,7 @@ class PageManagerController extends ControllerBase { ], ], ]; - $available_plugins = $this->blockManager->getDefinitionsForContexts($page_variant->getContexts()); + $available_plugins = $this->blockManager->getDefinitionsForContexts($variant_plugin->getContexts()); // Order by category, and then by admin label. $available_plugins = $this->blockManager->getSortedDefinitions($available_plugins); foreach ($available_plugins as $plugin_id => $plugin_definition) { @@ -329,11 +348,11 @@ class PageManagerController extends ControllerBase { // Add a link for each available block within each region. $build[$category_key]['content']['#links'][$plugin_id] = [ 'title' => $plugin_definition['admin_label'], - 'url' => Url::fromRoute('page_manager.variant_add_block', [ - 'page' => $page_variant->get('page'), - 'page_variant' => $page_variant->id(), + 'url' => Url::fromRoute('page_manager.block_display_add_block', [ + 'block_display' => $block_display, 'block_id' => $plugin_id, 'region' => $request->query->get('region'), + 'destination' => $request->query->get('destination'), ]), 'attributes' => $this->getAjaxAttributes(), ]; diff --git a/page_manager_ui/src/Entity/PageListBuilder.php b/page_manager_ui/src/Entity/PageListBuilder.php index a1bb281..8c3ec33 100644 --- a/page_manager_ui/src/Entity/PageListBuilder.php +++ b/page_manager_ui/src/Entity/PageListBuilder.php @@ -40,6 +40,16 @@ class PageListBuilder extends ConfigEntityListBuilder { } /** + * {@inheritdoc} + */ + public function getDefaultOperations(EntityInterface $entity) { + $operations = parent::getDefaultOperations($entity); + $operations['edit']['url'] = new Url('entity.page.edit_form', ['machine_name' => $entity->id(), 'step' => 'general']); + + return $operations; + } + + /** * Gets the displayable path of a page entity. * * @param \Drupal\page_manager\PageInterface $entity diff --git a/page_manager_ui/src/Form/AccessConditionAddForm.php b/page_manager_ui/src/Form/AccessConditionAddForm.php deleted file mode 100644 index c71a452..0000000 --- a/page_manager_ui/src/Form/AccessConditionAddForm.php +++ /dev/null @@ -1,73 +0,0 @@ -conditionManager = $condition_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('plugin.manager.condition') - ); - } - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'page_manager_access_condition_add_form'; - } - - /** - * {@inheritdoc} - */ - protected function prepareCondition($condition_id) { - // Create a new access condition instance. - return $this->conditionManager->createInstance($condition_id); - } - - /** - * {@inheritdoc} - */ - protected function submitButtonText() { - return $this->t('Add access condition'); - } - - /** - * {@inheritdoc} - */ - protected function submitMessageText() { - return $this->t('The %label access condition has been added.', ['%label' => $this->condition->getPluginDefinition()['label']]); - } - -} diff --git a/page_manager_ui/src/Form/AccessConditionDeleteForm.php b/page_manager_ui/src/Form/AccessConditionDeleteForm.php deleted file mode 100644 index ab254a6..0000000 --- a/page_manager_ui/src/Form/AccessConditionDeleteForm.php +++ /dev/null @@ -1,80 +0,0 @@ -t('Are you sure you want to delete the access condition %name?', ['%name' => $this->accessCondition->getPluginDefinition()['label']]); - } - - /** - * {@inheritdoc} - */ - public function getCancelUrl() { - return $this->page->toUrl('edit-form'); - } - - /** - * {@inheritdoc} - */ - public function getConfirmText() { - return $this->t('Delete'); - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $condition_id = NULL) { - $this->page = $page; - $this->accessCondition = $page->getAccessCondition($condition_id); - return parent::buildForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - $this->page->removeAccessCondition($this->accessCondition->getConfiguration()['uuid']); - $this->page->save(); - drupal_set_message($this->t('The access condition %name has been removed.', ['%name' => $this->accessCondition->getPluginDefinition()['label']])); - $form_state->setRedirectUrl($this->getCancelUrl()); - } - -} diff --git a/page_manager_ui/src/Form/AccessConditionEditForm.php b/page_manager_ui/src/Form/AccessConditionEditForm.php deleted file mode 100644 index 92cc124..0000000 --- a/page_manager_ui/src/Form/AccessConditionEditForm.php +++ /dev/null @@ -1,44 +0,0 @@ -page->getAccessCondition($condition_id); - } - - /** - * {@inheritdoc} - */ - protected function submitButtonText() { - return $this->t('Update access condition'); - } - - /** - * {@inheritdoc} - */ - protected function submitMessageText() { - return $this->t('The %label access condition has been updated.', ['%label' => $this->condition->getPluginDefinition()['label']]); - } - -} diff --git a/page_manager_ui/src/Form/AccessConditionFormBase.php b/page_manager_ui/src/Form/AccessConditionFormBase.php deleted file mode 100644 index 26b5ead..0000000 --- a/page_manager_ui/src/Form/AccessConditionFormBase.php +++ /dev/null @@ -1,51 +0,0 @@ -page = $page; - return parent::buildForm($form, $form_state, $condition_id, $page->getContexts()); - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - parent::submitForm($form, $form_state); - - $configuration = $this->condition->getConfiguration(); - // If this access condition is new, add it to the page. - if (!isset($configuration['uuid'])) { - $this->page->addAccessCondition($configuration); - } - - // Save the page entity. - $this->page->save(); - - $form_state->setRedirectUrl($this->page->toUrl('edit-form')); - } - -} diff --git a/page_manager_ui/src/Form/AccessConfigure.php b/page_manager_ui/src/Form/AccessConfigure.php new file mode 100644 index 0000000..0b9f3d2 --- /dev/null +++ b/page_manager_ui/src/Form/AccessConfigure.php @@ -0,0 +1,58 @@ +isNew() ? 'entity.page.add_step_form' : 'entity.page.edit_form'; + return [$route_name, [ + 'machine_name' => $this->machine_name, + 'step' => 'access', + ]]; + } + + /** + * {@inheritdoc} + */ + protected function getConditions($cached_values) { + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + return $page->get('access_conditions'); + } + + /** + * {@inheritdoc} + */ + protected function setConditions($cached_values, $conditions) { + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + $page->set('access_conditions', $conditions); + $cached_values['page'] = $page; + return $cached_values; + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + return $page->getContexts(); + } + +} diff --git a/page_manager_ui/src/Form/AccessDelete.php b/page_manager_ui/src/Form/AccessDelete.php new file mode 100644 index 0000000..7961bba --- /dev/null +++ b/page_manager_ui/src/Form/AccessDelete.php @@ -0,0 +1,57 @@ +isNew() ? 'entity.page.add_step_form' : 'entity.page.edit_form'; + return [$route_name, [ + 'machine_name' => $this->machine_name, + 'step' => 'access', + ]]; + } + + /** + * {@inheritdoc} + */ + protected function getConditions($cached_values) { + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + return $page->get('access_conditions'); + } + + /** + * {@inheritdoc} + */ + protected function setConditions($cached_values, $conditions) { + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + $page->set('access_conditions', $conditions); + return $cached_values; + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + return $page->getContexts(); + } + +} diff --git a/page_manager_ui/src/Form/AddVariantContextsForm.php b/page_manager_ui/src/Form/AddVariantContextsForm.php new file mode 100644 index 0000000..2e1af4b --- /dev/null +++ b/page_manager_ui/src/Form/AddVariantContextsForm.php @@ -0,0 +1,129 @@ +getTemporaryValue('wizard'); + /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $cached_values['page_variant']; + $context = $form_state->getValue('context'); + $content = $this->formBuilder->getForm($this->getContextClass(), $context, $this->getTempstoreId(), $this->machine_name, $page_variant->id()); + $content['#attached']['library'][] = 'core/drupal.dialog.ajax'; + list(, $route_parameters) = $this->getContextOperationsRouteInfo($cached_values, $this->machine_name, $context); + $content['submit']['#attached']['drupalSettings']['ajax'][$content['submit']['#id']]['url'] = $this->url($this->getContextAddRoute($cached_values), $route_parameters, ['query' => [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]]); + $response = new AjaxResponse(); + $response->addCommand(new OpenModalDialogCommand($this->t('Add new context'), $content, array('width' => '700'))); + return $response; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'page_manager_variant_context_form'; + } + + /** + * {@inheritdoc} + */ + protected function getContextClass($cached_values) { + return AddVariantStaticContextConfigure::class; + } + + /** + * {@inheritdoc} + */ + protected function getRelationshipClass($cached_values) { + //return AddVariantRelationshipConfigure::class; + } + + /** + * {@inheritdoc} + */ + protected function getContextAddRoute($cached_values) { + return 'entity.page_variant.add_step_form.context.add'; + } + + /** + * {@inheritdoc} + */ + protected function getRelationshipAddRoute($cached_values) { + return 'entity.page_variant.add_step_form.context.add'; + } + + /** + * {@inheritdoc} + */ + protected function getTempstoreId() { + return 'page_manager.page_variant'; + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $cached_values['page_variant']; + return $page_variant->getContexts(); + } + + /** + * {@inheritdoc} + */ + protected function getContextOperationsRouteInfo($cached_values, $machine_name, $row) { + /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = $cached_values['page_variant']; + return ['entity.page_variant.add_step_form.context', [ + 'page' => $page_variant->getPage()->id(), + 'machine_name' => $machine_name, + 'context_id' => $row + ]]; + } + + /** + * {@inheritdoc} + */ + protected function getRelationshipOperationsRouteInfo($cached_values, $machine_name, $row) { + /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = $cached_values['page_variant']; + return ['entity.page_variant.add_step_form.context', [ + 'page' => $page_variant->getPage()->id(), + 'machine_name' => $machine_name, + 'context_id' => $row + ]]; + } + + protected function isEditableContext($cached_values, $row) { + /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = $cached_values['page_variant']; + $page = $page_variant->getPage(); + return empty($page->getContexts()[$row]) && !empty($page_variant->getContexts()[$row]); + } + + +} diff --git a/page_manager_ui/src/Form/AddVariantSelectionConfigure.php b/page_manager_ui/src/Form/AddVariantSelectionConfigure.php new file mode 100644 index 0000000..854af46 --- /dev/null +++ b/page_manager_ui/src/Form/AddVariantSelectionConfigure.php @@ -0,0 +1,64 @@ +getPageVariant($cached_values); + return ['entity.page_variant.add_step_form', [ + 'page' => $page_variant->getPage()->id(), + 'machine_name' => $this->machine_name, + 'step' => 'selection', + ]]; + } + + /** + * {@inheritdoc} + */ + protected function getConditions($cached_values) { + $page_variant = $this->getPageVariant($cached_values); + return $page_variant->get('selection_criteria'); + } + + /** + * {@inheritdoc} + */ + protected function setConditions($cached_values, $conditions) { + $page_variant = $this->getPageVariant($cached_values); + $page_variant->set('selection_criteria', $conditions); + return $cached_values; + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + $page_variant = $this->getPageVariant($cached_values); + return $page_variant->getContexts(); + } + +} diff --git a/page_manager_ui/src/Form/AddVariantSelectionDelete.php b/page_manager_ui/src/Form/AddVariantSelectionDelete.php new file mode 100644 index 0000000..3ab11c6 --- /dev/null +++ b/page_manager_ui/src/Form/AddVariantSelectionDelete.php @@ -0,0 +1,64 @@ +getPageVariant($cached_values); + return ['entity.page_variant.add_step_form', [ + 'page' => $page_variant->getPage()->id(), + 'machine_name' => $this->machine_name, + 'step' => 'selection', + ]]; + } + + /** + * {@inheritdoc} + */ + protected function getConditions($cached_values) { + $page_variant = $this->getPageVariant($cached_values); + return $page_variant->get('selection_criteria'); + } + + /** + * {@inheritdoc} + */ + protected function setConditions($cached_values, $conditions) { + $page_variant = $this->getPageVariant($cached_values); + $page_variant->set('selection_criteria', $conditions); + return $cached_values; + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + $page_variant = $this->getPageVariant($cached_values); + return $page_variant->getContexts(); + } + +} diff --git a/page_manager_ui/src/Form/AddVariantSelectionForm.php b/page_manager_ui/src/Form/AddVariantSelectionForm.php new file mode 100644 index 0000000..f80bea3 --- /dev/null +++ b/page_manager_ui/src/Form/AddVariantSelectionForm.php @@ -0,0 +1,105 @@ + $page_variant->getPage()->id(), + 'machine_name' => $machine_name, + 'condition' => $row + ]]; + } + + /** + * {@inheritdoc} + */ + protected function getConditions($cached_values) { + /** @var $page \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $cached_values['page_variant']; + return $page_variant->get('selection_criteria'); + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $cached_values['page_variant']; + return $page_variant->getContexts(); + } + + /** + * {@inheritdoc} + */ + protected function getAddRoute($cached_values) { + return 'entity.page_variant.add_step_form.condition.add'; + } + + /** + * {@inheritdoc} + */ + public function add(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + $page_variant = $cached_values['page_variant']; + $condition = $form_state->getValue('conditions'); + $content = \Drupal::formBuilder()->getForm($this->getConditionClass(), $condition, $this->getTempstoreId(), $this->machine_name, $page_variant->id()); + $content['#attached']['library'][] = 'core/drupal.dialog.ajax'; + list(, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('conditions')); + $content['submit']['#attached']['drupalSettings']['ajax'][$content['submit']['#id']]['url'] = $this->url($this->getAddRoute($cached_values), $route_parameters, ['query' => [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]]); + $response = new AjaxResponse(); + $response->addCommand(new OpenModalDialogCommand($this->t('Configure Required Context'), $content, array('width' => '700'))); + return $response; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $triggering_element = $form_state->getTriggeringElement(); + if ($triggering_element['#value']->getUntranslatedString() != 'Add Condition') { + return; + } + parent::submitForm($form, $form_state); + } + +} diff --git a/page_manager_ui/src/Form/AddVariantStaticContextConfigure.php b/page_manager_ui/src/Form/AddVariantStaticContextConfigure.php new file mode 100644 index 0000000..6f33c0f --- /dev/null +++ b/page_manager_ui/src/Form/AddVariantStaticContextConfigure.php @@ -0,0 +1,81 @@ +getPageVariant($cached_values); + return ['entity.page_variant.add_step_form', [ + 'page' => $page_variant->getPage()->id(), + 'machine_name' => $this->machine_name, + 'step' => 'contexts', + ]]; + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + return $this->getPageVariant($cached_values)->getContexts(); + } + + /** + * {@inheritdoc} + */ + protected function addContext($cached_values, $context_id, ContextInterface $context) { + /** @var $page_variant \Drupal\page_manager\PageVariantInterface */ + $page_variant = $this->getPageVariant($cached_values); + $context_config = [ + 'label' => $context->getContextDefinition()->getLabel(), + 'type' => $context->getContextDefinition()->getDataType(), + 'description' => $context->getContextDefinition()->getDescription(), + 'value' => strpos($context->getContextDefinition()->getDataType(), 'entity:') === 0 ? $context->getContextValue()->uuid() : $context->getContextValue(), + ]; + $page_variant->setStaticContext($context_id, $context_config); + $cached_values['page_variant'] = $page_variant; + return $cached_values; + } + + /** + * {@inheritdoc} + */ + public function contextExists($value, $element, $form_state) { + return FALSE; + } + + /** + * {@inheritdoc} + */ + protected function disableMachineName($cached_values, $machine_name) { + if ($machine_name) { + return !empty($this->getContexts($cached_values)[$machine_name]); + } + return FALSE; + } + +} diff --git a/page_manager_ui/src/Form/AddVariantStaticContextDeleteForm.php b/page_manager_ui/src/Form/AddVariantStaticContextDeleteForm.php new file mode 100644 index 0000000..5adb99d --- /dev/null +++ b/page_manager_ui/src/Form/AddVariantStaticContextDeleteForm.php @@ -0,0 +1,72 @@ +getTempstore(); + /** @var $page \Drupal\page_manager\PageInterface */ + $page_variant = $this->getPageVariant($cached_values); + return $this->t('Are you sure you want to delete the static context %label?', ['%label' => $page_variant->getStaticContext($this->context_id)['label']]); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + $cached_values = $this->getTempstore(); + $page_variant = $this->getPageVariant($cached_values); + return new Url('entity.page_variant.add_step_form', [ + 'page' => $page_variant->getPage()->id(), + 'machine_name' => $this->machine_name, + 'step' => 'contexts', + ]); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $this->getTempstore(); + /** @var $page \Drupal\page_manager\PageInterface */ + $page_variant = $this->getPageVariant($cached_values); + drupal_set_message($this->t('The static context %label has been removed.', ['%label' => $page_variant->getStaticContext($this->context_id)['label']])); + $page_variant->removeStaticContext($this->context_id); + $this->setTempstore($cached_values); + parent::submitForm($form, $form_state); + } + + /** + * Get the page variant. + * + * @param array $cached_values + * The cached values from the wizard. + * + * @return \Drupal\page_manager\PageVariantInterface + */ + protected function getPageVariant($cached_values) { + return $cached_values['page_variant']; + } + +} diff --git a/page_manager_ui/src/Form/ConditionFormBase.php b/page_manager_ui/src/Form/ConditionFormBase.php deleted file mode 100644 index c9f4105..0000000 --- a/page_manager_ui/src/Form/ConditionFormBase.php +++ /dev/null @@ -1,110 +0,0 @@ -condition = $this->prepareCondition($condition_id); - $temporary = $form_state->getTemporary(); - $temporary['gathered_contexts'] = $contexts; - $form_state->setTemporary($temporary); - - // Allow the condition to add to the form. - $form['condition'] = $this->condition->buildConfigurationForm([], $form_state); - $form['condition']['#tree'] = TRUE; - - $form['actions'] = ['#type' => 'actions']; - $form['actions']['submit'] = [ - '#type' => 'submit', - '#value' => $this->submitButtonText(), - '#button_type' => 'primary', - ]; - - return $form; - } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, FormStateInterface $form_state) { - // Allow the condition to validate the form. - $condition_values = (new FormState())->setValues($form_state->getValue('condition')); - $this->condition->validateConfigurationForm($form, $condition_values); - // Update the original form values. - $form_state->setValue('condition', $condition_values->getValues()); - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - // Allow the condition to submit the form. - $condition_values = (new FormState())->setValues($form_state->getValue('condition')); - $this->condition->submitConfigurationForm($form, $condition_values); - // Update the original form values. - $form_state->setValue('condition', $condition_values->getValues()); - - if ($this->condition instanceof ContextAwarePluginInterface) { - $this->condition->setContextMapping($condition_values->getValue('context_mapping', [])); - } - - // Set the submission message. - drupal_set_message($this->submitMessageText()); - } - -} diff --git a/page_manager_ui/src/Form/PageAccessForm.php b/page_manager_ui/src/Form/PageAccessForm.php new file mode 100644 index 0000000..0f95a6b --- /dev/null +++ b/page_manager_ui/src/Form/PageAccessForm.php @@ -0,0 +1,82 @@ + $machine_name, 'condition' => $row]]; + } + + /** + * {@inheritdoc} + */ + protected function getConditions($cached_values) { + /** @var $page \Drupal\page_manager\Entity\Page */ + $page = $cached_values['page']; + return $page->get('access_conditions'); + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + /** @var $page \Drupal\page_manager\Entity\Page */ + $page = $cached_values['page']; + return $page->getContexts(); + } + + /** + * The route to which condition 'add' actions should submit. + * + * @return string + */ + protected function getAddRoute($cached_values) { + return 'entity.page.condition.add'; + } + + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $triggering_element = $form_state->getTriggeringElement(); + if ($triggering_element['#value']->getUntranslatedString() == 'Update') { + return; + } + parent::submitForm($form, $form_state); + } + +} diff --git a/page_manager_ui/src/Form/PageAddForm.php b/page_manager_ui/src/Form/PageAddForm.php deleted file mode 100644 index 3ae2346..0000000 --- a/page_manager_ui/src/Form/PageAddForm.php +++ /dev/null @@ -1,28 +0,0 @@ -t('The %label page has been added.', ['%label' => $this->entity->label()])); - $form_state->setRedirect('entity.page.edit_form', [ - 'page' => $this->entity->id(), - ]); - } - -} diff --git a/page_manager_ui/src/Form/PageEditForm.php b/page_manager_ui/src/Form/PageEditForm.php deleted file mode 100644 index f67afb6..0000000 --- a/page_manager_ui/src/Form/PageEditForm.php +++ /dev/null @@ -1,262 +0,0 @@ - 'checkbox', - '#title' => $this->t('Use admin theme'), - '#default_value' => $this->entity->usesAdminTheme(), - ]; - $attributes = $this->getAjaxAttributes(); - $add_button_attributes = $this->getAjaxButtonAttributes(); - - $form['parameters_section'] = $this->buildParametersForm(); - - $form['variant_section'] = [ - '#type' => 'details', - '#title' => $this->t('Variants'), - '#open' => TRUE, - ]; - $form['variant_section']['add_new_page'] = [ - '#type' => 'link', - '#title' => $this->t('Add new variant'), - '#url' => Url::fromRoute('page_manager.variant_select', [ - 'page' => $this->entity->id(), - ]), - '#attributes' => $add_button_attributes, - '#attached' => [ - 'library' => [ - 'core/drupal.ajax', - ], - ], - ]; - $form['variant_section']['variants'] = [ - '#type' => 'table', - '#header' => [ - $this->t('Label'), - $this->t('Plugin'), - $this->t('Weight'), - $this->t('Operations'), - ], - '#empty' => $this->t('There are no variants.'), - '#tabledrag' => [[ - 'action' => 'order', - 'relationship' => 'sibling', - 'group' => 'variant-weight', - ]], - ]; - /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ - foreach ($this->entity->getVariants() as $page_variant) { - $row = [ - '#attributes' => [ - 'class' => ['draggable'], - ], - ]; - $row['label']['#markup'] = $page_variant->label(); - $row['id']['#markup'] = $page_variant->getVariantPlugin()->adminLabel(); - $row['weight'] = [ - '#type' => 'weight', - '#default_value' => $page_variant->getWeight(), - '#title' => $this->t('Weight for @page_variant variant', ['@page_variant' => $page_variant->label()]), - '#title_display' => 'invisible', - '#attributes' => [ - 'class' => ['variant-weight'], - ], - ]; - $operations = []; - $operations['edit'] = [ - 'title' => $this->t('Edit'), - 'url' => $page_variant->toUrl('edit-form'), - ]; - $operations['delete'] = [ - 'title' => $this->t('Delete'), - 'url' => $page_variant->toUrl('delete-form'), - ]; - $row['operations'] = [ - '#type' => 'operations', - '#links' => $operations, - ]; - $form['variant_section']['variants'][$page_variant->id()] = $row; - } - - if ($access_conditions = $this->entity->getAccessConditions()) { - $form['access_section_section'] = [ - '#type' => 'details', - '#title' => $this->t('Access Conditions'), - '#open' => TRUE, - ]; - $form['access_section_section']['add'] = [ - '#type' => 'link', - '#title' => $this->t('Add new access condition'), - '#url' => Url::fromRoute('page_manager.access_condition_select', [ - 'page' => $this->entity->id(), - ]), - '#attributes' => $add_button_attributes, - '#attached' => [ - 'library' => [ - 'core/drupal.ajax', - ], - ], - ]; - $form['access_section_section']['access_section'] = [ - '#type' => 'table', - '#header' => [ - $this->t('Label'), - $this->t('Description'), - $this->t('Operations'), - ], - '#empty' => $this->t('There are no access conditions.'), - ]; - - $form['access_section_section']['access_logic'] = [ - '#type' => 'radios', - '#options' => [ - 'and' => $this->t('All conditions must pass'), - 'or' => $this->t('Only one condition must pass'), - ], - '#default_value' => $this->entity->getAccessLogic(), - ]; - - $form['access_section_section']['access'] = [ - '#tree' => TRUE, - ]; - foreach ($access_conditions as $access_id => $access_condition) { - $row = []; - $row['label']['#markup'] = $access_condition->getPluginDefinition()['label']; - $row['description']['#markup'] = $access_condition->summary(); - $operations = []; - $operations['edit'] = [ - 'title' => $this->t('Edit'), - 'url' => Url::fromRoute('page_manager.access_condition_edit', [ - 'page' => $this->entity->id(), - 'condition_id' => $access_id, - ]), - 'attributes' => $attributes, - ]; - $operations['delete'] = [ - 'title' => $this->t('Delete'), - 'url' => Url::fromRoute('page_manager.access_condition_delete', [ - 'page' => $this->entity->id(), - 'condition_id' => $access_id, - ]), - 'attributes' => $attributes, - ]; - $row['operations'] = [ - '#type' => 'operations', - '#links' => $operations, - ]; - $form['access_section_section']['access_section'][$access_id] = $row; - } - } - return $form; - } - - /** - * Builds the parameters form for a page entity. - * - * @return array - */ - protected function buildParametersForm() { - $form = [ - '#type' => 'details', - '#title' => $this->t('Parameters'), - '#open' => TRUE, - ]; - $form['parameters'] = [ - '#type' => 'table', - '#header' => [ - $this->t('Machine name'), - $this->t('Label'), - $this->t('Type'), - $this->t('Operations'), - ], - '#empty' => $this->t('There are no parameters.'), - ]; - foreach ($this->entity->getParameterNames() as $parameter_name) { - $parameter = $this->entity->getParameter($parameter_name); - $row = []; - $row['machine_name'] = $parameter['machine_name']; - if ($label = $parameter['label']) { - $row['label'] = $label; - } - else { - $row['type']['colspan'] = 2; - } - $row['type']['data'] = $parameter['type'] ?: $this->t('No context assigned'); - - $operations = []; - $operations['edit'] = [ - 'title' => $this->t('Edit'), - 'url' => Url::fromRoute('page_manager.parameter_edit', [ - 'page' => $this->entity->id(), - 'name' => $parameter['machine_name'], - ]), - 'attributes' => $this->getAjaxAttributes(), - ]; - $row['operations']['data'] = [ - '#type' => 'operations', - '#links' => $operations, - ]; - - $form['parameters']['#rows'][$parameter['machine_name']] = $row; - } - return $form; - } - - /** - * {@inheritdoc} - */ - public function save(array $form, FormStateInterface $form_state) { - if (!$form_state->isValueEmpty('variants')) { - foreach ($form_state->getValue('variants') as $variant_id => $data) { - if ($variant_entity = $this->entity->getVariant($variant_id)) { - $variant_entity->setWeight($data['weight']); - $variant_entity->save(); - } - } - } - parent::save($form, $form_state); - drupal_set_message($this->t('The %label page has been updated.', ['%label' => $this->entity->label()])); - $form_state->setRedirect('entity.page.collection'); - } - - /** - * {@inheritdoc} - */ - protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) { - $keys_to_ignore = ['variants', 'parameters']; - $values_to_restore = []; - foreach ($keys_to_ignore as $key) { - $values_to_restore[$key] = $form_state->getValue($key); - $form_state->unsetValue($key); - } - parent::copyFormValuesToEntity($entity, $form, $form_state); - foreach ($values_to_restore as $key => $value) { - $form_state->setValue($key, $value); - } - } - -} diff --git a/page_manager_ui/src/Form/PageFormBase.php b/page_manager_ui/src/Form/PageFormBase.php deleted file mode 100644 index e5c17a8..0000000 --- a/page_manager_ui/src/Form/PageFormBase.php +++ /dev/null @@ -1,120 +0,0 @@ -entityQuery = $entity_query; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('entity.query') - ); - } - - /** - * {@inheritdoc} - */ - public function form(array $form, FormStateInterface $form_state) { - $form['label'] = [ - '#type' => 'textfield', - '#title' => $this->t('Label'), - '#description' => $this->t('The label for this page.'), - '#default_value' => $this->entity->label(), - '#required' => TRUE, - '#maxlength' => '255', - ]; - $form['id'] = [ - '#type' => 'machine_name', - '#default_value' => $this->entity->id(), - '#disabled' => !$this->entity->isNew(), - '#maxlength' => 64, - '#required' => TRUE, - '#machine_name' => [ - 'exists' => [$this, 'exists'], - ], - ]; - $form['path'] = [ - '#type' => 'textfield', - '#title' => $this->t('Path'), - '#maxlength' => 255, - '#default_value' => $this->entity->getPath(), - '#required' => TRUE, - '#element_validate' => [[$this, 'validatePath']], - ]; - - return parent::form($form, $form_state); - } - - /** - * Determines if the page entity already exists. - * - * @param string $id - * The page entity ID. - * - * @return bool - * TRUE if the format exists, FALSE otherwise. - */ - public function exists($id) { - return (bool) $this->entityQuery->get('page') - ->condition('id', $id) - ->execute(); - } - - /** - * {@inheritdoc} - */ - public function validatePath(&$element, FormStateInterface $form_state) { - // Ensure the path has a leading slash. - $value = '/' . trim($element['#value'], '/'); - $form_state->setValueForElement($element, $value); - - // Ensure each path is unique. - $path = $this->entityQuery->get('page') - ->condition('path', $value) - ->condition('id', $form_state->getValue('id'), '<>') - ->execute(); - if ($path) { - $form_state->setErrorByName('path', $this->t('The page path must be unique.')); - } - } - -} diff --git a/page_manager_ui/src/Form/PageGeneralForm.php b/page_manager_ui/src/Form/PageGeneralForm.php new file mode 100644 index 0000000..2cf26b1 --- /dev/null +++ b/page_manager_ui/src/Form/PageGeneralForm.php @@ -0,0 +1,189 @@ +variantManager = $variant_manager; + $this->entityQuery = $entity_query; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.display_variant'), + $container->get('entity.query') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'page_manager_general_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var $page \Drupal\page_manager\Entity\Page */ + $page = $cached_values['page']; + $form['description'] = [ + '#type' => 'textarea', + '#title' => $this->t('Administrative description'), + '#default_value' => $page->getDescription(), + ]; + $form['path'] = [ + '#type' => 'textfield', + '#title' => $this->t('Path'), + '#maxlength' => 255, + '#default_value' => $page->getPath(), + '#required' => TRUE, + '#element_validate' => [[$this, 'validatePath']], + ]; + $form['use_admin_theme'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Use admin theme'), + '#default_value' => $page->usesAdminTheme(), + ]; + + if ($page->isNew()) { + $variant_plugin_options = []; + foreach ($this->variantManager->getDefinitions() as $plugin_id => $definition) { + // The following two variants are provided by Drupal Core. They are not + // configurable and therefore not compatible with Page Manager but have + // similar and confusing labels. Skip them so that they are not shown in + // the UI. + if (in_array($plugin_id, ['simple_page', 'block_page'])) { + continue; + } + + $variant_plugin_options[$plugin_id] = $definition['admin_label']; + } + $form['variant_plugin_id'] = [ + '#title' => $this->t('Variant type'), + '#type' => 'select', + '#options' => $variant_plugin_options, + '#default_value' => !empty($cached_values['variant_plugin_id']) ? $cached_values['variant_plugin_id'] : '', + ]; + $form['wizard_options'] = [ + '#type' => 'checkboxes', + '#title' => $this->t('Optional features'), + '#description' => $this->t('Check any optional features you need to be presented with forms for configuring them. If you do not check them here you will still be able to utilize these features once the new page is created. If you are not sure, leave these unchecked.'), + '#options' => [ + 'access' => $this->t('Page access'), + 'contexts' => $this->t('Variant contexts'), + 'selection' => $this->t('Variant selection criteria'), + ], + '#default_value' => !empty($cached_values['wizard_options']) ? $cached_values['wizard_options'] : [], + ]; + } + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var $page \Drupal\page_manager\Entity\Page */ + $page = $cached_values['page']; + $page->set('description', $form_state->getValue('description')); + $page->set('path', $form_state->getValue('path')); + $page->set('use_admin_theme', $form_state->getValue('use_admin_theme')); + + if ($page->isNew()) { + $page->set('id', $form_state->getValue('id')); + $page->set('label', $form_state->getValue('label')); + if (empty($cached_values['variant_plugin_id'])) { + $variant_plugin_id = $cached_values['variant_plugin_id'] = $form_state->getValue('variant_plugin_id'); + /* @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = \Drupal::entityManager() + ->getStorage('page_variant') + ->create([ + 'variant' => $form_state->getValue('variant_plugin_id'), + 'page' => $page->id(), + 'id' => "{$page->id()}-{$variant_plugin_id}-0", + 'label' => $form['variant_plugin_id']['#options'][$variant_plugin_id], + ]); + $page_variant->setPageEntity($page); + $page->addVariant($page_variant); + $cached_values['page_variant'] = $page_variant; + } + if ($cached_values['variant_plugin_id'] != $form_state->getValue('variant_plugin_id') && !empty($cached_values['page_variant'])) { + $page_variant = $cached_values['page_variant']; + /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */ + $page_variant->set('variant', $form_state->getValue('variant_plugin_id')); + $page_variant->set('variant_settings', []); + $cached_values['variant_plugin_id'] = $form_state->getValue('variant_plugin_id'); + } + + $cached_values['wizard_options'] = $form_state->getValue('wizard_options'); + $form_state->setTemporaryValue('wizard', $cached_values); + } + } + + /** + * {@inheritdoc} + */ + public function validatePath(&$element, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var $page \Drupal\page_manager\Entity\Page */ + $page = $cached_values['page']; + + // Ensure the path has a leading slash. + $value = '/' . trim($element['#value'], '/'); + $form_state->setValueForElement($element, $value); + + // Ensure each path is unique. + $path_query = $this->entityQuery->get('page') + ->condition('path', $value); + if (!$page->isNew()) { + $path_query->condition('id', $page->id(), '<>'); + } + $path = $path_query->execute(); + if ($path) { + $form_state->setErrorByName('path', $this->t('The page path must be unique.')); + } + } + +} diff --git a/page_manager_ui/src/Form/PageParametersForm.php b/page_manager_ui/src/Form/PageParametersForm.php new file mode 100644 index 0000000..e1a9a81 --- /dev/null +++ b/page_manager_ui/src/Form/PageParametersForm.php @@ -0,0 +1,126 @@ +getTemporaryValue('wizard'); + $this->machine_name = $cached_values['id']; + $form['items'] = [ + '#type' => 'markup', + '#prefix' => '
', + '#suffix' => '
', + '#theme' => 'table', + '#header' => [ + $this->t('Machine name'), + $this->t('Label'), + $this->t('Type'), + $this->t('Operations'), + ], + '#rows' => $this->renderRows($cached_values), + '#empty' => $this->t('There are no parameters defined for this page.') + ]; + return $form; + } + + protected function renderRows($cached_values) { + $rows = []; + /** @var $page \Drupal\page_manager\Entity\Page */ + $page = $cached_values['page']; + /** + * @var string $parameter + */ + foreach ($page->getParameterNames() as $parameter_name) { + $parameter = $page->getParameter($parameter_name); + $row = []; + $row['machine_name'] = $parameter['machine_name']; + if ($label = $parameter['label']) { + $row['label'] = $label; + } + else { + $row['type']['colspan'] = 2; + } + $row['type']['data'] = $parameter['type'] ?: $this->t('No context assigned'); + + list($route_partial, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $cached_values['id'], $parameter_name); + $build = [ + '#type' => 'operations', + '#links' => $this->getOperations($route_partial, $route_parameters), + ]; + $row['operations']['data'] = $build; + $rows[$parameter_name] = $row; + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + } + + protected function getOperations($route_name_base, array $route_parameters = array()) { + $operations['edit'] = array( + 'title' => t('Edit'), + 'url' => new Url($route_name_base . '.edit', $route_parameters), + 'weight' => 10, + 'attributes' => array( + 'class' => ['use-ajax'], + 'data-dialog-type' => 'modal', + 'data-dialog-options' => Json::encode([ + 'width' => 700, + ]), + ), + ); + return $operations; + } + + /** + * Returns the tempstore id to use. + * + * @return string + */ + protected function getTempstoreId() { + return 'page_manager.page'; + } + + protected function getOperationsRouteInfo($cached_values, $machine_name, $row) { + return ['page_manager.parameter', ['machine_name' => $machine_name, 'name' => $row]]; + } + +} diff --git a/page_manager_ui/src/Form/PageReorderVariantsForm.php b/page_manager_ui/src/Form/PageReorderVariantsForm.php new file mode 100644 index 0000000..047e7e1 --- /dev/null +++ b/page_manager_ui/src/Form/PageReorderVariantsForm.php @@ -0,0 +1,148 @@ +tempstore = $tempstore; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('user.shared_tempstore') + ); + } + + /** + * Get the tempstore id. + * + * @return string + */ + protected function getTempstoreId() { + return 'page_manager.page'; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'page_manager_reorder_variants_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, $machine_name = '') { + $cached_values = $this->tempstore->get($this->getTempstoreId())->get($machine_name); + $form_state->setTemporaryValue('wizard', $cached_values); + /** @var $page \Drupal\page_manager\Entity\Page */ + $page = $cached_values['page']; + + $form['variants'] = [ + '#type' => 'table', + '#header' => [ + $this->t('Label'), + $this->t('Plugin'), + $this->t('Weight'), + ], + '#empty' => $this->t('There are no variants.'), + '#tabledrag' => [[ + 'action' => 'order', + 'relationship' => 'sibling', + 'group' => 'variant-weight', + ]], + ]; + + $variants = $page->getVariants(); + // Variants can be resorted, but the getVariants() method is still cached + // so manually invoke the sorting for this UI. + @uasort($variants, [$page, 'variantSortHelper']); + if (!empty($cached_values['deleted_variants'])) { + foreach ($cached_values['deleted_variants'] as $page_variant) { + unset($variants[$page_variant->id()]); + } + } + /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ + foreach ($variants as $page_variant) { + $row = [ + '#attributes' => [ + 'class' => ['draggable'], + ], + ]; + $row['label']['#markup'] = $page_variant->label(); + $row['id']['#markup'] = $page_variant->getVariantPlugin()->adminLabel(); + $row['weight'] = [ + '#type' => 'weight', + '#default_value' => $page_variant->getWeight(), + '#title' => $this->t('Weight for @page_variant variant', ['@page_variant' => $page_variant->label()]), + '#title_display' => 'invisible', + '#attributes' => [ + 'class' => ['variant-weight'], + ], + ]; + $form['variants'][$page_variant->id()] = $row; + } + + $form['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Update'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var \Drupal\page_manager\Entity\Page $page */ + $page = $cached_values['page']; + + foreach ($form_state->getValue('variants') as $id => $values) { + if ($page_variant = $page->getVariant($id)) { + $page_variant->setWeight($values['weight']); + } + } + + $form_state->setRedirect('entity.page.edit_form', [ + 'machine_name' => $page->id(), + 'step' => 'general', + ]); + + $this->tempstore->get($this->getTempstoreId())->set($page->id(), $cached_values); + } + +} diff --git a/page_manager_ui/src/Form/PageVariantAddForm.php b/page_manager_ui/src/Form/PageVariantAddForm.php index 70f6594..62827e3 100644 --- a/page_manager_ui/src/Form/PageVariantAddForm.php +++ b/page_manager_ui/src/Form/PageVariantAddForm.php @@ -7,26 +7,140 @@ namespace Drupal\page_manager_ui\Form; +use Drupal\Core\Display\VariantManager; +use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\page_manager\Entity\PageVariant; +use Drupal\page_manager\PageInterface; +use Drupal\user\SharedTempStoreFactory; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a form for adding a variant. */ -class PageVariantAddForm extends PageVariantFormBase { +class PageVariantAddForm extends FormBase { + + /** + * The variant manager. + * + * @var \Drupal\Core\Display\VariantManager + */ + protected $variantManager; + + /** + * Tempstore factory. + * + * @var \Drupal\user\SharedTempStoreFactory + */ + protected $tempstore; + + /** + * Constructs a new DisplayVariantAddForm. + * + * @param \Drupal\Core\Display\VariantManager $variant_manager + * The variant manager. + */ + public function __construct(VariantManager $variant_manager, SharedTempStoreFactory $tempstore) { + $this->variantManager = $variant_manager; + $this->tempstore = $tempstore; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.display_variant'), + $container->get('user.shared_tempstore') + ); + } /** * {@inheritdoc} */ - protected function submitText() { - return $this->t('Add variant'); + public function getFormId() { + return 'page_manager_add_variant_form'; } /** * {@inheritdoc} */ - public function save(array $form, FormStateInterface $form_state) { - parent::save($form, $form_state); - $form_state->setRedirectUrl($this->getEntity()->toUrl('edit-form')); + public function buildForm(array $form, FormStateInterface $form_state, $machine_name = '') { + $cached_values = $form_state->getTemporaryValue('wizard'); + + $variant_plugin_options = []; + foreach ($this->variantManager->getDefinitions() as $plugin_id => $definition) { + // The following two variants are provided by Drupal Core. They are not + // configurable and therefore not compatible with Page Manager but have + // similar and confusing labels. Skip them so that they are not shown in + // the UI. + if (in_array($plugin_id, ['simple_page', 'block_page'])) { + continue; + } + + $variant_plugin_options[$plugin_id] = $definition['admin_label']; + } + $form['variant_plugin_id'] = [ + '#title' => $this->t('Type'), + '#type' => 'select', + '#options' => $variant_plugin_options, + '#default_value' => !empty($cached_values['variant_plugin_id']) ? $cached_values['variant_plugin_id'] : '', + ]; + $form['wizard_options'] = [ + '#type' => 'checkboxes', + '#title' => $this->t('Optional features'), + '#description' => $this->t('Check any optional features you need to be presented with forms for configuring them. If you do not check them here you will still be able to utilize these features once the new variant is created.'), + '#options' => [ + 'selection' => $this->t('Selection criteria'), + 'contexts' => $this->t('Contexts'), + ], + '#default_value' => !empty($cached_values['wizard_options']) ? $cached_values['wizard_options'] : [], + ]; + + return $form; + } + + /** + * Check if a variant id is taken. + * + * @param \Drupal\page_manager\PageInterface $page + * The page entity. + * @param string $variant_id + * The page variant id to check. + * + * @return bool + * TRUE if the ID is available; FALSE otherwise. + */ + protected function variantExists(PageInterface $page, $variant_id) { + return isset($page->getVariants()[$variant_id]) || PageVariant::load($variant_id); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var $page \Drupal\page_manager\Entity\Page */ + $page = $cached_values['page']; + $variant_plugin_id = $cached_values['variant_plugin_id'] = $form_state->getValue('variant_plugin_id'); + + /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $cached_values['page_variant']; + $page_variant->setVariantPluginId($variant_plugin_id); + $page_variant->set('label', $form_state->getValue('label')); + $page_variant->set('page', $page->id()); + + // Loop over variant ids until one is available. + $variant_id_base = "{$page->id()}-{$variant_plugin_id}"; + $key = 0; + while ($this->variantExists($page, "{$variant_id_base}-{$key}")) { + $key++; + } + + $cached_values['id'] = "{$variant_id_base}-{$key}"; + $page_variant->set('id', $cached_values['id']); + $cached_values['wizard_options'] = $form_state->getValue('wizard_options'); + $form_state->setTemporaryValue('wizard', $cached_values); } } diff --git a/page_manager_ui/src/Form/PageVariantConfigureForm.php b/page_manager_ui/src/Form/PageVariantConfigureForm.php new file mode 100644 index 0000000..a15cb15 --- /dev/null +++ b/page_manager_ui/src/Form/PageVariantConfigureForm.php @@ -0,0 +1,94 @@ +getTemporaryValue('wizard'); + /** @var \Drupal\page_manager\PageInterface $page */ + $page = $cached_values['page']; + /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = $cached_values['page_variant']; + + $form['page_variant_label'] = [ + '#type' => 'textfield', + '#title' => $this->t('Label'), + '#required' => TRUE, + '#size' => 32, + '#maxlength' => 255, + '#default_value' => $page_variant->label(), + ]; + + $variant_plugin = $page_variant->getVariantPlugin(); + $form['variant_settings'] = $variant_plugin->buildConfigurationForm([], (new FormState())->setValues($form_state->getValue('variant_settings', []))); + $form['variant_settings']['#tree'] = TRUE; + + if (!$page->isNew()) { + $form['delete'] = [ + '#type' => 'link', + '#title' => $this->t('Delete this variant'), + '#attributes' => [ + 'class' => ['button', 'use-ajax'], + 'data-dialog-type' => 'modal', + ], + '#url' => new Url('entity.page_variant.delete_form', [ + 'machine_name' => $page->id(), + 'variant_machine_name' => $page_variant->id(), + ]), + ]; + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $cached_values['page_variant']; + + $variant_plugin = $page_variant->getVariantPlugin(); + $variant_plugin->validateConfigurationForm($form['variant_settings'], (new FormState())->setValues($form_state->getValue('variant_settings', []))); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var $page \Drupal\page_manager\Entity\Page */ + $page = $cached_values['page']; + /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $cached_values['page_variant']; + $variant_plugin = $page_variant->getVariantPlugin(); + $variant_plugin->submitConfigurationForm($form['variant_settings'], (new FormState())->setValues($form_state->getValue('variant_settings', []))); + $configuration = $variant_plugin->getConfiguration(); + $page_variant->set('variant_settings', $configuration); + $page_variant->set('label', $form_state->getValue('page_variant_label')); + } + +} diff --git a/page_manager_ui/src/Form/PageVariantContextsForm.php b/page_manager_ui/src/Form/PageVariantContextsForm.php new file mode 100644 index 0000000..d48c138 --- /dev/null +++ b/page_manager_ui/src/Form/PageVariantContextsForm.php @@ -0,0 +1,130 @@ +getTemporaryValue('wizard'); + /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $cached_values['page_variant']; + $context = $form_state->getValue('context'); + $content = $this->formBuilder->getForm($this->getContextClass(), $context, $this->getTempstoreId(), $this->machine_name, $page_variant->id()); + $content['#attached']['library'][] = 'core/drupal.dialog.ajax'; + list(, $route_parameters) = $this->getContextOperationsRouteInfo($cached_values, $this->machine_name, $context); + $content['submit']['#attached']['drupalSettings']['ajax'][$content['submit']['#id']]['url'] = $this->url($this->getContextAddRoute($cached_values), $route_parameters, ['query' => [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]]); + $response = new AjaxResponse(); + $response->addCommand(new OpenModalDialogCommand($this->t('Add new context'), $content, array('width' => '700'))); + return $response; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'page_manager_variant_context_form'; + } + + /** + * {@inheritdoc} + */ + protected function getContextClass($cached_values) { + return StaticContextConfigure::class; + } + + /** + * {@inheritdoc} + */ + protected function getRelationshipClass($cached_values) { + //return VariantRelationshipConfigure::class; + } + + /** + * {@inheritdoc} + */ + protected function getContextAddRoute($cached_values) { + return 'entity.page_variant.context.add'; + } + + /** + * {@inheritdoc} + */ + protected function getRelationshipAddRoute($cached_values) { + return 'entity.page_variant.context.add'; + } + + /** + * {@inheritdoc} + */ + protected function getTempstoreId() { + return 'page_manager.page'; + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $cached_values['page_variant']; + return $page_variant->getContexts(); + } + + /** + * {@inheritdoc} + */ + protected function getContextOperationsRouteInfo($cached_values, $machine_name, $row) { + /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = $cached_values['page_variant']; + return ['entity.page_variant.context', [ + 'machine_name' => $machine_name, + 'variant_machine_name' => $page_variant->id(), + 'context_id' => $row + ]]; + } + + /** + * {@inheritdoc} + */ + protected function getRelationshipOperationsRouteInfo($cached_values, $machine_name, $row) { + /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = $cached_values['page_variant']; + return ['entity.page_variant.relationship', [ + 'machine_name' => $machine_name, + 'variant_machine_name' => $page_variant->id(), + 'relationship' => $row + ]]; + } + + protected function isEditableContext($cached_values, $row) { + /** @var \Drupal\page_manager\PageInterface $page */ + $page = $cached_values['page']; + /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = $cached_values['page_variant']; + return empty($page->getContexts()[$row]) && !empty($page_variant->getContexts()[$row]); + } + + +} diff --git a/page_manager_ui/src/Form/PageVariantDeleteForm.php b/page_manager_ui/src/Form/PageVariantDeleteForm.php index 55b7ae8..1b242e2 100644 --- a/page_manager_ui/src/Form/PageVariantDeleteForm.php +++ b/page_manager_ui/src/Form/PageVariantDeleteForm.php @@ -7,35 +7,74 @@ namespace Drupal\page_manager_ui\Form; -use Drupal\Core\Entity\EntityConfirmFormBase; +use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; +use Drupal\user\SharedTempStoreFactory; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Builds the form to delete a PageVariant. */ -class PageVariantDeleteForm extends EntityConfirmFormBase { +class PageVariantDeleteForm extends ConfirmFormBase { /** - * The entity being used by this form. + * Tempstore factory. * - * @var \Drupal\page_manager\PageVariantInterface + * @var \Drupal\user\SharedTempStoreFactory */ - protected $entity; + protected $tempstore; + + /** + * Constructs a PageVariantDeleteForm. + * + * @param \Drupal\user\SharedTempStoreFactory $tempstore + * The tempstore factory. + */ + public function __construct(SharedTempStoreFactory $tempstore) { + $this->tempstore = $tempstore; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('user.shared_tempstore') + ); + } + + /** + * Get the tempstore id. + * + * @return string + */ + protected function getTempstoreId() { + return 'page_manager.page'; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'page_manager_variant_delete_form'; + } /** * {@inheritdoc} */ public function getQuestion() { - return $this->t('Are you sure you want to delete %name?', ['%name' => $this->entity->label()]); + return $this->t('Are you sure you want to delete this variant?'); } /** * {@inheritdoc} */ public function getCancelUrl() { + $machine_name = $this->getRouteMatch()->getParameter('machine_name'); return new Url('entity.page.edit_form', [ - 'page' => $this->entity->get('page'), + 'machine_name' => $machine_name, + 'step' => 'general', ]); } @@ -50,13 +89,23 @@ class PageVariantDeleteForm extends EntityConfirmFormBase { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - $this->entity->delete(); + $machine_name = $this->getRouteMatch()->getParameter('machine_name'); + $variant_machine_name = $this->getRouteMatch()->getParameter('variant_machine_name'); + $cached_values = $this->tempstore->get($this->getTempstoreId())->get($machine_name); + /** @var \Drupal\page_manager\PageInterface $page */ + $page = $cached_values['page']; + $page_variant = $page->getVariant($variant_machine_name); + + // Add to a list to remove for real later. + $cached_values['deleted_variants'][$variant_machine_name] = $page_variant; drupal_set_message($this->t('The variant %label has been removed.', [ - '%label' => $this->entity->label(), + '%label' => $page_variant->label(), ])); $form_state->setRedirectUrl($this->getCancelUrl()); + + $this->tempstore->get($this->getTempstoreId())->set($page->id(), $cached_values); } } diff --git a/page_manager_ui/src/Form/PageVariantEditForm.php b/page_manager_ui/src/Form/PageVariantEditForm.php deleted file mode 100644 index 138a4cf..0000000 --- a/page_manager_ui/src/Form/PageVariantEditForm.php +++ /dev/null @@ -1,404 +0,0 @@ -t('Update variant'); - } - - /** - * {@inheritdoc} - */ - public function form(array $form, FormStateInterface $form_state) { - $form = parent::form($form, $form_state); - - if ($this->getVariantPlugin() instanceof BlockVariantInterface) { - $form['variant_settings']['block_section'] = $this->buildBlockForm(); - } - - $form['selection_section'] = $this->buildSelectionForm(); - - $form['context'] = $this->buildContextForm(); - - return $form; - } - - /** - * Builds the block form for a variant. - * - * @return array - */ - protected function buildBlockForm() { - $variant_plugin = $this->getVariantPlugin(); - if (!$variant_plugin instanceof BlockVariantInterface) { - return []; - } - - /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ - $page_variant = $this->getEntity(); - - // Set up the attributes used by a modal to prevent duplication later. - $attributes = $this->getAjaxAttributes(); - $add_button_attributes = $this->getAjaxButtonAttributes(); - - $form = []; - if ($block_assignments = $variant_plugin->getRegionAssignments()) { - // Build a table of all blocks used by this variant. - $form = [ - '#type' => 'details', - '#title' => $this->t('Blocks'), - '#open' => TRUE, - ]; - $form['add'] = [ - '#type' => 'link', - '#title' => $this->t('Add new block'), - '#url' => Url::fromRoute('page_manager.variant_select_block', [ - 'page' => $page_variant->get('page'), - 'page_variant' => $page_variant->id(), - ]), - '#attributes' => $add_button_attributes, - '#attached' => [ - 'library' => [ - 'core/drupal.ajax', - ], - ], - ]; - $form['blocks'] = [ - '#type' => 'table', - '#header' => [ - $this->t('Label'), - $this->t('Plugin ID'), - $this->t('Region'), - $this->t('Weight'), - $this->t('Operations'), - ], - '#empty' => $this->t('There are no regions for blocks.'), - // @todo This should utilize https://drupal.org/node/2065485. - '#parents' => ['variant_plugin', 'blocks'], - ]; - // Loop through the blocks per region. - foreach ($block_assignments as $region => $blocks) { - // Add a section for each region and allow blocks to be dragged between - // them. - $form['blocks']['#tabledrag'][] = [ - 'action' => 'match', - 'relationship' => 'sibling', - 'group' => 'block-region-select', - 'subgroup' => 'block-region-' . $region, - 'hidden' => FALSE, - ]; - $form['blocks']['#tabledrag'][] = [ - 'action' => 'order', - 'relationship' => 'sibling', - 'group' => 'block-weight', - 'subgroup' => 'block-weight-' . $region, - ]; - $form['blocks'][$region] = [ - '#attributes' => [ - 'class' => ['region-title', 'region-title-' . $region], - 'no_striping' => TRUE, - ], - ]; - $form['blocks'][$region]['title'] = [ - '#markup' => $variant_plugin->getRegionName($region), - '#wrapper_attributes' => [ - 'colspan' => 5, - ], - ]; - $form['blocks'][$region . '-message'] = [ - '#attributes' => [ - 'class' => [ - 'region-message', - 'region-' . $region . '-message', - empty($blocks) ? 'region-empty' : 'region-populated', - ], - ], - ]; - $form['blocks'][$region . '-message']['message'] = [ - '#markup' => '' . $this->t('No blocks in this region') . '', - '#wrapper_attributes' => [ - 'colspan' => 5, - ], - ]; - - /** @var \Drupal\Core\Block\BlockPluginInterface[] $blocks */ - foreach ($blocks as $block_id => $block) { - $row = [ - '#attributes' => [ - 'class' => ['draggable'], - ], - ]; - $row['label']['#markup'] = $block->label(); - $row['id']['#markup'] = $block->getPluginId(); - // Allow the region to be changed for each block. - $row['region'] = [ - '#title' => $this->t('Region'), - '#title_display' => 'invisible', - '#type' => 'select', - '#options' => $variant_plugin->getRegionNames(), - '#default_value' => $variant_plugin->getRegionAssignment($block_id), - '#attributes' => [ - 'class' => ['block-region-select', 'block-region-' . $region], - ], - ]; - // Allow the weight to be changed for each block. - $configuration = $block->getConfiguration(); - $row['weight'] = [ - '#type' => 'weight', - '#default_value' => isset($configuration['weight']) ? $configuration['weight'] : 0, - '#title' => $this->t('Weight for @block block', ['@block' => $block->label()]), - '#title_display' => 'invisible', - '#attributes' => [ - 'class' => ['block-weight', 'block-weight-' . $region], - ], - ]; - // Add the operation links. - $operations = []; - $operations['edit'] = [ - 'title' => $this->t('Edit'), - 'url' => Url::fromRoute('page_manager.variant_edit_block', [ - 'page' => $page_variant->get('page'), - 'page_variant' => $page_variant->id(), - 'block_id' => $block_id, - ]), - 'attributes' => $attributes, - ]; - $operations['delete'] = [ - 'title' => $this->t('Delete'), - 'url' => Url::fromRoute('page_manager.variant_delete_block', [ - 'page' => $page_variant->get('page'), - 'page_variant' => $page_variant->id(), - 'block_id' => $block_id, - ]), - 'attributes' => $attributes, - ]; - - $row['operations'] = [ - '#type' => 'operations', - '#links' => $operations, - ]; - $form['blocks'][$block_id] = $row; - } - } - } - return $form; - } - - /** - * Builds the selection form for a variant. - * - * @return array - */ - protected function buildSelectionForm() { - // Set up the attributes used by a modal to prevent duplication later. - $attributes = $this->getAjaxAttributes(); - $add_button_attributes = $this->getAjaxButtonAttributes(); - - /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ - $page_variant = $this->getEntity(); - - // Selection conditions. - $form = [ - '#type' => 'details', - '#title' => $this->t('Selection Conditions'), - '#open' => TRUE, - ]; - $form['add'] = [ - '#type' => 'link', - '#title' => $this->t('Add new selection condition'), - '#url' => Url::fromRoute('page_manager.selection_condition_select', [ - 'page' => $page_variant->get('page'), - 'page_variant' => $page_variant->id(), - ]), - '#attributes' => $add_button_attributes, - '#attached' => [ - 'library' => [ - 'core/drupal.ajax', - ], - ], - ]; - $form['table'] = [ - '#type' => 'table', - '#header' => [ - $this->t('Label'), - $this->t('Description'), - $this->t('Operations'), - ], - '#empty' => $this->t('There are no selection conditions.'), - ]; - - $form['selection_logic'] = [ - '#type' => 'radios', - '#options' => [ - 'and' => $this->t('All conditions must pass'), - 'or' => $this->t('Only one condition must pass'), - ], - '#default_value' => $page_variant->getSelectionLogic(), - ]; - - $form['selection'] = [ - '#tree' => TRUE, - ]; - foreach ($page_variant->getSelectionConditions() as $selection_id => $selection_condition) { - $row = []; - $row['label']['#markup'] = $selection_condition->getPluginDefinition()['label']; - $row['description']['#markup'] = $selection_condition->summary(); - $operations = []; - $operations['edit'] = [ - 'title' => $this->t('Edit'), - 'url' => Url::fromRoute('page_manager.selection_condition_edit', [ - 'page' => $page_variant->get('page'), - 'page_variant' => $page_variant->id(), - 'condition_id' => $selection_id, - ]), - 'attributes' => $attributes, - ]; - $operations['delete'] = [ - 'title' => $this->t('Delete'), - 'url' => Url::fromRoute('page_manager.selection_condition_delete', [ - 'page' => $page_variant->get('page'), - 'page_variant' => $page_variant->id(), - 'condition_id' => $selection_id, - ]), - 'attributes' => $attributes, - ]; - $row['operations'] = [ - '#type' => 'operations', - '#links' => $operations, - ]; - $form['table'][$selection_id] = $row; - } - - return $form; - } - - /** - * Builds the context form for a variant. - * - * @return array - */ - protected function buildContextForm() { - /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ - $page_variant = $this->getEntity(); - - // Set up the attributes used by a modal to prevent duplication later. - $attributes = $this->getAjaxAttributes(); - $add_button_attributes = $this->getAjaxButtonAttributes(); - - $form = [ - '#type' => 'details', - '#title' => $this->t('Available context'), - '#open' => TRUE, - ]; - $form['add'] = [ - '#type' => 'link', - '#title' => $this->t('Add new static context'), - '#url' => Url::fromRoute('page_manager.static_context_add', [ - 'page' => $page_variant->get('page'), - 'page_variant' => $page_variant->id(), - ]), - '#attributes' => $add_button_attributes, - '#attached' => [ - 'library' => [ - 'core/drupal.ajax', - ], - ], - ]; - $form['available_context'] = [ - '#type' => 'table', - '#header' => [ - $this->t('Label'), - $this->t('Name'), - $this->t('Type'), - $this->t('Operations'), - ], - '#empty' => $this->t('There is no available context.'), - ]; - $contexts = $page_variant->getContexts(); - foreach ($contexts as $name => $context) { - $context_definition = $context->getContextDefinition(); - - $row = []; - $row['label'] = [ - '#markup' => $context_definition->getLabel(), - ]; - $row['machine_name'] = [ - '#markup' => $name, - ]; - $row['type'] = [ - '#markup' => $context_definition->getDataType(), - ]; - - // Add operation links if the context is a static context. - $operations = []; - if ($page_variant->getStaticContext($name)) { - $operations['edit'] = [ - 'title' => $this->t('Edit'), - 'url' => Url::fromRoute('page_manager.static_context_edit', [ - 'page' => $page_variant->get('page'), - 'page_variant' => $page_variant->id(), - 'name' => $name, - ]), - 'attributes' => $attributes, - ]; - $operations['delete'] = [ - 'title' => $this->t('Delete'), - 'url' => Url::fromRoute('page_manager.static_context_delete', [ - 'page' => $page_variant->get('page'), - 'page_variant' => $page_variant->id(), - 'name' => $name, - ]), - 'attributes' => $attributes, - ]; - } - $row['operations'] = [ - '#type' => 'operations', - '#links' => $operations, - ]; - - $form['available_context'][$name] = $row; - } - return $form; - } - - /** - * {@inheritdoc} - */ - public function save(array $form, FormStateInterface $form_state) { - // @todo This feels very wrong. - $variant_plugin = $this->getVariantPlugin(); - if ($variant_plugin instanceof BlockVariantInterface) { - // If the blocks were rearranged, update their values. - if (!$form_state->isValueEmpty(['variant_plugin', 'blocks'])) { - foreach ($form_state->getValue(['variant_plugin', 'blocks']) as $block_id => $block_values) { - $variant_plugin->updateBlock($block_id, $block_values); - } - } - } - - parent::save($form, $form_state); - - $form_state->setRedirect('entity.page.edit_form', ['page' => $this->entity->get('page')]); - } - -} diff --git a/page_manager_ui/src/Form/PageVariantFormBase.php b/page_manager_ui/src/Form/PageVariantFormBase.php deleted file mode 100644 index 719f86f..0000000 --- a/page_manager_ui/src/Form/PageVariantFormBase.php +++ /dev/null @@ -1,177 +0,0 @@ -entityQuery = $entity_query; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('entity.query') - ); - } - - /** - * Returns the text to use for the submit button. - * - * @return string - * The submit button text. - */ - abstract protected function submitText(); - - /** - * {@inheritdoc} - */ - public function form(array $form, FormStateInterface $form_state) { - $form = parent::form($form, $form_state); - - $form['label'] = [ - '#type' => 'textfield', - '#title' => $this->t('Label'), - '#description' => $this->t('The label for this variant.'), - '#default_value' => $this->entity->label() ?: (string) $this->getVariantPlugin()->adminLabel(), - '#maxlength' => '255', - ]; - - $form['id'] = [ - '#type' => 'machine_name', - '#disabled' => !$this->entity->isNew(), - '#default_value' => !$this->entity->isNew() ? $this->entity->id() : '', - '#machine_name' => [ - 'exists' => [$this, 'exists'], - ], - ]; - - // Allow the variant to add to the form. - $form['variant_settings'] = $this->getVariantPlugin()->buildConfigurationForm([], $form_state); - $form['variant_settings']['#tree'] = TRUE; - - $form['actions'] = ['#type' => 'actions']; - $form['actions']['submit'] = [ - '#type' => 'submit', - '#value' => 'Add/Edit', - '#button_type' => 'primary', - ]; - - return $form; - } - - /** - * Determines if the page variant entity already exists. - * - * @param string $id - * The page variant entity ID. - * - * @return bool - * TRUE if the entity exists, FALSE otherwise. - */ - public function exists($id) { - return (bool) $this->entityQuery->get('page_variant') - ->condition('id', $id) - ->execute(); - } - - /** - * {@inheritdoc} - */ - public function save(array $form, FormStateInterface $form_state) { - $status = parent::save($form, $form_state); - - if ($status) { - drupal_set_message($this->t('Saved the %label variant.', [ - '%label' => $this->entity->label(), - ])); - } - else { - drupal_set_message($this->t('The %label variant was not saved.', [ - '%label' => $this->entity->label(), - ])); - } - } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, FormStateInterface $form_state) { - parent::validateForm($form, $form_state); - - // Allow the variant to validate the form. - $variant_plugin_values = (new FormState())->setValues($form_state->getValue('variant_settings')); - $this->getVariantPlugin()->validateConfigurationForm($form, $variant_plugin_values); - // Update the original form values. - $form_state->setValue('variant_settings', $variant_plugin_values->getValues()); - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - // Allow the variant to submit the form. - $variant_plugin_values = (new FormState())->setValues($form_state->getValue('variant_settings')); - $this->getVariantPlugin()->submitConfigurationForm($form, $variant_plugin_values); - // Update the original form values. - $form_state->setValue('variant_settings', $variant_plugin_values->getValues()); - - parent::submitForm($form, $form_state); - } - - /** - * Gets the variant plugin for this page variant entity. - * - * @return \Drupal\Core\Display\VariantInterface - */ - protected function getVariantPlugin() { - if (!$this->variantPlugin) { - $this->variantPlugin = $this->entity->getVariantPlugin(); - } - return $this->variantPlugin; - } - -} diff --git a/page_manager_ui/src/Form/PageVariantSelectionForm.php b/page_manager_ui/src/Form/PageVariantSelectionForm.php new file mode 100644 index 0000000..a344988 --- /dev/null +++ b/page_manager_ui/src/Form/PageVariantSelectionForm.php @@ -0,0 +1,104 @@ + $machine_name, + 'variant_machine_name' => $page_variant->id(), + 'condition' => $row + ]]; + } + + /** + * {@inheritdoc} + */ + protected function getConditions($cached_values) { + /** @var $page \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $cached_values['page_variant']; + return $page_variant->get('selection_criteria'); + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $cached_values['page_variant']; + return $page_variant->getContexts(); + } + + /** + * {@inheritdoc} + */ + protected function getAddRoute($cached_values) { + return 'entity.page_variant.condition.add'; + } + + /** + * {@inheritdoc} + */ + public function add(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + $page_variant = $cached_values['page_variant']; + $condition = $form_state->getValue('conditions'); + $content = \Drupal::formBuilder()->getForm($this->getConditionClass(), $condition, $this->getTempstoreId(), $this->machine_name, $page_variant->id()); + $content['#attached']['library'][] = 'core/drupal.dialog.ajax'; + list(, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('conditions')); + $content['submit']['#attached']['drupalSettings']['ajax'][$content['submit']['#id']]['url'] = $this->url($this->getAddRoute($cached_values), $route_parameters, ['query' => [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]]); + $response = new AjaxResponse(); + $response->addCommand(new OpenModalDialogCommand($this->t('Configure Required Context'), $content, array('width' => '700'))); + return $response; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $triggering_element = $form_state->getTriggeringElement(); + if ($triggering_element['#value']->getUntranslatedString() != 'Add Condition') { + return; + } + parent::submitForm($form, $form_state); + } + +} diff --git a/page_manager_ui/src/Form/ParameterEditForm.php b/page_manager_ui/src/Form/ParameterEditForm.php index 6b53955..fa9cbec 100644 --- a/page_manager_ui/src/Form/ParameterEditForm.php +++ b/page_manager_ui/src/Form/ParameterEditForm.php @@ -13,6 +13,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\TypedData\PrimitiveInterface; use Drupal\Core\TypedData\TypedDataManagerInterface; use Drupal\page_manager\PageInterface; +use Drupal\user\SharedTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -49,16 +50,36 @@ class ParameterEditForm extends FormBase { protected $typedDataManager; /** + * @var \Drupal\user\SharedTempStoreFactory + */ + protected $tempstore; + + /** + * @var string + */ + protected $tempstore_id; + + /** + * The machine name of the page being edited in tempstore. + * + * @var string + */ + protected $machine_name; + + /** * Constructs a new ParameterEditForm. * * @param \Drupal\Core\Entity\EntityTypeRepositoryInterface $entity_type_repository * The entity type repository. * @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager * The typed data manager. + * @param \Drupal\user\SharedTempStoreFactory $tempstore + * The temporary store. */ - public function __construct(EntityTypeRepositoryInterface $entity_type_repository, TypedDataManagerInterface $typed_data_manager) { + public function __construct(EntityTypeRepositoryInterface $entity_type_repository, TypedDataManagerInterface $typed_data_manager, SharedTempStoreFactory $tempstore) { $this->entityTypeRepository = $entity_type_repository; $this->typedDataManager = $typed_data_manager; + $this->tempstore = $tempstore; } /** @@ -67,10 +88,19 @@ class ParameterEditForm extends FormBase { public static function create(ContainerInterface $container) { return new static( $container->get('entity_type.repository'), - $container->get('typed_data_manager') + $container->get('typed_data_manager'), + $container->get('user.shared_tempstore') ); } + protected function getTempstore() { + return $this->tempstore->get($this->tempstore_id)->get($this->machine_name); + } + + protected function setTempstore($cached_values) { + $this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values); + } + /** * {@inheritdoc} */ @@ -81,9 +111,12 @@ class ParameterEditForm extends FormBase { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $name = '') { - $this->page = $page; - $parameter = $this->page->getParameter($name); + public function buildForm(array $form, FormStateInterface $form_state, $name = '', $tempstore_id = NULL, $machine_name = NULL) { + $this->tempstore_id = $tempstore_id; + $this->machine_name = $machine_name; + $cached_values = $this->getTempstore(); + $page = $cached_values['page']; + $parameter = $page->getParameter($name); $form['machine_name'] = [ '#type' => 'value', @@ -153,23 +186,48 @@ class ParameterEditForm extends FormBase { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { + $cache_values = $this->getTempstore(); + /** @var \Drupal\page_manager\PageInterface $page */ + $page = $cache_values['page']; $name = $form_state->getValue('machine_name'); $type = $form_state->getValue('type'); - if ($type === static::NO_CONTEXT_KEY) { - $this->page->removeParameter($name); + $page->removeParameter($name); $label = NULL; } else { $label = $form_state->getValue('label'); - $this->page->setParameter($name, $type, $label); + $page->setParameter($name, $type, $label); } - $this->page->save(); - // Set the submission message. + $this->setTempstore($cache_values); drupal_set_message($this->t('The %label parameter has been updated.', ['%label' => $label ?: $name])); + list($route_name, $route_parameters) = $this->getParentRouteInfo($cache_values); + $form_state->setRedirect($route_name, $route_parameters); + } - $form_state->setRedirectUrl($this->page->toUrl('edit-form')); + /** + * Returns the parent route to redirect after form submission. + * + * @return array + * Array containing the route name and its parameters. + */ + protected function getParentRouteInfo($cached_values) { + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + + if ($page->isNew()) { + return ['entity.page.add_step_form', [ + 'machine_name' => $this->machine_name, + 'step' => 'parameters', + ]]; + } + else { + return ['entity.page.edit_form', [ + 'machine_name' => $this->machine_name, + 'step' => 'parameters', + ]]; + } } } diff --git a/page_manager_ui/src/Form/SelectionConditionAddForm.php b/page_manager_ui/src/Form/SelectionConditionAddForm.php deleted file mode 100644 index c392745..0000000 --- a/page_manager_ui/src/Form/SelectionConditionAddForm.php +++ /dev/null @@ -1,73 +0,0 @@ -conditionManager = $condition_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('plugin.manager.condition') - ); - } - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'page_manager_selection_condition_add_form'; - } - - /** - * {@inheritdoc} - */ - protected function prepareCondition($condition_id) { - // Create a new selection condition instance. - return $this->conditionManager->createInstance($condition_id); - } - - /** - * {@inheritdoc} - */ - protected function submitButtonText() { - return $this->t('Add selection condition'); - } - - /** - * {@inheritdoc} - */ - protected function submitMessageText() { - return $this->t('The %label selection condition has been added.', ['%label' => $this->condition->getPluginDefinition()['label']]); - } - -} diff --git a/page_manager_ui/src/Form/SelectionConditionDeleteForm.php b/page_manager_ui/src/Form/SelectionConditionDeleteForm.php deleted file mode 100644 index 4795fa2..0000000 --- a/page_manager_ui/src/Form/SelectionConditionDeleteForm.php +++ /dev/null @@ -1,80 +0,0 @@ -t('Are you sure you want to delete the selection condition %name?', ['%name' => $this->selectionCondition->getPluginDefinition()['label']]); - } - - /** - * {@inheritdoc} - */ - public function getCancelUrl() { - return $this->pageVariant->toUrl('edit-form'); - } - - /** - * {@inheritdoc} - */ - public function getConfirmText() { - return $this->t('Delete'); - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $condition_id = NULL) { - $this->pageVariant = $page_variant; - $this->selectionCondition = $page_variant->getSelectionCondition($condition_id); - return parent::buildForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - $this->pageVariant->removeSelectionCondition($this->selectionCondition->getConfiguration()['uuid']); - $this->pageVariant->save(); - drupal_set_message($this->t('The selection condition %name has been removed.', ['%name' => $this->selectionCondition->getPluginDefinition()['label']])); - $form_state->setRedirectUrl($this->getCancelUrl()); - } - -} diff --git a/page_manager_ui/src/Form/SelectionConditionEditForm.php b/page_manager_ui/src/Form/SelectionConditionEditForm.php deleted file mode 100644 index 416a724..0000000 --- a/page_manager_ui/src/Form/SelectionConditionEditForm.php +++ /dev/null @@ -1,44 +0,0 @@ -pageVariant->getSelectionCondition($condition_id); - } - - /** - * {@inheritdoc} - */ - protected function submitButtonText() { - return $this->t('Update selection condition'); - } - - /** - * {@inheritdoc} - */ - protected function submitMessageText() { - return $this->t('The %label selection condition has been updated.', ['%label' => $this->condition->getPluginDefinition()['label']]); - } - -} diff --git a/page_manager_ui/src/Form/SelectionConditionFormBase.php b/page_manager_ui/src/Form/SelectionConditionFormBase.php deleted file mode 100644 index e1087ff..0000000 --- a/page_manager_ui/src/Form/SelectionConditionFormBase.php +++ /dev/null @@ -1,51 +0,0 @@ -pageVariant = $page_variant; - return parent::buildForm($form, $form_state, $condition_id, $page_variant->getContexts()); - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - parent::submitForm($form, $form_state); - - $configuration = $this->condition->getConfiguration(); - // If this selection condition is new, add it to the page. - if (!isset($configuration['uuid'])) { - $this->pageVariant->addSelectionCondition($configuration); - } - - // Save the page entity. - $this->pageVariant->save(); - - $form_state->setRedirectUrl($this->pageVariant->toUrl('edit-form')); - } - -} diff --git a/page_manager_ui/src/Form/SelectionConfigure.php b/page_manager_ui/src/Form/SelectionConfigure.php new file mode 100644 index 0000000..23b933d --- /dev/null +++ b/page_manager_ui/src/Form/SelectionConfigure.php @@ -0,0 +1,95 @@ +getVariant($this->variantMachineName); + } + + /** + * {@inheritdoc} + */ + protected function getParentRouteInfo($cached_values) { + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + + if ($page->isNew()) { + return ['entity.page.add_step_form', [ + 'machine_name' => $this->machine_name, + 'step' => 'selection', + ]]; + } + else { + $page_variant = $this->getPageVariant($cached_values); + return ['entity.page.edit_form', [ + 'machine_name' => $this->machine_name, + 'step' => 'page_variant__' . $page_variant->id() . '__selection', + ]]; + } + } + + /** + * @inheritDoc + */ + public function buildForm(array $form, FormStateInterface $form_state, $condition = NULL, $tempstore_id = NULL, $machine_name = NULL, $variant_machine_name = NULL) { + $this->variantMachineName = $variant_machine_name; + return parent::buildForm($form, $form_state, $condition, $tempstore_id, $machine_name); + } + + /** + * {@inheritdoc} + */ + protected function getConditions($cached_values) { + $page_variant = $this->getPageVariant($cached_values); + return $page_variant->get('selection_criteria'); + } + + /** + * {@inheritdoc} + */ + protected function setConditions($cached_values, $conditions) { + $page_variant = $this->getPageVariant($cached_values); + $page_variant->set('selection_criteria', $conditions); + return $cached_values; + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + /** @var $page \Drupal\page_manager\Entity\PageVariant */ + $page_variant = $this->getPageVariant($cached_values); + return $page_variant->getContexts(); + } + +} diff --git a/page_manager_ui/src/Form/SelectionDelete.php b/page_manager_ui/src/Form/SelectionDelete.php new file mode 100644 index 0000000..9c03a50 --- /dev/null +++ b/page_manager_ui/src/Form/SelectionDelete.php @@ -0,0 +1,95 @@ +getVariant($this->variantMachineName); + } + + /** + * {@inheritdoc} + */ + protected function getParentRouteInfo($cached_values) { + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + + if ($page->isNew()) { + return ['entity.page.add_step_form', [ + 'machine_name' => $this->machine_name, + 'step' => 'selection', + ]]; + } + else { + $page_variant = $this->getPageVariant($cached_values); + return ['entity.page.edit_form', [ + 'machine_name' => $this->machine_name, + 'step' => 'page_variant__' . $page_variant->id() . '__selection', + ]]; + } + } + + /** + * @inheritDoc + */ + public function buildForm(array $form, FormStateInterface $form_state, $id = NULL, $tempstore_id = NULL, $machine_name = NULL, $variant_machine_name = NULL) { + $this->variantMachineName = $variant_machine_name; + return parent::buildForm($form, $form_state, $id, $tempstore_id, $machine_name); + } + + + /** + * {@inheritdoc} + */ + protected function getConditions($cached_values) { + $page_variant = $this->getPageVariant($cached_values); + return $page_variant->get('selection_criteria'); + } + + /** + * {@inheritdoc} + */ + protected function setConditions($cached_values, $conditions) { + $page_variant = $this->getPageVariant($cached_values); + $page_variant->set('selection_criteria', $conditions); + return $cached_values; + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + $page_variant = $this->getPageVariant($cached_values); + return $page_variant->getContexts(); + } + +} diff --git a/page_manager_ui/src/Form/StaticContextAddForm.php b/page_manager_ui/src/Form/StaticContextAddForm.php deleted file mode 100644 index a6e9a07..0000000 --- a/page_manager_ui/src/Form/StaticContextAddForm.php +++ /dev/null @@ -1,36 +0,0 @@ -t('Add Static Context'); - } - - /** - * {@inheritdoc} - */ - protected function submitMessageText() { - return $this->t('The %label static context has been added.', ['%label' => $this->staticContext['label']]); - } - -} diff --git a/page_manager_ui/src/Form/StaticContextConfigure.php b/page_manager_ui/src/Form/StaticContextConfigure.php new file mode 100644 index 0000000..bfc8edf --- /dev/null +++ b/page_manager_ui/src/Form/StaticContextConfigure.php @@ -0,0 +1,119 @@ +getVariant($this->variantMachineName); + } + + /** + * {@inheritdoc} + */ + protected function getParentRouteInfo($cached_values) { + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + + if ($page->isNew()) { + return ['entity.page.add_step_form', [ + 'machine_name' => $this->machine_name, + 'step' => 'contexts', + ]]; + } + else { + $page_variant = $this->getPageVariant($cached_values); + return ['entity.page.edit_form', [ + 'machine_name' => $this->machine_name, + 'step' => 'page_variant__' . $page_variant->id() . '__contexts', + ]]; + } + } + + /** + * {@inheritdoc} + * + * Overridden to set the variantMachineName. + */ + public function buildForm(array $form, FormStateInterface $form_state, $context_id = NULL, $tempstore_id = NULL, $machine_name = NULL, $variant_machine_name = NULL) { + $this->variantMachineName = $variant_machine_name; + return parent::buildForm($form, $form_state, $context_id, $tempstore_id, $machine_name); + } + + /** + * {@inheritdoc} + */ + protected function getContexts($cached_values) { + /** @var $page_variant \Drupal\page_manager\PageVariantInterface */ + $page_variant = !empty($cached_values['page_variant']) ? $cached_values['page_variant'] : NULL; + if (is_null($page_variant)) { + $page_variant = $this->getPageVariant($cached_values); + } + return $page_variant->getContexts(); + } + + /** + * {@inheritdoc} + */ + protected function addContext($cached_values, $context_id, ContextInterface $context) { + /** @var $page_variant \Drupal\page_manager\PageVariantInterface */ + $page_variant = $this->getPageVariant($cached_values); + $context_config = [ + 'label' => $context->getContextDefinition()->getLabel(), + 'type' => $context->getContextDefinition()->getDataType(), + 'description' => $context->getContextDefinition()->getDescription(), + 'value' => strpos($context->getContextDefinition()->getDataType(), 'entity:') === 0 ? $context->getContextValue()->uuid() : $context->getContextValue(), + ]; + $page_variant->setStaticContext($context_id, $context_config); + $cached_values['page_variant'] = $page_variant; + return $cached_values; + } + + /** + * {@inheritdoc} + */ + public function contextExists($value, $element, $form_state) { + return FALSE; + } + + /** + * {@inheritdoc} + */ + protected function disableMachineName($cached_values, $machine_name) { + if ($machine_name) { + return !empty($this->getContexts($cached_values)[$machine_name]); + } + return FALSE; + } + +} diff --git a/page_manager_ui/src/Form/StaticContextDeleteForm.php b/page_manager_ui/src/Form/StaticContextDeleteForm.php index 554b27c..0206a31 100644 --- a/page_manager_ui/src/Form/StaticContextDeleteForm.php +++ b/page_manager_ui/src/Form/StaticContextDeleteForm.php @@ -8,27 +8,20 @@ namespace Drupal\page_manager_ui\Form; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Form\ConfirmFormBase; -use Drupal\page_manager\PageVariantInterface; +use Drupal\Core\Url; +use Drupal\ctools\Form\ContextDelete; /** * Provides a form for deleting an access condition. */ -class StaticContextDeleteForm extends ConfirmFormBase { +class StaticContextDeleteForm extends ContextDelete { /** - * The page variant entity this selection condition belongs to. + * The machine-name of the variant. * - * @var \Drupal\page_manager\PageVariantInterface + * @var string */ - protected $pageVariant; - - /** - * The static context's machine name. - * - * @var array - */ - protected $staticContext; + protected $variantMachineName; /** * {@inheritdoc} @@ -41,40 +34,72 @@ class StaticContextDeleteForm extends ConfirmFormBase { * {@inheritdoc} */ public function getQuestion() { - return $this->t('Are you sure you want to delete the static context %label?', ['%label' => $this->pageVariant->getStaticContext($this->staticContext)['label']]); + $cached_values = $this->getTempstore(); + /** @var $page \Drupal\page_manager\PageInterface */ + $page_variant = $this->getPageVariant($cached_values); + return $this->t('Are you sure you want to delete the static context %label?', ['%label' => $page_variant->getStaticContext($this->context_id)['label']]); } /** * {@inheritdoc} */ public function getCancelUrl() { - return $this->pageVariant->toUrl('edit-form'); + $cached_values = $this->getTempstore(); + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + + if ($page->isNew()) { + return new Url('entity.page.add_step_form', [ + 'machine_name' => $this->machine_name, + 'step' => 'contexts', + ]); + } + else { + $page_variant = $this->getPageVariant($cached_values); + return new Url('entity.page.edit_form', [ + 'machine_name' => $this->machine_name, + 'step' => 'page_variant__' . $page_variant->id() . '__contexts', + ]); + } } /** * {@inheritdoc} */ - public function getConfirmText() { - return $this->t('Delete'); + public function buildForm(array $form, FormStateInterface $form_state, $tempstore_id = NULL, $machine_name = NULL, $context_id = NULL, $variant_machine_name = NULL) { + $this->variantMachineName = $variant_machine_name; + return parent::buildForm($form, $form_state, $tempstore_id, $machine_name, $context_id); } /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $name = NULL) { - $this->pageVariant = $page_variant; - $this->staticContext = $name; - return parent::buildForm($form, $form_state); + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $this->getTempstore(); + /** @var $page \Drupal\page_manager\PageInterface */ + $page_variant = $this->getPageVariant($cached_values); + drupal_set_message($this->t('The static context %label has been removed.', ['%label' => $page_variant->getStaticContext($this->context_id)['label']])); + $page_variant->removeStaticContext($this->context_id); + $this->setTempstore($cached_values); + parent::submitForm($form, $form_state); } /** - * {@inheritdoc} + * Get the page variant. + * + * @param array $cached_values + * The cached values from the wizard. + * + * @return \Drupal\page_manager\PageVariantInterface */ - public function submitForm(array &$form, FormStateInterface $form_state) { - drupal_set_message($this->t('The static context %label has been removed.', ['%label' => $this->pageVariant->getStaticContext($this->staticContext)['label']])); - $this->pageVariant->removeStaticContext($this->staticContext); - $this->pageVariant->save(); - $form_state->setRedirectUrl($this->getCancelUrl()); + protected function getPageVariant($cached_values) { + if (isset($cached_values['page_variant'])) { + return $cached_values['page_variant']; + } + + /** @var $page \Drupal\page_manager\PageInterface */ + $page = $cached_values['page']; + return $page->getVariant($this->variantMachineName); } } diff --git a/page_manager_ui/src/Form/StaticContextEditForm.php b/page_manager_ui/src/Form/StaticContextEditForm.php deleted file mode 100644 index 88ae79b..0000000 --- a/page_manager_ui/src/Form/StaticContextEditForm.php +++ /dev/null @@ -1,52 +0,0 @@ -t('Update Static Context'); - } - - /** - * {@inheritdoc} - */ - protected function submitMessageText() { - return $this->t('The %label static context has been updated.', ['%label' => $this->staticContext['label']]); - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $name = '') { - $form = parent::buildForm($form, $form_state, $page_variant, $name); - // The machine name of an existing context is read-only. - $form['machine_name'] = array( - '#type' => 'value', - '#value' => $name, - ); - return $form; - } - -} diff --git a/page_manager_ui/src/Form/StaticContextFormBase.php b/page_manager_ui/src/Form/StaticContextFormBase.php deleted file mode 100644 index 4abff8f..0000000 --- a/page_manager_ui/src/Form/StaticContextFormBase.php +++ /dev/null @@ -1,262 +0,0 @@ -entityRepository = $entity_repository; - $this->entityTypeRepository = $entity_type_repository; - $this->entityTypeManager = $entity_type_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('entity.repository'), - $container->get('entity_type.repository'), - $container->get('entity_type.manager') - ); - } - - /** - * Returns the text to use for the submit button. - * - * @return string - * The submit button text. - */ - abstract protected function submitButtonText(); - - /** - * Returns the text to use for the submit message. - * - * @return string - * The submit message text. - */ - abstract protected function submitMessageText(); - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $name = '') { - $this->pageVariant = $page_variant; - $this->staticContext = $this->pageVariant->getStaticContext($name); - - // Allow the condition to add to the form. - $form['label'] = [ - '#type' => 'textfield', - '#title' => $this->t('Label'), - '#default_value' => isset($this->staticContext['label']) ? $this->staticContext['label'] : '', - '#required' => TRUE, - ]; - $form['machine_name'] = [ - '#type' => 'machine_name', - '#maxlength' => 64, - '#required' => TRUE, - '#machine_name' => [ - 'exists' => [$this, 'contextExists'], - 'source' => ['label'], - ], - '#default_value' => $name, - ]; - $form['entity_type'] = [ - '#type' => 'select', - '#title' => $this->t('Entity type'), - '#options' => $this->entityTypeRepository->getEntityTypeLabels(TRUE), - '#limit_validation_errors' => array(array('entity_type')), - '#submit' => ['::rebuildSubmit'], - '#executes_submit_callback' => TRUE, - '#ajax' => array( - 'callback' => '::updateEntityType', - 'wrapper' => 'add-static-context-wrapper', - 'method' => 'replace', - ), - ]; - - $entity = NULL; - if ($form_state->hasValue('entity_type')) { - $entity_type = $form_state->getValue('entity_type'); - if ($this->staticContext['value']) { - $entity = $this->entityRepository->loadEntityByUuid($entity_type, $this->staticContext['value']); - } - } - elseif (!empty($this->staticContext['type'])) { - list(, $entity_type) = explode(':', $this->staticContext['type']); - $entity = $this->entityRepository->loadEntityByUuid($entity_type, $this->staticContext['value']); - } - elseif ($this->entityTypeManager->hasDefinition('node')) { - $entity_type = 'node'; - } - else { - $entity_type = 'user'; - } - - $form['entity_type']['#default_value'] = $entity_type; - - $form['selection'] = [ - '#type' => 'entity_autocomplete', - '#prefix' => '
', - '#suffix' => '
', - '#required' => TRUE, - '#target_type' => $entity_type, - '#default_value' => $entity, - '#title' => $this->t('Select entity'), - ]; - - $form['actions'] = ['#type' => 'actions']; - $form['actions']['submit'] = [ - '#type' => 'submit', - '#value' => $this->submitButtonText(), - '#button_type' => 'primary', - ]; - - return $form; - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - $selection = $form_state->getValue('selection'); - $entity_type = $form_state->getValue('entity_type'); - $entity = $this->getEntityFromSelection($entity_type, $selection); - - $this->staticContext = [ - 'label' => $form_state->getValue('label'), - 'type' => 'entity:' . $entity_type, - 'value' => $entity->uuid(), - ]; - $this->pageVariant->setStaticContext($form_state->getValue('machine_name'), $this->staticContext); - $this->pageVariant->save(); - - // Set the submission message. - drupal_set_message($this->submitMessageText()); - - $form_state->setRedirectUrl($this->pageVariant->toUrl('edit-form')); - } - - /** - * Get the entity from the selection. - * - * @param string $entity_type - * The entity type ID. - * @param string $selection - * The value from the selection box. - * - * @return \Drupal\Core\Entity\Entity|null - * The entity reference in selection. - */ - protected function getEntityFromSelection($entity_type, $selection) { - if (!isset($selection)) { - return NULL; - } - return $this->entityTypeManager->getStorage($entity_type)->load($selection); - } - - /** - * Determines if a context with that name already exists. - * - * @param string $name - * The context name. - * - * @return bool - * TRUE if the format exists, FALSE otherwise. - */ - public function contextExists($name) { - return isset($this->pageVariant->getContexts()[$name]); - } - - /** - * Submit handler for the entity_type select field. - * - * @param array $form - * The form array. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The form state object. - * - * @return $this - */ - public function rebuildSubmit(array $form, FormStateInterface $form_state) { - return $form_state->setRebuild(); - } - - /** - * AJAX callback for the entity_type select field. - * - * @param array $form - * The form array. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The form state object. - * - * @return array - * The updated entity auto complete widget on the form. - */ - public function updateEntityType(array $form, FormStateInterface $form_state) { - return $form['selection']; - } - -} diff --git a/page_manager_ui/src/Form/VariantPluginAddBlockForm.php b/page_manager_ui/src/Form/VariantPluginAddBlockForm.php index f0709b1..a372a12 100644 --- a/page_manager_ui/src/Form/VariantPluginAddBlockForm.php +++ b/page_manager_ui/src/Form/VariantPluginAddBlockForm.php @@ -10,6 +10,7 @@ namespace Drupal\page_manager_ui\Form; use Drupal\Component\Plugin\PluginManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\page_manager\PageVariantInterface; +use Drupal\user\SharedTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -31,7 +32,8 @@ class VariantPluginAddBlockForm extends VariantPluginConfigureBlockFormBase { * @param \Drupal\Component\Plugin\PluginManagerInterface $block_manager * The block manager. */ - public function __construct(PluginManagerInterface $block_manager) { + public function __construct(SharedTempStoreFactory $tempstore, PluginManagerInterface $block_manager) { + parent::__construct($tempstore); $this->blockManager = $block_manager; } @@ -40,6 +42,7 @@ class VariantPluginAddBlockForm extends VariantPluginConfigureBlockFormBase { */ public static function create(ContainerInterface $container) { return new static( + $container->get('user.shared_tempstore'), $container->get('plugin.manager.block') ); } @@ -63,8 +66,8 @@ class VariantPluginAddBlockForm extends VariantPluginConfigureBlockFormBase { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL, PageVariantInterface $page_variant = NULL, $block_id = NULL) { - $form = parent::buildForm($form, $form_state, $page_variant, $block_id); + public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL, $block_display = NULL, $block_id = NULL) { + $form = parent::buildForm($form, $form_state, $block_display, $block_id); $form['region']['#default_value'] = $request->query->get('region'); return $form; } diff --git a/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php b/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php index 7d440ef..59de6c6 100644 --- a/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php +++ b/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php @@ -10,9 +10,12 @@ namespace Drupal\page_manager_ui\Form; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormState; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Plugin\Context\Context; use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait; use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\page_manager\PageVariantInterface; +use Drupal\user\SharedTempStoreFactory; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a base form for configuring a block as part of a variant. @@ -22,11 +25,18 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { use ContextAwarePluginAssignmentTrait; /** - * The page entity. + * Tempstore factory. * - * @var \Drupal\page_manager\PageVariantInterface + * @var \Drupal\user\SharedTempStoreFactory */ - protected $pageVariant; + protected $tempstore; + + /** + * The variant plugin. + * + * @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant + */ + protected $variantPlugin; /** * The plugin being configured. @@ -36,6 +46,43 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { protected $block; /** + * Constructs a new VariantPluginConfigureBlockFormBase. + * + * @param \Drupal\user\SharedTempStoreFactory $tempstore + * The tempstore factory. + */ + public function __construct(SharedTempStoreFactory $tempstore) { + $this->tempstore = $tempstore; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('user.shared_tempstore') + ); + } + + /** + * Get the tempstore id. + * + * @return string + */ + protected function getTempstoreId() { + return 'page_manager.block_display'; + } + + /** + * Get the tempstore. + * + * @return \Drupal\user\SharedTempStore + */ + protected function getTempstore() { + return $this->tempstore->get($this->getTempstoreId()); + } + + /** * Prepares the block plugin based on the block ID. * * @param string $block_id @@ -57,10 +104,24 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $block_id = NULL) { - $this->pageVariant = $page_variant; + public function buildForm(array $form, FormStateInterface $form_state, $block_display = NULL, $block_id = NULL) { + $cached_values = $this->tempstore->get('page_manager.block_display')->get($block_display); + /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */ + $this->variantPlugin = $cached_values['plugin']; + + // Rehydrate the contexts on this end. + $contexts = []; + /** + * @var string $context_name + * @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface $context_definition + */ + foreach ($cached_values['contexts'] as $context_name => $context_definition) { + $contexts[$context_name] = new Context($context_definition); + } + $this->variantPlugin->setContexts($contexts); + $this->block = $this->prepareBlock($block_id); - $form_state->set('page_variant_id', $page_variant->id()); + $form_state->set('variant_id', $this->getVariantPlugin()->id()); $form_state->set('block_id', $this->block->getConfiguration()['uuid']); $form['#tree'] = TRUE; @@ -78,7 +139,7 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { ]; if ($this->block instanceof ContextAwarePluginInterface) { - $form['context_mapping'] = $this->addContextAssignmentElement($this->block, $this->pageVariant->getContexts()); + $form['context_mapping'] = $this->addContextAssignmentElement($this->block, $this->getVariantPlugin()->getContexts()); } $form['actions']['submit'] = [ @@ -120,9 +181,10 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { } $this->getVariantPlugin()->updateBlock($this->block->getConfiguration()['uuid'], ['region' => $form_state->getValue('region')]); - $this->pageVariant->save(); - $form_state->setRedirectUrl($this->pageVariant->toUrl('edit-form')); + $cached_values = $this->getTempstore()->get($form_state->get('variant_id')); + $cached_values['plugin'] = $this->getVariantPlugin(); + $this->getTempstore()->set($form_state->get('variant_id'), $cached_values); } /** @@ -131,7 +193,7 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase { * @return \Drupal\ctools\Plugin\BlockVariantInterface */ protected function getVariantPlugin() { - return $this->pageVariant->getVariantPlugin(); + return $this->variantPlugin; } } diff --git a/page_manager_ui/src/Form/VariantPluginContentForm.php b/page_manager_ui/src/Form/VariantPluginContentForm.php new file mode 100644 index 0000000..3d67314 --- /dev/null +++ b/page_manager_ui/src/Form/VariantPluginContentForm.php @@ -0,0 +1,257 @@ +tempstore = $tempstore; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('user.shared_tempstore') + ); + } + + /** + * Get the tempstore ID. + * + * @return string + */ + protected function getTempstoreId() { + return 'page_manager.block_display'; + } + + /** + * Get the tempstore. + * + * @return \Drupal\user\SharedTempStore + */ + protected function getTempstore() { + return $this->tempstore->get($this->getTempstoreId()); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'page_manager_block_page_content'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */ + $variant_plugin = $cached_values['plugin']; + + // Store the block display plugin so we can get it in our dialogs. + if (!empty($this->getTempstore()->get($variant_plugin->id())['plugin'])) { + $variant_plugin->setConfiguration($this->getTempstore()->get($variant_plugin->id())['plugin']->getConfiguration()); + $form_state->setTemporaryValue('wizard', $cached_values); + } + $context_definitions = []; + foreach ($variant_plugin->getContexts() as $context_name => $context) { + $context_definitions[$context_name] = $context->getContextDefinition(); + } + $this->getTempstore()->set($variant_plugin->id(), [ + 'plugin' => $variant_plugin, + 'access' => $cached_values['access'], + 'contexts' => $context_definitions, + ]); + + // Set up the attributes used by a modal to prevent duplication later. + $attributes = $this->getAjaxAttributes(); + $add_button_attributes = $this->getAjaxButtonAttributes(); + + if ($block_assignments = $variant_plugin->getRegionAssignments()) { + // Build a table of all blocks used by this variant. + $form['add'] = [ + '#type' => 'link', + '#title' => $this->t('Add new block'), + '#url' => Url::fromRoute('page_manager.block_display_select_block', [ + 'block_display' => $variant_plugin->id(), + 'destination' => $this->getRequest()->getRequestUri(), + ]), + '#attributes' => $add_button_attributes, + '#attached' => [ + 'library' => [ + 'core/drupal.ajax', + ], + ], + ]; + $form['blocks'] = [ + '#type' => 'table', + '#header' => [ + $this->t('Label'), + $this->t('Plugin ID'), + $this->t('Region'), + $this->t('Weight'), + $this->t('Operations'), + ], + '#empty' => $this->t('There are no regions for blocks.'), + ]; + // Loop through the blocks per region. + foreach ($block_assignments as $region => $blocks) { + // Add a section for each region and allow blocks to be dragged between + // them. + $form['blocks']['#tabledrag'][] = [ + 'action' => 'match', + 'relationship' => 'sibling', + 'group' => 'block-region-select', + 'subgroup' => 'block-region-' . $region, + 'hidden' => FALSE, + ]; + $form['blocks']['#tabledrag'][] = [ + 'action' => 'order', + 'relationship' => 'sibling', + 'group' => 'block-weight', + 'subgroup' => 'block-weight-' . $region, + ]; + $form['blocks'][$region] = [ + '#attributes' => [ + 'class' => ['region-title', 'region-title-' . $region], + 'no_striping' => TRUE, + ], + ]; + $form['blocks'][$region]['title'] = [ + '#markup' => $variant_plugin->getRegionName($region), + '#wrapper_attributes' => [ + 'colspan' => 5, + ], + ]; + $form['blocks'][$region . '-message'] = [ + '#attributes' => [ + 'class' => [ + 'region-message', + 'region-' . $region . '-message', + empty($blocks) ? 'region-empty' : 'region-populated', + ], + ], + ]; + $form['blocks'][$region . '-message']['message'] = [ + '#markup' => '' . $this->t('No blocks in this region') . '', + '#wrapper_attributes' => [ + 'colspan' => 5, + ], + ]; + + /** @var \Drupal\Core\Block\BlockPluginInterface[] $blocks */ + foreach ($blocks as $block_id => $block) { + $row = [ + '#attributes' => [ + 'class' => ['draggable'], + ], + ]; + $row['label']['#markup'] = $block->label(); + $row['id']['#markup'] = $block->getPluginId(); + // Allow the region to be changed for each block. + $row['region'] = [ + '#title' => $this->t('Region'), + '#title_display' => 'invisible', + '#type' => 'select', + '#options' => $variant_plugin->getRegionNames(), + '#default_value' => $variant_plugin->getRegionAssignment($block_id), + '#attributes' => [ + 'class' => ['block-region-select', 'block-region-' . $region], + ], + ]; + // Allow the weight to be changed for each block. + $configuration = $block->getConfiguration(); + $row['weight'] = [ + '#type' => 'weight', + '#default_value' => isset($configuration['weight']) ? $configuration['weight'] : 0, + '#title' => $this->t('Weight for @block block', ['@block' => $block->label()]), + '#title_display' => 'invisible', + '#attributes' => [ + 'class' => ['block-weight', 'block-weight-' . $region], + ], + ]; + // Add the operation links. + $operations = []; + $operations['edit'] = [ + 'title' => $this->t('Edit'), + 'url' => Url::fromRoute('page_manager.block_display_edit_block', [ + 'block_display' => $variant_plugin->id(), + 'block_id' => $block_id, + 'destination' => $this->getRequest()->getRequestUri(), + ]), + 'attributes' => $attributes, + ]; + $operations['delete'] = [ + 'title' => $this->t('Delete'), + 'url' => Url::fromRoute('page_manager.block_display_delete_block', [ + 'block_display' => $variant_plugin->id(), + 'block_id' => $block_id, + 'destination' => $this->getRequest()->getRequestUri(), + ]), + 'attributes' => $attributes, + ]; + + $row['operations'] = [ + '#type' => 'operations', + '#links' => $operations, + ]; + $form['blocks'][$block_id] = $row; + } + } + } + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */ + $variant_plugin = $cached_values['plugin']; + + // If the blocks were rearranged, update their values. + if (!$form_state->isValueEmpty('blocks')) { + foreach ($form_state->getValue('blocks') as $block_id => $block_values) { + $variant_plugin->updateBlock($block_id, $block_values); + } + } + + // Remove from the tempstore so we refresh from the database the next time + // we come here. + $this->getTempstore()->delete($variant_plugin->id()); + } + +} diff --git a/page_manager_ui/src/Form/VariantPluginDeleteBlockForm.php b/page_manager_ui/src/Form/VariantPluginDeleteBlockForm.php index 02a013e..b451074 100644 --- a/page_manager_ui/src/Form/VariantPluginDeleteBlockForm.php +++ b/page_manager_ui/src/Form/VariantPluginDeleteBlockForm.php @@ -17,11 +17,9 @@ use Drupal\page_manager\PageVariantInterface; class VariantPluginDeleteBlockForm extends ConfirmFormBase { /** - * The page variant. - * - * @var \Drupal\page_manager\PageVariantInterface + * @var \Drupal\ctools\Plugin\BlockVariantInterface */ - protected $pageVariant; + protected $plugin; /** * The plugin being configured. @@ -31,6 +29,24 @@ class VariantPluginDeleteBlockForm extends ConfirmFormBase { protected $block; /** + * Get the tempstore id. + * + * @return string + */ + protected function getTempstoreId() { + return 'page_manager.block_display'; + } + + /** + * Get the tempstore. + * + * @return \Drupal\user\SharedTempStore + */ + protected function getTempstore() { + return \Drupal::service('user.shared_tempstore')->get($this->getTempstoreId()); + } + + /** * {@inheritdoc} */ public function getFormId() { @@ -48,7 +64,7 @@ class VariantPluginDeleteBlockForm extends ConfirmFormBase { * {@inheritdoc} */ public function getCancelUrl() { - return $this->pageVariant->toUrl('edit-form'); + return \Drupal::request()->attributes->get('destination'); } /** @@ -61,9 +77,13 @@ class VariantPluginDeleteBlockForm extends ConfirmFormBase { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $block_id = NULL) { - $this->pageVariant = $page_variant; - $this->block = $this->getVariantPlugin()->getBlock($block_id); + public function buildForm(array $form, FormStateInterface $form_state, $block_display = NULL, $block_id = NULL) { + $this->plugin = $this->getTempstore()->get($block_display)['plugin']; + $this->block = $this->plugin->getBlock($block_id); + $form['block_display'] = [ + '#type' => 'value', + '#value' => $block_display + ]; return parent::buildForm($form, $form_state); } @@ -71,20 +91,11 @@ class VariantPluginDeleteBlockForm extends ConfirmFormBase { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - $this->getVariantPlugin()->removeBlock($this->block->getConfiguration()['uuid']); - $this->pageVariant->save(); + $this->plugin->removeBlock($this->block->getConfiguration()['uuid']); + $cached_values = $this->getTempstore()->get($form_state->getValue('block_display')); + $cached_values['plugin'] = $this->plugin; + $this->getTempstore()->set($form_state->getValue('block_display'), $cached_values); drupal_set_message($this->t('The block %label has been removed.', ['%label' => $this->block->label()])); - - $form_state->setRedirectUrl($this->getCancelUrl()); - } - - /** - * Gets the variant plugin for this page variant entity. - * - * @return \Drupal\ctools\Plugin\BlockVariantInterface - */ - protected function getVariantPlugin() { - return $this->pageVariant->getVariantPlugin(); } } diff --git a/page_manager_ui/src/Tests/AddVariantSelectionTest.php b/page_manager_ui/src/Tests/AddVariantSelectionTest.php new file mode 100644 index 0000000..cda3cfa --- /dev/null +++ b/page_manager_ui/src/Tests/AddVariantSelectionTest.php @@ -0,0 +1,156 @@ +drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + $this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']); + $this->drupalLogin($this->drupalCreateUser(['administer pages', 'create article content'])); + + $this->drupalPlaceBlock('page_title_block'); + } + + /** + * Tests configuration of the selection criteria wizard step. + */ + public function testSelectionCriteria() { + // Create a node, and check its page. + $node = $this->drupalCreateNode(['type' => 'article']); + $node2 = $this->drupalCreateNode(['type' => 'article']); + $this->drupalGet('node/' . $node->id()); + $this->assertResponse(200); + $this->assertText($node->label()); + $this->assertTitle($node->label() . ' | Drupal'); + + // Create a new page entity. + $edit_page = [ + 'label' => 'Selection criteria', + 'id' => 'selection_criteria', + 'path' => 'selection-criteria', + 'variant_plugin_id' => 'block_display' + ]; + $this->drupalPostForm('admin/structure/page_manager/add', $edit_page, 'Next'); + $this->drupalPostForm(NULL, [], 'Next'); + $this->drupalPostForm(NULL, [], 'Finish'); + $this->clickLink('Add variant'); + $edit = [ + 'label' => 'Variant two', + 'variant_plugin_id' => 'block_display', + 'wizard_options[contexts]' => TRUE, + 'wizard_options[selection]' => TRUE, + ]; + $this->drupalPostForm(NULL, $edit, 'Next'); + // Add a static context for each node to the page variant. + $contexts = array( + array( + 'title' => 'Static Node', + 'machine_name' => 'static_node', + 'description' => 'Static node 1', + 'node' => $node, + ), + array( + 'title' => 'Static Node 2', + 'machine_name' => 'static_node_2', + 'description' => 'Static node 2', + 'node' => $node2, + ), + ); + foreach ($contexts as $context) { + $edit = [ + 'context' => 'entity:node', + ]; + $this->drupalPostForm(NULL, $edit, 'Add new context'); + $edit = [ + 'label' => $context['title'], + 'machine_name' => $context['machine_name'], + 'description' => $context['description'], + 'context_value' => $context['node']->getTitle() . ' (' . $context['node']->id() . ')', + ]; + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText($context['title']); + } + $this->drupalPostForm(NULL, [], 'Next'); + + // Configure selection criteria. + $edit = [ + 'conditions' => 'entity_bundle:node', + ]; + $this->drupalPostForm(NULL, $edit, 'Add Condition'); + + $edit = [ + 'bundles[article]' => TRUE, + 'bundles[page]' => TRUE, + 'context_mapping[node]' => 'static_node_2', + ]; + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText('Content type is article or page'); + $this->clickLink('Edit'); + $edit = [ + 'bundles[article]' => TRUE, + 'context_mapping[node]' => 'static_node_2', + ]; + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText('Content type is article'); + $this->clickLink('Delete'); + $this->drupalPostForm(NULL, [], 'Delete'); + $this->assertNoText('Content type is article'); + $this->drupalPostForm(NULL, [], 'Next'); + + // Configure the new variant. + $variant_edit = [ + 'variant_settings[page_title]' => 'Variant two criteria test', + ]; + $this->drupalPostForm(NULL, $variant_edit, 'Next'); + + // Add a block that renders the node from the first static context. + $this->clickLink('Add new block'); + $this->clickLink('Entity view (Content)'); + $edit = [ + 'settings[label]' => 'Static node view', + 'settings[label_display]' => 1, + 'settings[view_mode]' => 'default', + 'region' => 'top', + ]; + $this->drupalPostForm(NULL, $edit, 'Add block'); + $this->assertText($edit['settings[label]']); + + // Add a block that renders the node from the second static context. + $this->clickLink('Add new block'); + $this->clickLink('Entity view (Content)'); + $edit = [ + 'settings[label]' => 'Static node 2 view', + 'settings[label_display]' => 1, + 'settings[view_mode]' => 'default', + 'region' => 'bottom', + 'context_mapping[entity]' => $contexts[1]['machine_name'], + ]; + $this->drupalPostForm(NULL, $edit, 'Add block'); + $this->assertText($edit['settings[label]']); + $this->drupalPostForm(NULL, [], 'Finish'); + } + +} diff --git a/page_manager_ui/src/Tests/PageManagerAdminTest.php b/page_manager_ui/src/Tests/PageManagerAdminTest.php index 9811fd6..715df72 100644 --- a/page_manager_ui/src/Tests/PageManagerAdminTest.php +++ b/page_manager_ui/src/Tests/PageManagerAdminTest.php @@ -50,12 +50,14 @@ class PageManagerAdminTest extends WebTestBase { */ public function testAdmin() { $this->doTestAddPage(); + $this->doTestAccessConditions(); + $this->doTestSelectionCriteria(); + $this->doTestSelectionCriteriaWithAjax(); $this->doTestDisablePage(); $this->doTestAddVariant(); $this->doTestAddBlock(); $this->doTestSecondPage(); $this->doTestEditBlock(); - $this->doTestAlterBlock(); $this->doTestEditVariant(); $this->doTestReorderVariants(); $this->doTestAddPageWithDuplicatePath(); @@ -65,6 +67,7 @@ class PageManagerAdminTest extends WebTestBase { $this->doTestAddBlockWithAjax(); $this->doTestEditBlock(); $this->doTestExistingPathWithoutParameters(); + $this->doTestUpdateSubmit(); $this->doTestDeletePage(); } @@ -81,47 +84,167 @@ class PageManagerAdminTest extends WebTestBase { $edit = [ 'id' => 'foo', 'path' => 'admin/foo', + 'variant_plugin_id' => 'http_status_code', + 'use_admin_theme' => TRUE, + 'description' => 'This is our first test page.', + // Go through all available steps (we skip them all in doTestSecondPage()) + 'wizard_options[access]' => TRUE, + 'wizard_options[selection]' => TRUE, ]; - $this->drupalPostForm(NULL, $edit, 'Save'); - $this->assertText('Label field is required.'); + $this->drupalPostForm(NULL, $edit, 'Next'); + $this->assertText('Administrative title field is required.'); // Add a new page with a label. $edit += ['label' => 'Foo']; - $this->drupalPostForm(NULL, $edit, 'Save'); - $this->assertRaw(new FormattableMarkup('The %label page has been added.', ['%label' => 'Foo'])); - - // Assert that no variant was added by default. - $this->drupalGet('admin/structure/page_manager/manage/foo'); - $this->assertText('There are no variants.'); - - // Test that it is available immediately. - $this->drupalGet('admin/foo'); - $this->assertResponse(404); - $this->drupalGet('admin/structure/page_manager/manage/foo'); - $this->clickLink('Add new variant'); - $this->clickLink('HTTP status code'); + $this->drupalPostForm(NULL, $edit, 'Next'); + + // Test the 'Page access' step. + $this->assertTitle('Page access | Drupal'); + $access_path = 'admin/structure/page_manager/add/foo/access'; + $this->assertUrl($access_path . '?js=nojs'); + $this->doTestAccessConditions($access_path, FALSE); + $this->drupalPostForm(NULL, [], 'Next'); + + // Test the 'Selection criteria' step. + $this->assertTitle('Selection criteria | Drupal'); + $selection_path = 'admin/structure/page_manager/add/foo/selection'; + $this->assertUrl($selection_path . '?js=nojs'); + $this->doTestSelectionCriteria($selection_path, FALSE); + $this->drupalPostForm(NULL, [], 'Next'); + + // Configure the variant. $edit = [ - 'id' => 'http_status_code', - 'label' => 'Status Code', + 'page_variant_label' => 'Status Code', 'variant_settings[status_code]' => 200, ]; - $this->drupalPostForm(NULL, $edit, 'Save'); - - // There is a variant now, so the empty text is no longer visible. - $this->assertNoText('There are no variants.'); + $this->drupalPostForm(NULL, $edit, 'Finish'); + $this->assertRaw(new FormattableMarkup('Saved the %label Page.', ['%label' => 'Foo'])); + // We've gone from the add wizard to the edit wizard. + $this->drupalGet('admin/structure/page_manager/manage/foo/general'); $this->drupalGet('admin/foo'); $this->assertResponse(200); $this->assertTitle('Foo | Drupal'); - $this->drupalGet('admin/structure/page_manager/manage/foo'); - $this->clickLink('Edit'); - $this->drupalPostForm(NULL, ['variant_settings[status_code]' => 403], 'Save'); + + // Change the status code to 403. + $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-http_status_code-0__general'); + $edit = [ + 'variant_settings[status_code]' => 403, + ]; + $this->drupalPostForm(NULL, $edit, 'Update'); // Set the weight of the 'Status Code' variant to 10. + $this->drupalGet('admin/structure/page_manager/manage/foo/reorder_variants'); + $edit = [ + 'variants[foo-http_status_code-0][weight]' => 10, + ]; + $this->drupalPostForm(NULL, $edit, 'Update'); + $this->drupalPostForm(NULL, [], 'Update and save'); + } + + /** + * Tests access conditions step on both add and edit wizard. + * + * @param string $path + * The path this step is supposed to be at. + * @param bool|TRUE $redirect + * Whether or not to redirect to the path. + */ + protected function doTestAccessConditions($path = 'admin/structure/page_manager/manage/foo/access', $redirect = TRUE) { + if ($this->getUrl() !== $path && $redirect) { + $this->drupalGet($path); + } + + $this->assertRaw('No required conditions have been configured.'); + + // Configure a new condition. + $edit = [ + 'conditions' => 'user_role', + ]; + $this->drupalPostForm(NULL, $edit, 'Add Condition'); + $this->assertTitle('Add access condition | Drupal'); + $edit = [ + 'roles[authenticated]' => TRUE, + ]; + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertRaw('The user is a member of Authenticated user'); + // Make sure we're still on the same wizard. + $this->assertUrl($path); + + // Edit the condition. + $this->clickLink('Edit'); + $this->assertTitle('Edit access condition | Drupal'); + $edit = [ + 'roles[anonymous]' => TRUE, + ]; + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertRaw('The user is a member of Anonymous user, Authenticated user'); + $this->assertUrl($path); + + // Delete the condition. + $this->clickLink('Delete'); + $this->assertTitle('Are you sure you want to delete the user_role condition? | Drupal'); + $this->drupalPostForm(NULL, [], 'Delete'); + $this->assertRaw('No required conditions have been configured.'); + $this->assertUrl($path); + } + + /** + * Tests selection criteria step on both add and edit wizard. + * + * @param string $path + * The path this step is supposed to be at. + * @param bool|TRUE $redirect + * Whether or not to redirect to the path. + */ + protected function doTestSelectionCriteria($path = 'admin/structure/page_manager/manage/foo/page_variant__foo-http_status_code-0__selection', $redirect = TRUE) { + if ($this->getUrl() !== $path && $redirect) { + $this->drupalGet($path); + } + $this->assertRaw('No required conditions have been configured.'); + + // Configure a new condition. + $edit = [ + 'conditions' => 'user_role', + ]; + $this->drupalPostForm(NULL, $edit, 'Add Condition'); + $this->assertTitle('Add new selection condition | Drupal'); $edit = [ - 'variants[http_status_code][weight]' => 10, + 'roles[authenticated]' => TRUE, ]; $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertRaw('The user is a member of Authenticated user'); + // Make sure we're still on the add wizard (not the edit wizard). + $this->assertUrl($path); + + // Edit the condition. + $this->clickLink('Edit'); + $this->assertTitle('Edit selection condition | Drupal'); + $edit = [ + 'roles[anonymous]' => TRUE, + ]; + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertRaw('The user is a member of Anonymous user, Authenticated user'); + $this->assertUrl($path); + + // Delete the condition. + $this->clickLink('Delete'); + $this->assertTitle('Are you sure you want to delete the user_role condition? | Drupal'); + $this->drupalPostForm(NULL, [], 'Delete'); + $this->assertRaw('No required conditions have been configured.'); + $this->assertUrl($path); + } + + /** + * Tests the AJAX form for Selection Criteria. + */ + protected function doTestSelectionCriteriaWithAjax() { + $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-http_status_code-0__selection'); + $edit = [ + 'conditions' => 'user_role', + ]; + $response = $this->drupalPostAjaxForm(NULL, $edit, ['add' => 'Add Condition']); + $this->assertEqual($response[2]['dialogOptions']['title'], 'Configure Required Context'); } /** @@ -148,20 +271,27 @@ class PageManagerAdminTest extends WebTestBase { * Tests adding a variant. */ protected function doTestAddVariant() { - $this->drupalGet('admin/structure/page_manager/manage/foo'); + $this->drupalGet('admin/structure/page_manager/manage/foo/general'); // Add a new variant. - $this->clickLink('Add new variant'); - $this->assertNoText('Page with blocks'); - $this->assertNoText('Simple page'); - $this->clickLink('Block page'); + $this->clickLink('Add variant'); $edit = [ + 'variant_plugin_id' => 'block_display', 'label' => 'First', - 'id' => 'block_page', + ]; + $this->drupalPostForm(NULL, $edit, 'Next'); + + // Set the page title. + $edit = [ 'variant_settings[page_title]' => 'Example title', ]; - $this->drupalPostForm(NULL, $edit, 'Save'); - $this->assertRaw(new FormattableMarkup('Saved the %label variant.', ['%label' => 'First'])); + $this->drupalPostForm(NULL, $edit, 'Next'); + + // Finish variant wizard without adding blocks. + $this->drupalPostForm(NULL, [], 'Finish'); + + // Save page to apply variant changes. + $this->drupalPostForm(NULL, [], 'Update and save'); // Test that the variant is still used but empty. $this->drupalGet('admin/foo'); @@ -175,8 +305,7 @@ class PageManagerAdminTest extends WebTestBase { * Tests adding a block to a variant. */ protected function doTestAddBlock() { - $this->drupalGet('admin/structure/page_manager/manage/foo'); - $this->clickLink('Edit'); + $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__content'); // Add a block to the variant. $this->clickLink('Add new block'); @@ -188,6 +317,7 @@ class PageManagerAdminTest extends WebTestBase { 'region' => 'top', ]; $this->drupalPostForm(NULL, $edit, 'Add block'); + $this->drupalPostForm(NULL, [], 'Update and save'); // Test that the block is displayed. $this->drupalGet('admin/foo'); @@ -216,20 +346,20 @@ class PageManagerAdminTest extends WebTestBase { 'id' => 'second', 'label' => 'Second', 'path' => 'second', + 'variant_plugin_id' => 'block_display', ]; - $this->drupalPostForm(NULL, $edit, 'Save'); - $this->assertRaw(new FormattableMarkup('The %label page has been added.', ['%label' => 'Second'])); + $this->drupalPostForm(NULL, $edit, 'Next'); - // Add a variant. - $this->clickLink('Add new variant'); - $this->clickLink('Block page'); + // Configure the variant. $edit = [ - 'label' => 'Second variant', - 'id' => 'second_block_page', + 'page_variant_label' => 'Second variant', 'variant_settings[page_title]' => 'Second title', ]; - $this->drupalPostForm(NULL, $edit, 'Save'); - $this->assertRaw(new FormattableMarkup('Saved the %label variant.', ['%label' => 'Second variant'])); + $this->drupalPostForm(NULL, $edit, 'Next'); + + // We're now on the content step, but we don't need to add any blocks. + $this->drupalPostForm(NULL, [], 'Finish'); + $this->assertRaw(new FormattableMarkup('Saved the %label Page.', ['%label' => 'Second'])); // Visit both pages, make sure that they do not interfere with each other. $this->drupalGet('admin/foo'); @@ -242,78 +372,43 @@ class PageManagerAdminTest extends WebTestBase { * Tests editing a block. */ protected function doTestEditBlock() { - $this->drupalGet('admin/structure/page_manager/manage/foo'); - $this->clickLink('Edit'); - $this->clickLink('Edit'); + $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__general'); $edit = [ - 'settings[label]' => 'Updated block label', + 'variant_settings[page_title]' => 'Updated block label', + 'page_variant_label' => 'Updated block label', ]; - $this->drupalPostForm(NULL, $edit, 'Update block'); + $this->drupalPostForm(NULL, $edit, 'Update and save'); // Test that the block is displayed. $this->drupalGet('admin/foo'); $this->assertResponse(200); // Check the block label. - $this->assertRaw($edit['settings[label]']); - } - - /** - * Tests altering a block. - */ - protected function doTestAlterBlock() { - // Alter the block label. - $this->drupalGet('admin/structure/page_manager/manage/foo'); - $this->clickLink('Edit'); - $this->clickLink('Edit'); - $edit = [ - 'settings[label]' => 'Label to be altered', - ]; - $this->drupalPostForm(NULL, $edit, 'Update block'); - $this->drupalGet('admin/foo'); - $this->assertResponse(200); - // Check if the block label is altered correctly. - $this->assertRaw('Altered label'); - - // Re-set the previous block label. - $this->drupalGet('admin/structure/page_manager/manage/foo'); - $this->clickLink('Edit'); - $this->clickLink('Edit'); - $edit = [ - 'settings[label]' => 'Updated block label', - ]; - $this->drupalPostForm(NULL, $edit, 'Update block'); - $this->drupalGet('admin/foo'); - $this->assertResponse(200); - // Check the block label. - $this->assertRaw($edit['settings[label]']); + $this->assertRaw($edit['variant_settings[page_title]']); } /** * Tests editing a variant. */ protected function doTestEditVariant() { - if (!$block = $this->findBlockByLabel('block_page', 'Updated block label')) { + if (!$block = $this->findBlockByLabel('foo-block_display-0', 'User account menu')) { $this->fail('Block not found'); return; } $block_config = $block->getConfiguration(); - $this->drupalGet('admin/structure/page_manager/manage/foo'); - $this->clickLink('Edit'); - $this->assertTitle('Edit First variant | Drupal'); + $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__content'); - $this->assertOptionSelected('edit-variant-plugin-blocks-' . $block_config['uuid'] . '-region', 'top'); - $this->assertOptionSelected('edit-variant-plugin-blocks-' . $block_config['uuid'] . '-weight', 0); + $this->assertOptionSelected('edit-blocks-' . $block_config['uuid'] . '-region', 'top'); + $this->assertOptionSelected('edit-blocks-' . $block_config['uuid'] . '-weight', 0); - $form_name = 'variant_plugin[blocks][' . $block_config['uuid'] . ']'; + $form_name = 'blocks[' . $block_config['uuid'] . ']'; $edit = [ $form_name . '[region]' => 'bottom', $form_name . '[weight]' => -10, ]; - $this->drupalPostForm(NULL, $edit, 'Save'); - $this->assertRaw(new FormattableMarkup('Saved the %label variant.', ['%label' => 'First'])); - $this->clickLink('Edit'); - $this->assertOptionSelected('edit-variant-plugin-blocks-' . $block_config['uuid'] . '-region', 'bottom'); - $this->assertOptionSelected('edit-variant-plugin-blocks-' . $block_config['uuid'] . '-weight', -10); + $this->drupalPostForm(NULL, $edit, 'Update'); + $this->assertOptionSelected('edit-blocks-' . $block_config['uuid'] . '-region', 'bottom'); + $this->assertOptionSelected('edit-blocks-' . $block_config['uuid'] . '-weight', -10); + $this->drupalPostForm(NULL, [], 'Update and save'); } /** @@ -330,10 +425,14 @@ class PageManagerAdminTest extends WebTestBase { } $this->assertEqual($expected, $links); + $this->drupalGet('admin/structure/page_manager/manage/foo/general'); + $this->clickLink('Reorder variants'); + $edit = [ - 'variants[http_status_code][weight]' => -10, + 'variants[foo-http_status_code-0][weight]' => -10, ]; - $this->drupalPostForm('admin/structure/page_manager/manage/foo', $edit, 'Save'); + $this->drupalPostForm(NULL, $edit, 'Update'); + $this->drupalPostForm(NULL, [], 'Update and save'); $this->drupalGet('admin/foo'); $this->assertResponse(403); } @@ -348,7 +447,7 @@ class PageManagerAdminTest extends WebTestBase { 'id' => 'bar', 'path' => 'admin/foo', ]; - $this->drupalPostForm('admin/structure/page_manager/add', $edit, 'Save'); + $this->drupalPostForm('admin/structure/page_manager/add', $edit, 'Next'); $this->assertText('The page path must be unique.'); $this->drupalGet('admin/structure/page_manager'); $this->assertNoText('Bar'); @@ -362,10 +461,11 @@ class PageManagerAdminTest extends WebTestBase { $this->drupalGet('admin/foo'); $this->assertTheme('classy'); + $this->drupalGet('admin/structure/page_manager/manage/foo/general'); $edit = [ 'use_admin_theme' => FALSE, ]; - $this->drupalPostForm('admin/structure/page_manager/manage/foo', $edit, 'Save'); + $this->drupalPostForm(NULL, $edit, 'Update and save'); $this->drupalGet('admin/foo'); $this->assertTheme('bartik'); @@ -377,11 +477,12 @@ class PageManagerAdminTest extends WebTestBase { * Tests removing a variant. */ protected function doTestRemoveVariant() { - $this->drupalGet('admin/structure/page_manager/manage/foo'); - $this->clickLink('Delete'); - $this->assertRaw(new FormattableMarkup('Are you sure you want to delete %label?', ['%label' => 'Status Code'])); + $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-http_status_code-0__general'); + $this->clickLink('Delete this variant'); + $this->assertRaw('Are you sure you want to delete this variant?'); $this->drupalPostForm(NULL, [], 'Delete'); $this->assertRaw(new FormattableMarkup('The variant %label has been removed.', ['%label' => 'Status Code'])); + $this->drupalPostForm(NULL, [], 'Update and save'); } /** @@ -399,12 +500,12 @@ class PageManagerAdminTest extends WebTestBase { } $this->assertEqual($expected, $links); - $this->drupalGet('admin/structure/page_manager/manage/foo'); - $this->clickLink('Edit'); + $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__content'); $this->clickLink('Delete'); - $this->assertRaw(new FormattableMarkup('Are you sure you want to delete the block %label?', ['%label' => 'Updated block label'])); + $this->assertRaw(new FormattableMarkup('Are you sure you want to delete the block %label?', ['%label' => 'User account menu'])); $this->drupalPostForm(NULL, [], 'Delete'); - $this->assertRaw(new FormattableMarkup('The block %label has been removed.', ['%label' => 'Updated block label'])); + $this->assertRaw(new FormattableMarkup('The block %label has been removed.', ['%label' => 'User account menu'])); + $this->drupalPostForm(NULL, [], 'Update and save'); // Assert that the block is now gone. $this->drupalGet('admin/foo'); @@ -417,8 +518,7 @@ class PageManagerAdminTest extends WebTestBase { * Tests adding a block with #ajax to a variant. */ protected function doTestAddBlockWithAjax() { - $this->drupalGet('admin/structure/page_manager/manage/foo'); - $this->clickLink('Edit'); + $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__content'); // Add a block to the variant. $this->clickLink('Add new block'); $this->clickLink('Page Manager Test Block'); @@ -426,6 +526,7 @@ class PageManagerAdminTest extends WebTestBase { 'region' => 'top', ]; $this->drupalPostForm(NULL, $edit, 'Add block'); + $this->drupalPostForm(NULL, [], 'Update and save'); // Test that the block is displayed. $this->drupalGet('admin/foo'); @@ -450,20 +551,16 @@ class PageManagerAdminTest extends WebTestBase { 'label' => 'existing', 'id' => 'existing', 'path' => 'admin', + 'variant_plugin_id' => 'http_status_code', ]; - $this->drupalPostForm(NULL, $edit, 'Save'); - - // Regular result is displayed. - $this->assertText('The existing page has been added'); + $this->drupalPostForm(NULL, $edit, 'Next'); - $this->clickLink('Add new variant'); - $this->clickLink('HTTP status code'); + // Configure the variant. $edit = [ - 'id' => 'http_status_code', - 'label' => 'Status Code', + 'page_variant_label' => 'Status Code', 'variant_settings[status_code]' => 404, ]; - $this->drupalPostForm(NULL, $edit, 'Save'); + $this->drupalPostForm(NULL, $edit, 'Finish'); // Ensure the existing path leads to the new page. $this->drupalGet('admin'); @@ -471,6 +568,52 @@ class PageManagerAdminTest extends WebTestBase { } /** + * Tests the Update button on Variant forms. + */ + protected function doTestUpdateSubmit() { + // Add a block variant. + $this->drupalGet('admin/structure/page_manager/manage/foo/general'); + + // Add a new variant. + $this->clickLink('Add variant'); + $edit = [ + 'variant_plugin_id' => 'block_display', + 'label' => 'First', + ]; + $this->drupalPostForm(NULL, $edit, 'Next'); + + // Set the page title. + $edit = [ + 'variant_settings[page_title]' => 'Example title', + ]; + $this->drupalPostForm(NULL, $edit, 'Next'); + + // Finish variant wizard without adding blocks. + $this->drupalPostForm(NULL, [], 'Finish'); + + // Update the description and click on Update. + $edit = [ + 'page_variant_label' => 'First updated', + 'variant_settings[page_title]' => 'Example title updated', + ]; + $this->drupalPostForm('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__general', $edit, 'Update'); + $this->assertFieldByName('page_variant_label', 'First updated'); + $this->assertFieldByName('variant_settings[page_title]', 'Example title updated'); + + // Click on Update at Contexts. Nothing should happen. + $this->drupalPostForm('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__contexts', [], 'Update'); + $this->assertUrl('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__contexts'); + + // Click on Update at Selection criteria. Nothing should happen. + $this->drupalPostForm('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__selection', [], 'Update'); + $this->assertUrl('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__selection'); + + // Click on Update at Content. Nothing should happen. + $this->drupalPostForm('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__content', [], 'Update'); + $this->assertUrl('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__content'); + } + + /** * Tests deleting a page. */ protected function doTestDeletePage() { @@ -496,54 +639,34 @@ class PageManagerAdminTest extends WebTestBase { */ public function testExistingRoutes() { // Test that the page without placeholder is accessible. + $this->drupalGet('admin/structure/page_manager/add'); $edit = [ 'label' => 'Placeholder test 2', 'id' => 'placeholder2', 'path' => '/page-manager-test', + 'variant_plugin_id' => 'http_status_code', ]; - $this->drupalPostForm('admin/structure/page_manager/add', $edit, 'Save'); - $this->drupalGet('page-manager-test'); - // Without a single variant, it will fall through to the original. - $this->assertResponse(200); + $this->drupalPostForm(NULL, $edit, 'Next'); - $this->drupalGet('admin/structure/page_manager/manage/placeholder2'); - $this->clickLink('Add new variant'); - $this->clickLink('HTTP status code'); $edit = [ - 'id' => 'http_status_code', - 'label' => 'Status Code', - 'variant_settings[status_code]' => 404, + 'variant_settings[status_code]' => 418, ]; - $this->drupalPostForm(NULL, $edit, 'Save'); + $this->drupalPostForm(NULL, $edit, 'Finish'); $this->drupalGet('page-manager-test'); - $this->assertResponse(404); + $this->assertResponse(418); // Test that the page test is accessible. $page_string = 'test-page'; $this->drupalGet('page-manager-test/' . $page_string); $this->assertResponse(200); - } - /** - * Tests the parameters UI. - */ - public function testParameters() { - $this->drupalGet('admin/structure/page_manager'); - $this->clickLink('Add page'); - $edit = [ - 'id' => 'foo', - 'label' => 'Foo', - 'path' => '/foo/{user}', - ]; - $this->drupalPostForm(NULL, $edit, 'Save'); - $this->assertText('No context assigned'); - - $this->clickLink('Edit'); - $this->drupalPostForm(NULL, ['type' => 'entity:user'], 'Update parameter'); - $this->assertNoText('No context assigned'); - $this->assertText('entity:user'); - $parameters = Page::load('foo')->getParameters(); - $this->assertIdentical(['user' => ['machine_name' => 'user', 'type' => 'entity:user', 'label' => 'User']], $parameters); + // Without a single variant, it will fall through to the original. + $this->drupalGet('admin/structure/page_manager/manage/placeholder2/page_variant__placeholder2-http_status_code-0__general'); + $this->clickLink('Delete this variant'); + $this->drupalPostForm(NULL, [], 'Delete'); + $this->drupalPostForm(NULL, [], 'Update and save'); + $this->drupalGet('page-manager-test'); + $this->assertResponse(200); } /** @@ -570,6 +693,7 @@ class PageManagerAdminTest extends WebTestBase { * Either a block plugin, or NULL. */ protected function findBlockByLabel($page_variant_id, $block_label) { + /** @var \Drupal\page_manager\Entity\PageVariant $page_variant */ if ($page_variant = PageVariant::load($page_variant_id)) { /** @var \Drupal\ctools\Plugin\BlockVariantInterface $variant_plugin */ $variant_plugin = $page_variant->getVariantPlugin(); diff --git a/page_manager_ui/src/Tests/PageParametersTest.php b/page_manager_ui/src/Tests/PageParametersTest.php new file mode 100644 index 0000000..cd338f9 --- /dev/null +++ b/page_manager_ui/src/Tests/PageParametersTest.php @@ -0,0 +1,105 @@ +drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + + $this->drupalPlaceBlock('local_tasks_block'); + $this->drupalPlaceBlock('local_actions_block'); + $this->drupalPlaceBlock('system_branding_block'); + $this->drupalPlaceBlock('page_title_block'); + + $this->drupalLogin($this->drupalCreateUser([ + 'administer pages', + 'access administration pages', + 'view the administration theme', + 'create article content', + ])); + } + + /** + * Tests page parameters when adding a page and when editing it. + */ + public function testParameters() { + $node = $this->drupalCreateNode(['type' => 'article']); + + // Create a page. + $this->drupalGet('admin/structure'); + $this->clickLink('Pages'); + $this->clickLink('Add page'); + $edit = [ + 'id' => 'foo', + 'label' => 'Foo', + 'path' => 'admin/foo/{node}', + 'variant_plugin_id' => 'block_display', + 'use_admin_theme' => TRUE, + 'description' => 'Sample test page.', + ]; + $this->drupalPostForm(NULL, $edit, 'Next'); + + // Test the 'Parameters' step. + $this->assertTitle('Page parameters | Drupal'); + $access_path = 'admin/structure/page_manager/add/foo/parameters'; + $this->assertUrl($access_path . '?js=nojs'); + $this->assertNoText('There are no parameters defined for this page.'); + + // Edit the node parameter. + $this->clickLink('Edit'); + $this->assertTitle('Edit parameter | Drupal'); + $edit = [ + 'type' => 'entity:node', + ]; + $this->drupalPostForm(NULL, $edit, 'Update parameter'); + $this->assertText('The Node parameter has been updated.'); + + // Skip the variant General configuration step. + $this->drupalPostForm(NULL, [], 'Next'); + + // Add the Node block to the top region. + $this->drupalPostForm(NULL, [], 'Next'); + $this->clickLink('Add new block'); + $this->clickLink('Entity view (Content)'); + $edit = [ + 'region' => 'top', + ]; + $this->drupalPostForm(NULL, $edit, 'Add block'); + + // Finish the wizard. + $this->drupalPostForm(NULL, [], 'Finish'); + $this->assertRaw(new FormattableMarkup('Saved the %label Page.', ['%label' => 'Foo'])); + + // Check that the node's title is visible at the page. + $this->drupalGet('admin/foo/' . $node->id()); + $this->assertResponse(200); + $this->assertText($node->getTitle()); + } + +} diff --git a/page_manager_ui/src/Tests/StaticContextTest.php b/page_manager_ui/src/Tests/StaticContextTest.php new file mode 100644 index 0000000..ca5d9b2 --- /dev/null +++ b/page_manager_ui/src/Tests/StaticContextTest.php @@ -0,0 +1,240 @@ +drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + $this->drupalLogin($this->drupalCreateUser(['administer pages', 'create article content'])); + + $this->drupalPlaceBlock('page_title_block'); + } + + /** + * Tests that a node bundle condition controls the node view page. + */ + public function testStaticContext() { + // Create a node, and check its page. + $node = $this->drupalCreateNode(['type' => 'article']); + $node2 = $this->drupalCreateNode(['type' => 'article']); + $this->drupalGet('node/' . $node->id()); + $this->assertResponse(200); + $this->assertText($node->label()); + $this->assertTitle($node->label() . ' | Drupal'); + + // Create a new page entity. + $edit_page = [ + 'label' => 'Static node context', + 'id' => 'static_node_context', + 'path' => 'static-context', + 'variant_plugin_id' => 'block_display', + 'wizard_options[contexts]' => TRUE, + ]; + $this->drupalPostForm('admin/structure/page_manager/add', $edit_page, 'Next'); + + // Add a static context for each node to the page variant. + $contexts = array( + array( + 'title' => 'Static Node', + 'machine_name' => 'static_node', + 'description' => 'Static node 1', + 'node' => $node, + ), + array( + 'title' => 'Static Node 2', + 'machine_name' => 'static_node_2', + 'description' => 'Static node 2', + 'node' => $node2, + ), + ); + foreach ($contexts as $context) { + $edit = [ + 'context' => 'entity:node', + ]; + $this->drupalPostForm(NULL, $edit, 'Add new context'); + $edit = [ + 'label' => $context['title'], + 'machine_name' => $context['machine_name'], + 'description' => $context['description'], + 'context_value' => $context['node']->getTitle() . ' (' . $context['node']->id() . ')', + ]; + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText($context['title']); + } + $this->drupalPostForm(NULL, [], 'Next'); + + // Add a new variant. + $variant_edit = [ + 'variant_settings[page_title]' => 'Static context test page', + ]; + $this->drupalPostForm(NULL, $variant_edit, 'Next'); + + // Add a block that renders the node from the first static context. + $this->clickLink('Add new block'); + $this->clickLink('Entity view (Content)'); + $edit = [ + 'settings[label]' => 'Static node view', + 'settings[label_display]' => 1, + 'settings[view_mode]' => 'default', + 'region' => 'top', + ]; + $this->drupalPostForm(NULL, $edit, 'Add block'); + $this->assertText($edit['settings[label]']); + + // Add a block that renders the node from the second static context. + $this->clickLink('Add new block'); + $this->clickLink('Entity view (Content)'); + $edit = [ + 'settings[label]' => 'Static node 2 view', + 'settings[label_display]' => 1, + 'settings[view_mode]' => 'default', + 'region' => 'bottom', + 'context_mapping[entity]' => $contexts[1]['machine_name'], + ]; + $this->drupalPostForm(NULL, $edit, 'Add block'); + $this->assertText($edit['settings[label]']); + $this->drupalPostForm(NULL, [], 'Finish'); + + // Open the page and verify that the node from the static context is there. + $this->drupalGet($edit_page['path']); + $this->assertText($node->label()); + $this->assertText($node->get('body')->getValue()[0]['value']); + $this->assertText($node2->label()); + $this->assertText($node2->get('body')->getValue()[0]['value']); + + // Change the second static context to the first node. + $this->drupalGet('admin/structure/page_manager/manage/static_node_context/page_variant__static_node_context-block_display-0__contexts'); + $this->clickLink('Edit', 1); + $edit = [ + 'label' => 'Static Node 2 edited', + 'context_value' => $node->getTitle(), + ]; + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText("Static Node 2 edited"); + $this->drupalPostForm(NULL, [], 'Update and save'); + + // Open the page and verify that the node from the static context is there. + $this->drupalGet($edit_page['path']); + $this->assertText($node->label()); + $this->assertText($node->get('body')->getValue()[0]['value']); + // Also make sure the second node is NOT there. + $this->assertNoText($node2->label()); + $this->assertNoText($node2->get('body')->getValue()[0]['value']); + + // Change the first static context to the second node. + $this->drupalGet('admin/structure/page_manager/manage/static_node_context/page_variant__static_node_context-block_display-0__contexts'); + $this->clickLink('Edit'); + $edit = array( + 'label' => 'Static Node edited', + 'context_value' => $node2->getTitle(), + ); + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText("Static Node 2 edited"); + + // Remove the second static context view block from the variant. + $this->drupalGet('admin/structure/page_manager/manage/static_node_context/page_variant__static_node_context-block_display-0__content'); + $this->clickLink('Delete', 1); + $this->drupalPostForm(NULL, [], t('Delete')); + $this->drupalPostForm(NULL, [], 'Update and save'); + + // Make sure only the second static context's node is rendered on the page. + $this->drupalGet($edit_page['path']); + $this->assertNoText($node->label()); + $this->assertNoText($node->get('body')->getValue()[0]['value']); + $this->assertText($node2->label()); + $this->assertText($node2->get('body')->getValue()[0]['value']); + + // Delete a static context and verify that it was deleted. + $this->drupalGet('admin/structure/page_manager/manage/static_node_context/page_variant__static_node_context-block_display-0__contexts'); + $this->clickLink('Delete'); + $this->drupalPostForm(NULL, [], t('Delete')); + $this->assertText("The static context Static Node edited has been removed."); + // Reload the page to clear the message + $this->drupalGet($this->getUrl()); + $this->assertNoText($edit['label']); + + // Test contexts in a new variant. + $this->drupalGet('admin/structure/page_manager/manage/static_node_context/general'); + $this->clickLink('Add variant'); + $edit = [ + 'label' => 'Variant two', + 'variant_plugin_id' => 'block_display', + 'wizard_options[contexts]' => TRUE, + ]; + $this->drupalPostForm(NULL, $edit, 'Next'); + foreach ($contexts as $context) { + $edit = [ + 'context' => 'entity:node', + ]; + $this->drupalPostForm(NULL, $edit, 'Add new context'); + $edit = [ + 'label' => $context['title'], + 'machine_name' => $context['machine_name'], + 'description' => $context['description'], + 'context_value' => $context['node']->getTitle() . ' (' . $context['node']->id() . ')', + ]; + $this->drupalPostForm(NULL, $edit, 'Save'); + $this->assertText($context['title']); + } + $this->drupalPostForm(NULL, [], 'Next'); + + // Configure the new variant. + $variant_edit = [ + 'variant_settings[page_title]' => 'Variant two static context test', + ]; + $this->drupalPostForm(NULL, $variant_edit, 'Next'); + + // Add a block that renders the node from the first static context. + $this->clickLink('Add new block'); + $this->clickLink('Entity view (Content)'); + $edit = [ + 'settings[label]' => 'Static node view', + 'settings[label_display]' => 1, + 'settings[view_mode]' => 'default', + 'region' => 'top', + ]; + $this->drupalPostForm(NULL, $edit, 'Add block'); + $this->assertText($edit['settings[label]']); + + // Add a block that renders the node from the second static context. + $this->clickLink('Add new block'); + $this->clickLink('Entity view (Content)'); + $edit = [ + 'settings[label]' => 'Static node 2 view', + 'settings[label_display]' => 1, + 'settings[view_mode]' => 'default', + 'region' => 'bottom', + 'context_mapping[entity]' => $contexts[1]['machine_name'], + ]; + $this->drupalPostForm(NULL, $edit, 'Add block'); + $this->assertText($edit['settings[label]']); + $this->drupalPostForm(NULL, [], 'Finish'); + } + +} diff --git a/page_manager_ui/src/Wizard/PageAddWizard.php b/page_manager_ui/src/Wizard/PageAddWizard.php new file mode 100644 index 0000000..8bf2213 --- /dev/null +++ b/page_manager_ui/src/Wizard/PageAddWizard.php @@ -0,0 +1,78 @@ + $this->t('Contexts'), + 'form' => PageVariantContextsForm::class, + ]; + $operations['selection'] = [ + 'title' => $this->t('Selection criteria'), + 'form' => PageVariantSelectionForm::class, + ]; + $operations['display_variant'] = [ + 'title' => $this->t('Configure variant'), + 'form' => PageVariantConfigureForm::class, + ]; + + // Hide the Parameters step if there aren't any path parameters. + if (isset($cached_values['page']) && !$cached_values['page']->getParameterNames()) { + unset($operations['parameters']); + } + + // Hide any optional steps that aren't selected. + $optional_steps = ['access', 'contexts', 'selection']; + foreach ($optional_steps as $step_name) { + if (empty($cached_values['wizard_options'][$step_name])) { + unset($operations[$step_name]); + } + } + + // Add any wizard operations from the plugin itself. + if (!empty($cached_values['page_variant'])) { + /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = $cached_values['page_variant']; + $variant_plugin = $page_variant->getVariantPlugin(); + if ($variant_plugin instanceof PluginWizardInterface) { + if ($variant_plugin instanceof ContextAwareVariantInterface) { + $variant_plugin->setContexts($page_variant->getContexts()); + } + $cached_values['plugin'] = $variant_plugin; + foreach ($variant_plugin->getWizardOperations($cached_values) as $name => $operation) { + $operation['values']['plugin'] = $variant_plugin; + $operation['submit'][] = '::submitVariantStep'; + $operations[$name] = $operation; + } + } + } + + return $operations; + } + +} diff --git a/page_manager_ui/src/Wizard/PageEditWizard.php b/page_manager_ui/src/Wizard/PageEditWizard.php new file mode 100644 index 0000000..e9a413b --- /dev/null +++ b/page_manager_ui/src/Wizard/PageEditWizard.php @@ -0,0 +1,314 @@ +getVariants(); + if (!empty($cached_values['deleted_variants'])) { + foreach (array_keys($cached_values['deleted_variants']) as $page_variant_id) { + // @todo There's a bug that adds non-variants to the deleted_variants + // key in the cached_values. This has something to do with adding a + // block_page variant to a page in tempstore that's already had a + // variant previously deleted and then reordering the blocks in a + // region. It's pretty weird, and as we rebuild that UI, I suspect it + // will go away, but the keys aren't manipulated, so we use them + // instead of the entity. + unset($variants[$page_variant_id]); + } + } + // Suppress errors because of https://bugs.php.net/bug.php?id=50688. + @uasort($variants, '\Drupal\page_manager\Entity\PageVariant::sort'); + + foreach ($variants as $page_variant) { + $page_variant->setPageEntity($page); + foreach ($this->getVariantOperations($page_variant, $cached_values) as $name => $operation) { + $operation['values']['page_variant'] = $page_variant; + $operation['breadcrumbs'] = [ + $this->t('Variants'), + $page_variant->label() ?: $this->t('Variant'), + ]; + $operations['page_variant__' . $page_variant->id() . '__' . $name] = $operation; + } + } + } + + return $operations; + } + + /** + * Get operations for the variant. + * + * @param \Drupal\page_manager\PageVariantInterface $page_variant + * The page variant entity. + * @param mixed $cached_values + * The cached values. + * + * @returns array + */ + protected function getVariantOperations(PageVariantInterface $page_variant, $cached_values) { + $operations = []; + $operations['general'] = [ + 'title' => $this->t('General'), + 'form' => PageVariantConfigureForm::class, + ]; + $operations['contexts'] = [ + 'title' => $this->t('Contexts'), + 'form' => PageVariantContextsForm::class, + ]; + $operations['selection'] = [ + 'title' => $this->t('Selection criteria'), + 'form' => PageVariantSelectionForm::class, + ]; + + // Add any wizard operations from the plugin itself. + $variant_plugin = $page_variant->getVariantPlugin(); + if ($variant_plugin instanceof PluginWizardInterface) { + if ($variant_plugin instanceof ContextAwareVariantInterface) { + $variant_plugin->setContexts($page_variant->getContexts()); + } + $cached_values['plugin'] = $variant_plugin; + foreach ($variant_plugin->getWizardOperations($cached_values) as $name => $operation) { + $operation['values']['plugin'] = $variant_plugin; + $operation['submit'][] = '::submitVariantStep'; + $operations[$name] = $operation; + } + } + + return $operations; + } + + /** + * Get action links for the page. + * + * @return array + * An array of associative arrays with the following keys: + * - title: The link text + * - url: A URL object + */ + protected function getPageActionLinks(PageInterface $page) { + $links = []; + + $links[] = [ + 'title' => $this->t('Delete page'), + 'url' => new Url('entity.page.delete_form', [ + 'page' => $this->getMachineName(), + ]), + ]; + + $links[] = [ + 'title' => $this->t('Add variant'), + 'url' => new Url('entity.page_variant.add_form', [ + 'page' => $this->getMachineName(), + ]), + ]; + + $links[] = [ + 'title' => $this->t('Reorder variants'), + 'url' => new Url('entity.page.reorder_variants_form', [ + 'machine_name' => $this->getMachineName(), + ]), + ]; + + return $links; + } + + /** + * {@inheritdoc} + */ + protected function customizeForm(array $form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var $page \Drupal\page_manager\Entity\Page */ + $page = $cached_values['page']; + + // The page actions. + $form['wizard_actions'] = [ + '#theme' => 'links', + '#links' => [], + '#attributes' => [ + 'class' => ['inline'], + ] + ]; + foreach ($this->getPageActionLinks($page) as $action) { + $form['wizard_actions']['#links'][] = $action + [ + 'attributes' => [ + 'class' => 'use-ajax', + 'data-dialog-type' => 'modal', + 'data-dialog-options' => Json::encode([ + 'width' => 700, + ]), + ], + ]; + } + + // The tree of wizard steps. + $form['wizard_tree'] = [ + '#theme' => ['page_manager_wizard_tree'], + '#wizard' => $this, + '#cached_values' => $form_state->getTemporaryValue('wizard'), + ]; + + $form['#theme'] = 'page_manager_wizard_form'; + $form['#attached']['library'][] = 'page_manager_ui/admin'; + return $form; + } + + /** + * {@inheritdoc} + */ + protected function actions(FormInterface $form_object, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + $operation = $this->getOperation($cached_values); + + $actions = []; + + $actions['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Update'), + '#validate' => [ + '::populateCachedValues', + [$form_object, 'validateForm'], + ], + '#submit' => [ + [$form_object, 'submitForm'], + ], + ]; + + $actions['update_and_save'] = [ + '#type' => 'submit', + '#value' => $this->t('Update and save'), + '#button_type' => 'primary', + '#validate' => [ + '::populateCachedValues', + [$form_object, 'validateForm'], + ], + '#submit' => [ + [$form_object, 'submitForm'], + ], + ]; + + $actions['finish'] = [ + '#type' => 'submit', + '#value' => $this->t('Save'), + '#validate' => [ + '::populateCachedValues', + [$form_object, 'validateForm'], + ], + '#submit' => [ + [$form_object, 'submitForm'], + ], + ]; + + $actions['cancel'] = [ + '#type' => 'submit', + '#value' => $this->t('Cancel'), + '#submit' => [ + '::clearTempstore' + ], + ]; + + // Add any submit or validate functions for the step and the global ones. + foreach (['submit', 'update_and_save', 'finish'] as $button) { + if (isset($operation['validate'])) { + $actions[$button]['#validate'] = array_merge($actions[$button]['#validate'], $operation['validate']); + } + $actions[$button]['#validate'][] = '::validateForm'; + if (isset($operation['submit'])) { + $actions[$button]['#submit'] = array_merge($actions[$button]['#submit'], $operation['submit']); + } + $actions[$button]['#submit'][] = '::submitForm'; + } + $actions['update_and_save']['#submit'][] = '::finish'; + $actions['finish']['#submit'][] = '::finish'; + + if ($form_state->get('ajax')) { + $cached_values = $form_state->getTemporaryValue('wizard'); + $ajax_parameters = $this->getNextParameters($cached_values); + $ajax_parameters['step'] = $this->getStep($cached_values); + $actions['submit']['#ajax'] = [ + 'callback' => '::ajaxSubmit', + 'url' => Url::fromRoute($this->getRouteName(), $ajax_parameters), + 'options' => ['query' => \Drupal::request()->query->all() + [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]], + ]; + $actions['update_and_save']['#ajax'] = [ + 'callback' => '::ajaxFinish', + 'url' => Url::fromRoute($this->getRouteName(), $ajax_parameters), + 'options' => ['query' => \Drupal::request()->query->all() + [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]], + ]; + $actions['finish']['#ajax'] = [ + 'callback' => '::ajaxFinish', + 'url' => Url::fromRoute($this->getRouteName(), $ajax_parameters), + 'options' => ['query' => \Drupal::request()->query->all() + [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]], + ]; + } + + return $actions; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + // Normally, the wizard only saves the data when the 'Next' button is + // clicked, but we want to save the data always when editing. + $this->getTempstore()->set($this->getMachineName(), $cached_values); + } + + /** + * @inheritDoc + */ + public function finish(array &$form, FormStateInterface $form_state) { + // Delete any of the variants marked for deletion. + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var \Drupal\page_manager\Entity\Page $page */ + $page = $cached_values['page']; + if (!empty($cached_values['deleted_variants'])) { + foreach (array_keys($cached_values['deleted_variants']) as $page_variant_id) { + $page->removeVariant($page_variant_id); + } + } + + parent::finish($form, $form_state); + } + + /** + * Clears the temporary store. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public function clearTempstore(array &$form, FormStateInterface $form_state) { + $this->getTempstore()->delete($this->getMachineName()); + } + +} diff --git a/page_manager_ui/src/Wizard/PageVariantAddWizard.php b/page_manager_ui/src/Wizard/PageVariantAddWizard.php new file mode 100644 index 0000000..9ebf82b --- /dev/null +++ b/page_manager_ui/src/Wizard/PageVariantAddWizard.php @@ -0,0 +1,198 @@ +t('Page Variant'); + } + + /** + * {@inheritdoc} + */ + public function getMachineLabel() { + return $this->t('Label'); + } + + /** + * {@inheritdoc} + */ + public function getRouteName() { + return 'entity.page_variant.add_step_form'; + } + + /** + * {@inheritdoc} + */ + public function initValues() { + $cached_values = parent::initValues(); + $cached_values['access'] = new PageManagerPluginAccess(); + return $cached_values; + } + + /** + * {@inheritdoc} + */ + public function getOperations($cached_values) { + $operations = []; + $operations['type'] = [ + 'title' => $this->t('Page variant type'), + 'form' => PageVariantAddForm::class, + ]; + $operations['contexts'] = [ + 'title' => $this->t('Contexts'), + 'form' => AddVariantContextsForm::class, + ]; + $operations['selection'] = [ + 'title' => $this->t('Selection criteria'), + 'form' => AddVariantSelectionForm::class, + ]; + $operations['configure'] = [ + 'title' => $this->t('Configure variant'), + 'form' => PageVariantConfigureForm::class, + ]; + + // Hide any optional steps that aren't selected. + $optional_steps = ['selection', 'contexts']; + foreach ($optional_steps as $step_name) { + if (isset($cached_values['wizard_options']) && empty($cached_values['wizard_options'][$step_name])) { + unset($operations[$step_name]); + } + } + + // Add any wizard operations from the plugin itself. + if (!empty($cached_values['page_variant']) && !empty($cached_values['variant_plugin_id'])) { + /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = $cached_values['page_variant']; + $variant_plugin = $page_variant->getVariantPlugin(); + if ($variant_plugin instanceof PluginWizardInterface) { + if ($variant_plugin instanceof ContextAwareVariantInterface) { + $variant_plugin->setContexts($page_variant->getContexts()); + } + $cached_values['plugin'] = $variant_plugin; + foreach ($variant_plugin->getWizardOperations($cached_values) as $name => $operation) { + $operation['values']['plugin'] = $variant_plugin; + $operations[$name] = $operation; + } + } + } + + return $operations; + } + + /** + * {@inheritdoc} + */ + protected function customizeForm(array $form, FormStateInterface $form_state) { + $form = parent::customizeForm($form, $form_state); + + // We set the variant id as part of form submission. + if ($this->step == 'type' && isset($form['name']['id'])) { + unset($form['name']['id']); + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, $page = NULL) { + $form = parent::buildForm($form, $form_state); // TODO: Change the autogenerated stub + + // Get the page tempstore so we can modify the unsaved page. + if (!isset($cached_values['page']) || !$cached_values['page']->id()) { + $cached_values = $form_state->getTemporaryValue('wizard'); + $page_tempstore = $this->tempstore->get('page_manager.page')->get($page); + $cached_values['page'] = $page_tempstore['page']; + $form_state->setTemporaryValue('wizard', $cached_values); + } + + // Hide form elements that are not useful during the add wizard. + if ($this->step == 'configure') { + $form['page_variant_label']['#type'] = 'value'; + unset($form['delete']); + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function getNextParameters($cached_values) { + $parameters = parent::getNextParameters($cached_values); + + // Add the page to the url parameters. + $parameters['page'] = $cached_values['page']->id(); + return $parameters; + } + + /** + * {@inheritdoc} + */ + public function getPreviousParameters($cached_values) { + $parameters = parent::getPreviousParameters($cached_values); + + // Add the page to the url parameters. + $parameters['page'] = $cached_values['page']->id(); + return $parameters; + } + + /** + * {@inheritdoc} + */ + public function finish(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + + // Add the variant to the parent page tempstore. + $page_tempstore = $this->tempstore->get('page_manager.page')->get($cached_values['page']->id()); + $page_tempstore['page']->addVariant($cached_values['page_variant']); + $this->tempstore->get('page_manager.page')->set($cached_values['page']->id(), $page_tempstore); + + $variant_plugin = $cached_values['page_variant']->getVariantPlugin(); + drupal_set_message($this->t('The %label @entity_type has been added to the page, but has not been saved. Please save the page to store changes.', array( + '%label' => $cached_values['page_variant']->label(), + '@entity_type' => $variant_plugin->adminLabel(), + ))); + + $form_state->setRedirectUrl(new Url('entity.page.edit_form', [ + 'machine_name' => $cached_values['page']->id(), + 'step' => 'general', + ])); + } + +} diff --git a/page_manager_ui/src/Wizard/PageWizardBase.php b/page_manager_ui/src/Wizard/PageWizardBase.php new file mode 100644 index 0000000..e6529f2 --- /dev/null +++ b/page_manager_ui/src/Wizard/PageWizardBase.php @@ -0,0 +1,117 @@ +t('Page Manager'); + } + + /** + * {@inheritdoc} + */ + public function getMachineLabel() { + return $this->t('Administrative title'); + } + + /** + * {@inheritdoc} + */ + public function getOperations($cached_values) { + $operations = []; + $operations['general'] = [ + 'title' => $this->t('Page information'), + 'form' => PageGeneralForm::class, + ]; + /** @var $page \Drupal\page_manager\Entity\Page */ + $page = $cached_values['page']; + + if ($page) { + $matches = []; + preg_match_all('|\{\w+\}|', $page->getPath(), $matches); + if (array_filter($matches)) { + $operations['parameters'] = [ + 'title' => $this->t('Page parameters'), + 'form' => PageParametersForm::class, + ]; + } + } + $operations['access'] = [ + 'title' => $this->t('Page access'), + 'form' => PageAccessForm::class, + ]; + + return $operations; + } + + /** + * Submission callback for the variant plugin steps. + */ + public function submitVariantStep(array &$form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = $cached_values['page_variant']; + /** @var \Drupal\Core\Display\VariantInterface $plugin */ + $plugin = $cached_values['plugin']; + + // Make sure the variant plugin on the page variant gets the configuration + // from the 'plugin' which should have been setup by the variant's steps. + if (!empty($plugin) && !empty($page_variant)) { + $page_variant->getVariantPlugin()->setConfiguration($plugin->getConfiguration()); + } + } + + public function finish(array &$form, FormStateInterface $form_state) { + parent::finish($form, $form_state); + + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var \Drupal\page_manager\Entity\Page $page */ + $page = $cached_values['page']; + foreach($page->getVariants() as $variant) { + $variant->save(); + } + + $form_state->setRedirectUrl(new Url('entity.page.edit_form', [ + 'machine_name' => $this->machine_name, + 'step' => $this->step + ])); + } + +} diff --git a/page_manager_ui/src/Wizard/RouteParameters.php b/page_manager_ui/src/Wizard/RouteParameters.php new file mode 100644 index 0000000..26996dc --- /dev/null +++ b/page_manager_ui/src/Wizard/RouteParameters.php @@ -0,0 +1,86 @@ + [ + 'title' => $this->t('Assign Parameter Context'), + 'form' => ParameterAssignContextForm::class, + ], + 'settings' => [ + 'title' => $this->t('Parameter Settings'), + 'form' => ParameterSettingsForm::class, + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getRouteName() { + return 'page_manager.route.parameters.configure'; + } + + /** + * Override to get the parameter from the URL and make it available to steps. + */ + public function buildForm(array $form, FormStateInterface $form_state, $parameter = NULL) { + $this->parameter = $parameter; + return parent::buildForm($form, $form_state); + } + + public function getNextParameters($cached_values) { + $parameters = parent::getNextParameters($cached_values); + $parameters['parameter'] = $this->parameter; + return $parameters; + } + + public function getPreviousParameters($cached_values) { + $parameters = parent::getPreviousParameters($cached_values); + $parameters['parameter'] = $this->parameter; + return $parameters; + } + + /** + * Save the values to the tempstore. + */ + public function finish(array &$form, FormStateInterface $form_state) { + $this->getTempstore()->set($this->getMachineName(), $form_state->getTemporaryValue('wizard')); + } + + public function ajaxFinish(array $form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + $response = new AjaxResponse(); + $response->addCommand(new RedirectCommand($this->url('entity.page.edit_form', ['machine_name' => $cached_values['id'], 'step' => 'parameters']))); + $response->addCommand(new CloseModalDialogCommand()); + return $response; + } + +} diff --git a/page_manager_ui/templates/page-manager-wizard-form.html.twig b/page_manager_ui/templates/page-manager-wizard-form.html.twig new file mode 100644 index 0000000..82b45d9 --- /dev/null +++ b/page_manager_ui/templates/page-manager-wizard-form.html.twig @@ -0,0 +1,31 @@ +{# +/** + * @file + * Default theme implementation for a 'form' element. + * + * Available variables + * - attributes: A list of HTML attributes for the wrapper element. + * - children: The child elements of the form. + * + * @see template_preprocess_form() + * + * @ingroup themeable + */ +#} +
+
+ {{ form.wizard_actions }} +
+
+
+ {{ form.wizard_tree }} +
+
+ {{ form|without('wizard_actions', 'wizard_tree', 'actions') }} +
+
+ +
+ {{ form.actions }} +
+
diff --git a/page_manager_ui/templates/page-manager-wizard-tree.html.twig b/page_manager_ui/templates/page-manager-wizard-tree.html.twig new file mode 100644 index 0000000..1ae866d --- /dev/null +++ b/page_manager_ui/templates/page-manager-wizard-tree.html.twig @@ -0,0 +1,47 @@ +{# +/** + * @file + * Default theme implementation to display wizard tree. + * + * Available variables: + * - step: The current step name. + * - tree: A nested list of menu items. Each menu item contains: + * - title: The menu link title. + * - url: The menu link url, instance of \Drupal\Core\Url + * - children: The menu item child items. + * - step: The name of the step. + * + * @ingroup themeable + */ +#} +{% import _self as page_manager %} + +{# + We call a macro which calls itself to render the full tree. + @see http://twig.sensiolabs.org/doc/tags/macro.html +#} +{{ page_manager.wizard_tree(tree, step, 0) }} + +{% macro wizard_tree(items, step, menu_level) %} + {% import _self as page_manager %} + {% if items %} + + {% endif %} +{% endmacro %} diff --git a/src/Entity/Page.php b/src/Entity/Page.php index 49c5a8a..6fd9f33 100644 --- a/src/Entity/Page.php +++ b/src/Entity/Page.php @@ -8,6 +8,9 @@ namespace Drupal\page_manager\Entity; use Drupal\Component\Plugin\Context\ContextInterface; +use Drupal\Core\Cache\CacheableMetadata; +use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\page_manager\Event\PageManagerContextEvent; use Drupal\page_manager\Event\PageManagerEvents; use Drupal\page_manager\PageInterface; @@ -34,6 +37,7 @@ use Drupal\page_manager\PageVariantInterface; * config_export = { * "id", * "label", + * "description", * "use_admin_theme", * "path", * "access_logic", @@ -59,6 +63,13 @@ class Page extends ConfigEntityBase implements PageInterface { protected $label; /** + * The description of the page entity. + * + * @var string + */ + protected $description; + + /** * The path of the page entity. * * @var string @@ -123,6 +134,13 @@ class Page extends ConfigEntityBase implements PageInterface { /** * {@inheritdoc} */ + public function getDescription() { + return $this->description; + } + + /** + * {@inheritdoc} + */ public function getPath() { return $this->path; } @@ -223,7 +241,11 @@ class Page extends ConfigEntityBase implements PageInterface { * {@inheritdoc} */ public function getParameters() { - return $this->parameters; + $names = $this->getParameterNames(); + if ($names) { + return array_intersect_key($this->parameters, array_flip($names)); + } + return []; } /** @@ -245,6 +267,12 @@ class Page extends ConfigEntityBase implements PageInterface { 'type' => $type, 'label' => $label, ]; + // Reset contexts when a parameter is added or changed. + $this->contexts = []; + // Reset the contexts of every variant. + foreach ($this->getVariants() as $page_variant) { + $page_variant->resetCollectedContexts(); + } return $this; } @@ -253,6 +281,12 @@ class Page extends ConfigEntityBase implements PageInterface { */ public function removeParameter($name) { unset($this->parameters[$name]); + // Reset contexts when a parameter is removed. + $this->contexts = []; + // Reset the contexts of every variant. + foreach ($this->getVariants() as $page_variant) { + $page_variant->resetCollectedContexts(); + } return $this; } @@ -281,8 +315,10 @@ class Page extends ConfigEntityBase implements PageInterface { * @return $this */ protected function filterParameters() { - foreach ($this->getParameters() as $name => $parameter) { - if (empty($parameter['type'])) { + $names = $this->getParameterNames(); + foreach ($this->get('parameters') as $name => $parameter) { + // Remove parameters without any type, or which are no longer valid. + if (empty($parameter['type']) || !in_array($name, $names)) { $this->removeParameter($name); } } @@ -300,8 +336,34 @@ class Page extends ConfigEntityBase implements PageInterface { * {@inheritdoc} */ public function getContexts() { + // @todo add the other global contexts here as they are added + // @todo maybe come up with a non-hardcoded way of doing this? + $global_contexts = [ + 'current_user' + ]; if (!$this->contexts) { $this->eventDispatcher()->dispatch(PageManagerEvents::PAGE_CONTEXT, new PageManagerContextEvent($this)); + foreach ($this->getParameters() as $machine_name => $configuration) { + // Parameters can be updated in the UI, so unless it's a global context + // we'll need to rely on the current settings in the tempstore instead + // of the ones cached in the router. + if (!isset($global_contexts[$machine_name])) { + // First time through, parameters will not be defined by the route. + if (!isset($this->contexts[$machine_name])) { + $cacheability = new CacheableMetadata(); + $cacheability->setCacheContexts(['route']); + + $context_definition = new ContextDefinition($configuration['type'], $configuration['label']); + $context = new Context($context_definition); + $context->addCacheableDependency($cacheability); + $this->contexts[$machine_name] = $context; + } + else { + $this->contexts[$machine_name]->getContextDefinition()->setDataType($configuration['type']); + $this->contexts[$machine_name]->getContextDefinition()->setLabel($configuration['label']); + } + } + } } return $this->contexts; } @@ -310,7 +372,13 @@ class Page extends ConfigEntityBase implements PageInterface { * {@inheritdoc} */ public function addVariant(PageVariantInterface $variant) { + // If variants hasn't been initialized, we initialize it before adding the + // new variant. + if ($this->variants === NULL) { + $this->getVariants(); + } $this->variants[$variant->id()] = $variant; + $this->sortVariants(); return $this; } @@ -330,6 +398,7 @@ class Page extends ConfigEntityBase implements PageInterface { */ public function removeVariant($variant_id) { $this->getVariant($variant_id)->delete(); + unset($this->variants[$variant_id]); return $this; } @@ -343,10 +412,19 @@ class Page extends ConfigEntityBase implements PageInterface { foreach ($this->variantStorage()->loadByProperties(['page' => $this->id()]) as $variant) { $this->variants[$variant->id()] = $variant; } + $this->sortVariants(); + } + return $this->variants; + } + + /** + * Sort variants. + */ + protected function sortVariants() { + if (isset($this->variants)) { // Suppress errors because of https://bugs.php.net/bug.php?id=50688. @uasort($this->variants, [$this, 'variantSortHelper']); } - return $this->variants; } /** @@ -378,25 +456,28 @@ class Page extends ConfigEntityBase implements PageInterface { public function __sleep() { $vars = parent::__sleep(); - // Ensure any plugin collections are stored correctly before serializing. - // @todo Let https://www.drupal.org/node/2650588 handle this instead. - foreach ($this->getPluginCollections() as $plugin_config_key => $plugin_collection) { - $this->set($plugin_config_key, $plugin_collection->getConfiguration()); + // Gathered contexts objects should not be serialized. + if (($key = array_search('contexts', $vars)) !== FALSE) { + unset($vars[$key]); } - // Avoid serializing plugin collections and entities as they might contain - // references to a lot of objects including the container. - $unset_vars = [ - 'variants', - 'accessConditionCollection', - ]; - foreach ($unset_vars as $unset_var) { - if (!empty($this->{$unset_var})) { - unset($vars[array_search($unset_var, $vars)]); - } + return $vars; + } + + /** + * {@inheritdoc} + * + * @todo: Remove this as part of https://www.drupal.org/node/2696683. + */ + protected function urlRouteParameters($rel) { + if ($rel == 'edit-form') { + $uri_route_parameters = []; + $uri_route_parameters['machine_name'] = $this->id(); + $uri_route_parameters['step'] = 'general'; + return $uri_route_parameters; } - return $vars; + return parent::urlRouteParameters($rel); } } diff --git a/src/Entity/PageVariant.php b/src/Entity/PageVariant.php index f6b729c..58ad7f8 100644 --- a/src/Entity/PageVariant.php +++ b/src/Entity/PageVariant.php @@ -13,6 +13,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection; +use Drupal\page_manager\PageInterface; use Drupal\page_manager\PageVariantInterface; /** @@ -100,6 +101,13 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface { protected $page; /** + * The loaded page entity this page variant entity belongs to. + * + * @var \Drupal\page_manager\PageInterface + */ + protected $pageEntity; + + /** * The plugin configuration for the selection criteria condition plugins. * * @var array @@ -212,6 +220,9 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface { */ protected function getVariantPluginCollection() { if (!$this->variantPluginCollection) { + if (empty($this->variant_settings['uuid'])) { + $this->variant_settings['uuid'] = $this->uuidGenerator()->generate(); + } $this->variantPluginCollection = new DefaultSingleLazyPluginCollection(\Drupal::service('plugin.manager.display_variant'), $this->variant, $this->variant_settings); } return $this->variantPluginCollection; @@ -234,11 +245,35 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface { /** * {@inheritdoc} */ + public function setVariantPluginId($variant) { + $this->variant = $variant; + return $this; + } + + /** + * {@inheritdoc} + */ public function getPage() { - if (!$this->page) { - throw new \UnexpectedValueException('The page variant has no associated page'); + if (!$this->pageEntity) { + if (!$this->page) { + throw new \UnexpectedValueException('The page variant has no associated page'); + } + $this->pageEntity = $this->getPageStorage()->load($this->page); + if (!$this->pageEntity) { + throw new \UnexpectedValueException(sprintf('The page %s could not be loaded', $this->page)); + } } - return $this->getPageStorage()->load($this->page); + + return $this->pageEntity; + } + + /** + * {@inheritdoc} + */ + public function setPageEntity(PageInterface $page) { + $this->pageEntity = $page; + $this->page = $page->id(); + return $this; } /** @@ -248,17 +283,15 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface { if (is_null($this->contexts)) { $static_contexts = $this->getContextMapper()->getContextValues($this->getStaticContexts()); $page_contexts = $this->getPage()->getContexts(); - $this->contexts = array_merge($static_contexts, $page_contexts); + $this->contexts = $page_contexts + $static_contexts; } return $this->contexts; } /** - * Resets the collected contexts. - * - * @return $this + * {@inheritdoc} */ - protected function resetCollectedContexts() { + public function resetCollectedContexts() { $this->contexts = NULL; return $this; } @@ -428,8 +461,12 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface { */ public function __sleep() { $vars = parent::__sleep(); + // Gathered contexts objects should not be serialized. - unset($vars[array_search('contexts', $vars)]); + if (($key = array_search('contexts', $vars)) !== FALSE) { + unset($vars[$key]); + } + return $vars; } diff --git a/src/PageInterface.php b/src/PageInterface.php index 96eaab2..f9e74ec 100644 --- a/src/PageInterface.php +++ b/src/PageInterface.php @@ -25,6 +25,14 @@ interface PageInterface extends ConfigEntityInterface, EntityWithPluginCollectio public function status(); /** + * Returns the description for the page entity. + * + * @return string + * The description for the page entity. + */ + public function getDescription(); + + /** * Returns the path for the page entity. * * @return string diff --git a/src/PageVariantInterface.php b/src/PageVariantInterface.php index 9705d8c..d0cd19a 100644 --- a/src/PageVariantInterface.php +++ b/src/PageVariantInterface.php @@ -30,6 +30,19 @@ interface PageVariantInterface extends ConfigEntityInterface, EntityWithPluginCo public function getVariantPluginId(); /** + * Sets the plugin ID of the variant plugin without loading the Plugin + * collections. + * + * @param string $variant + * The plugin ID of the variant plugin. + * + * @return $this + * + * @see \Drupal\page_manager\Entity\PageVariant::getPluginCollections() + */ + public function setVariantPluginId($variant); + + /** * Gets the page this variant is on. * * @return \Drupal\page_manager\PageInterface @@ -37,6 +50,19 @@ interface PageVariantInterface extends ConfigEntityInterface, EntityWithPluginCo public function getPage(); /** + * Sets the page with a full entity object. + * + * This is mainly useful for setting an unsaved page on a page variant so you + * can continue to work with it prior to saving. + * + * @param \Drupal\page_manager\PageInterface $page + * The page entity object this variant is associated with. + * + * @return $this + */ + public function setPageEntity(PageInterface $page); + + /** * Gets the values for all defined contexts. * * @return \Drupal\Core\Plugin\Context\ContextInterface[] @@ -45,6 +71,13 @@ interface PageVariantInterface extends ConfigEntityInterface, EntityWithPluginCo public function getContexts(); /** + * Resets the collected contexts. + * + * @return $this + */ + public function resetCollectedContexts(); + + /** * Gets the weight of this variant (compared to other variants on the page). * * @return int diff --git a/src/Plugin/DisplayVariant/HttpStatusCodeDisplayVariant.php b/src/Plugin/DisplayVariant/HttpStatusCodeDisplayVariant.php index 048b8f8..47d0fa8 100644 --- a/src/Plugin/DisplayVariant/HttpStatusCodeDisplayVariant.php +++ b/src/Plugin/DisplayVariant/HttpStatusCodeDisplayVariant.php @@ -41,7 +41,7 @@ class HttpStatusCodeDisplayVariant extends VariantBase { ] + $options; // Add the HTTP status code, so it's easier for people to find it. - array_walk($options, function($title, $code) { + array_walk($options, function($title, $code) use (&$options) { $options[$code] = $this->t('@code (@title)', ['@code' => $code, '@title' => $title]); }); diff --git a/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php b/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php index 654dc4c..49c4a3a 100644 --- a/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php +++ b/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php @@ -21,6 +21,8 @@ use Drupal\Core\Render\Element; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Utility\Token; use Drupal\ctools\Plugin\DisplayVariant\BlockDisplayVariant; +use Drupal\ctools\Plugin\PluginWizardInterface; +use Drupal\page_manager_ui\Form\VariantPluginContentForm; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -31,7 +33,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * admin_label = @Translation("Block page") * ) */ -class PageBlockDisplayVariant extends BlockDisplayVariant { +class PageBlockDisplayVariant extends BlockDisplayVariant implements PluginWizardInterface { /** * The module handler. @@ -248,6 +250,18 @@ class PageBlockDisplayVariant extends BlockDisplayVariant { /** * {@inheritdoc} */ + public function getWizardOperations($cached_values) { + return [ + 'content' => [ + 'title' => $this->t('Content'), + 'form' => VariantPluginContentForm::class, + ], + ]; + } + + /** + * {@inheritdoc} + */ public function defaultConfiguration() { return parent::defaultConfiguration() + [ 'page_title' => '', @@ -300,4 +314,18 @@ class PageBlockDisplayVariant extends BlockDisplayVariant { ]; } + /** + * {@inheritdoc} + */ + public function __sleep() { + $vars = parent::__sleep(); + + // Gathered contexts objects should not be serialized. + if (($key = array_search('contexts', $vars)) !== FALSE) { + unset($vars[$key]); + } + + return $vars; + } + } diff --git a/src/Tests/PageConfigSchemaTest.php b/src/Tests/PageConfigSchemaTest.php index 2af9051..9c4bafb 100644 --- a/src/Tests/PageConfigSchemaTest.php +++ b/src/Tests/PageConfigSchemaTest.php @@ -39,6 +39,9 @@ class PageConfigSchemaTest extends KernelTestBase { */ public function testValidPageConfigSchema() { $id = 'node_view'; + $label = 'Node view'; + $description = 'When enabled, this overrides the default Drupal behavior for displaying nodes at /node/{node}. If you add variants, you may use selection criteria such as node type or language or user access to provide different views of nodes. If no variant is selected, the default Drupal node view will be used. This page only affects nodes viewed as pages, it will not affect nodes viewed in lists or at other locations.'; + /** @var \Drupal\page_manager\PageInterface $page */ $page = Page::load($id); @@ -91,6 +94,9 @@ class PageConfigSchemaTest extends KernelTestBase { $page_config = \Drupal::config("page_manager.page.$id"); $this->assertSame($page_config->get('id'), $id); + $this->assertSame($page_config->get('label'), $label); + $this->assertSame($page_config->get('description'), $description); + $variant_config = \Drupal::config("page_manager.page_variant.$page_variant_id"); $this->assertSame($variant_config->get('id'), $page_variant_id); diff --git a/src/Tests/PageManagerConfigTranslationTest.php b/src/Tests/PageManagerConfigTranslationTest.php new file mode 100644 index 0000000..0627d38 --- /dev/null +++ b/src/Tests/PageManagerConfigTranslationTest.php @@ -0,0 +1,68 @@ +save(); + + $this->drupalLogin($this->drupalCreateUser(['administer site configuration', 'translate configuration'])); + + PageVariant::create([ + 'variant' => 'http_status_code', + 'label' => 'HTTP status code', + 'id' => 'http_status_code', + 'page' => 'node_view', + ])->save(); + } + + /** + * Tests config translation. + */ + public function testTranslation() { + $this->drupalGet('admin/config/regional/config-translation'); + $this->assertLinkByHref('admin/config/regional/config-translation/page'); + $this->assertLinkByHref('admin/config/regional/config-translation/page_variant'); + + $this->drupalGet('admin/config/regional/config-translation/page'); + $this->assertText('Node view'); + $this->clickLink('Translate'); + $this->clickLink('Add'); + $this->assertField('translation[config_names][page_manager.page.node_view][label]'); + + $this->drupalGet('admin/config/regional/config-translation/page_variant'); + $this->assertText('HTTP status code'); + $this->clickLink('Translate'); + $this->clickLink('Add'); + $this->assertField('translation[config_names][page_manager.page_variant.http_status_code][label]'); + } + +} diff --git a/src/Tests/PageNodeSelectionTest.php b/src/Tests/PageNodeSelectionTest.php index e1816da..95d3570 100644 --- a/src/Tests/PageNodeSelectionTest.php +++ b/src/Tests/PageNodeSelectionTest.php @@ -84,7 +84,11 @@ class PageNodeSelectionTest extends WebTestBase { 'page' => 'node_view', ]); $block_page_plugin = $block_page_variant->getVariantPlugin(); + $this->assertTrue(!empty($block_page_plugin->getConfiguration()['uuid'])); + $uuid = $block_page_plugin->getConfiguration()['uuid']; $block_page_plugin->setConfiguration(['page_title' => '[node:title]']); + $second_uuid = $block_page_plugin->getConfiguration()['uuid']; + $this->assertEqual($uuid, $second_uuid); /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $block_page_plugin */ $block_page_plugin->addBlock([ 'id' => 'entity_view:node', @@ -121,6 +125,8 @@ class PageNodeSelectionTest extends WebTestBase { // Test cacheability metadata. $this->drupalGet('node/' . $node3->id()); + $this->assertTitle($node3->label() . ' | Drupal'); + $this->assertText($node3->body->value); $this->assertNoText($node2->label()); // Ensure this doesn't affect the /node/add page. diff --git a/src/Tests/PagePlaceholderTest.php b/src/Tests/PagePlaceholderTest.php index 9f940e3..a2ef479 100644 --- a/src/Tests/PagePlaceholderTest.php +++ b/src/Tests/PagePlaceholderTest.php @@ -53,12 +53,16 @@ class PagePlaceholderTest extends WebTestBase { $page->save(); // Create a new variant. + /* @var $http_status_variant \Drupal\page_manager\Entity\PageVariant */ $http_status_variant = PageVariant::create([ - 'variant' => 'http_status_code', 'label' => 'HTTP status code', 'id' => 'http_status_code', 'page' => 'placeholder', ]); + + // Test setting variant post create works. + $http_status_variant->setVariantPluginId('http_status_code'); + $http_status_variant->getVariantPlugin()->setConfiguration(['status_code' => 200]); $http_status_variant->save(); $this->triggerRouterRebuild(); diff --git a/src/Tests/PageTestHelperTrait.php b/src/Tests/PageTestHelperTrait.php index ca7480f..aa5b339 100644 --- a/src/Tests/PageTestHelperTrait.php +++ b/src/Tests/PageTestHelperTrait.php @@ -12,12 +12,7 @@ namespace Drupal\page_manager\Tests; */ trait PageTestHelperTrait { - /** - * @see \Drupal\simpletest\TestBase::$container - * - * @var \Symfony\Component\DependencyInjection\ContainerInterface - */ - protected $container; + // @fixme: Remove this change when https://www.drupal.org/node/2684281 is fixed. /** * Triggers a router rebuild. diff --git a/src/Tests/StaticContextTest.php b/src/Tests/StaticContextTest.php deleted file mode 100644 index 2b382e7..0000000 --- a/src/Tests/StaticContextTest.php +++ /dev/null @@ -1,179 +0,0 @@ -drupalCreateContentType(['type' => 'article', 'name' => 'Article']); - $this->drupalLogin($this->drupalCreateUser(['administer pages', 'create article content'])); - - $this->drupalPlaceBlock('page_title_block'); - } - - /** - * Tests that a node bundle condition controls the node view page. - */ - public function testStaticContext() { - // Create a node, and check its page. - $node = $this->drupalCreateNode(['type' => 'article']); - $node2 = $this->drupalCreateNode(['type' => 'article']); - $this->drupalGet('node/' . $node->id()); - $this->assertResponse(200); - $this->assertText($node->label()); - $this->assertTitle($node->label() . ' | Drupal'); - - // Create a new page entity. - $edit_page = [ - 'label' => 'Static node context', - 'id' => 'static_node_context', - 'path' => 'static-context', - ]; - $this->drupalPostForm('admin/structure/page_manager/add', $edit_page, 'Save'); - - // Add a new variant. - $this->clickLink('Add new variant'); - $this->clickLink('Block page'); - $variant_edit = [ - 'label' => 'Static context blocks', - 'id' => 'block_page', - 'variant_settings[page_title]' => 'Static context test page', - ]; - $this->drupalPostForm(NULL, $variant_edit, 'Save'); - - // Add a static context for each node to the page variant. - $contexts = array( - array( - 'title' => 'Static Node', - 'machine_name' => 'static_node', - 'node' => $node, - ), - array( - 'title' => 'Static Node 2', - 'machine_name' => 'static_node_2', - 'node' => $node2, - ), - ); - foreach ($contexts as $context) { - $this->clickLink('Add new static context'); - $edit = array( - 'label' => $context['title'], - 'machine_name' => $context['machine_name'], - 'entity_type' => 'node', - 'selection' => $context['node']->getTitle(), - ); - $this->drupalPostForm(NULL, $edit, 'Add Static Context'); - $this->assertText('The ' . $edit['label'] . ' static context has been added.'); - } - - // Add a block that renders the node from the first static context. - $this->clickLink('Add new block'); - $this->clickLink('Entity view (Content)'); - $edit = [ - 'settings[label]' => 'Static node view', - 'settings[label_display]' => 1, - 'settings[view_mode]' => 'default', - 'region' => 'top', - ]; - $this->drupalPostForm(NULL, $edit, 'Add block'); - $this->assertText($edit['settings[label]']); - - // Add a block that renders the node from the second static context. - $this->clickLink('Add new block'); - $this->clickLink('Entity view (Content)'); - $edit = [ - 'settings[label]' => 'Static node 2 view', - 'settings[label_display]' => 1, - 'settings[view_mode]' => 'default', - 'region' => 'bottom', - 'context_mapping[entity]' => $contexts[1]['machine_name'], - ]; - $this->drupalPostForm(NULL, $edit, 'Add block'); - $this->assertText($edit['settings[label]']); - - // Open the page and verify that the node from the static context is there. - $this->drupalGet($edit_page['path']); - $this->assertText($node->label()); - $this->assertText($node->get('body')->getValue()[0]['value']); - $this->assertText($node2->label()); - $this->assertText($node2->get('body')->getValue()[0]['value']); - - // Change the second static context to the first node. - $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']); - $this->clickLink('Edit'); - $this->clickLink('Edit', 3); - $edit = array( - 'label' => 'Static Node 2 edited', - 'entity_type' => 'node', - 'selection' => $node->getTitle(), - ); - $this->drupalPostForm(NULL, $edit, t('Update Static Context')); - $this->assertText('The ' . $edit['label'] . ' static context has been updated.'); - - // Open the page and verify that the node from the static context is there. - $this->drupalGet($edit_page['path']); - $this->assertText($node->label()); - $this->assertText($node->get('body')->getValue()[0]['value']); - // Also make sure the second node is NOT there. - $this->assertNoText($node2->label()); - $this->assertNoText($node2->get('body')->getValue()[0]['value']); - - // Change the first static context to the second node. - $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']); - $this->clickLink('Edit'); - $this->clickLink('Edit', 2); - $edit = array( - 'label' => 'Static Node edited', - 'entity_type' => 'node', - 'selection' => $node2->getTitle(), - ); - $this->drupalPostForm(NULL, $edit, t('Update Static Context')); - $this->assertText('The ' . $edit['label'] . ' static context has been updated.'); - - // Remove the second static context view block from the variant. - $this->clickLink('Delete', 1); - $this->drupalPostForm(NULL, NULL, t('Delete')); - - // Make sure only the second static context's node is rendered on the page. - $this->drupalGet($edit_page['path']); - $this->assertNoText($node->label()); - $this->assertNoText($node->get('body')->getValue()[0]['value']); - $this->assertText($node2->label()); - $this->assertText($node2->get('body')->getValue()[0]['value']); - - // Delete a static context and verify that it was deleted. - $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']); - $this->clickLink('Edit'); - $this->clickLink('Delete', 1); - $this->drupalPostForm(NULL, NULL, t('Delete')); - $this->assertText('The static context ' . $edit['label'] . ' has been removed.'); - $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']); - $this->assertNoText($edit['label']); - } - -} diff --git a/tests/src/Kernel/PageVariantPageEntityTest.php b/tests/src/Kernel/PageVariantPageEntityTest.php new file mode 100644 index 0000000..874cb7c --- /dev/null +++ b/tests/src/Kernel/PageVariantPageEntityTest.php @@ -0,0 +1,86 @@ + 'test_page']); + $page->save(); + + /* @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = PageVariant::create([ + 'id' => 'test_page_variant', + 'page' => 'test_page', + ]); + + // Get the page from the variant. + $page_first = $page_variant->getPage(); + $this->assertNotEmpty($page_first); + $page_second = $page_variant->getPage(); + $this->assertEquals(spl_object_hash($page_first), spl_object_hash($page_second)); + } + + /** + * Tests that a an unsaved page can be set against a page variant. + */ + public function testUnsavedPage() { + /* @var \Drupal\page_manager\PageInterface $page */ + $page = Page::create(['id' => 'test_page']); + + /* @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = PageVariant::create([ + 'id' => 'test_page_variant', + 'page' => 'test_page', + ]); + $page_variant->setPageEntity($page); + + // Get the page from the variant. + $page_result = $page_variant->getPage(); + $this->assertEquals($page, $page_result); + } + + /** + * Tests that a page gets cached on the page variant. + */ + public function testChangePageId() { + /* @var \Drupal\page_manager\PageVariantInterface $page_variant */ + $page_variant = PageVariant::create(['id' => 'test_page_variant']); + + // Check the page gets set correctly. + /* @var \Drupal\page_manager\PageInterface $page */ + $page1 = Page::create(['id' => 'test_page_1']); + $page_variant->setPageEntity($page1); + $this->assertEquals('test_page_1', $page_variant->get('page')); + + // Check the page gets changed correctly. + /* @var \Drupal\page_manager\PageInterface $page */ + $page2 = Page::create(['id' => 'test_page_2']); + $page_variant->setPageEntity($page2); + $this->assertEquals('test_page_2', $page_variant->get('page')); + } + +} diff --git a/tests/src/Kernel/SerializationTest.php b/tests/src/Kernel/SerializationTest.php new file mode 100644 index 0000000..38bf2bf --- /dev/null +++ b/tests/src/Kernel/SerializationTest.php @@ -0,0 +1,251 @@ +installEntitySchema('user'); + } + + /** + * Assert that an object successfully serializes and unserializes. + * + * @param object $object + * The object to serialize. + * @param string $message + * + * @return object + * The unserialized object. + */ + protected function assertSerialization($object, $message = '') { + $unserialized = unserialize(serialize($object)); + $this->assertInstanceOf(get_class($object), $unserialized, $message); + return $unserialized; + } + + /** + * Create a basic page. + * + * @return \Drupal\page_manager\Entity\Page + */ + protected function createPage() { + return Page::create([ + 'id' => $this->randomMachineName(), + 'label' => $this->randomString(), + 'description' => $this->randomString(), + 'path' => 'admin/foo', + 'use_admin_theme' => TRUE, + ]); + } + + /** + * Create a basic page variant. + * + * @return \Drupal\page_manager\Entity\PageVariant + */ + protected function createPageVariant() { + return PageVariant::create([ + 'id' => $this->randomMachineName(), + 'label' => $this->randomString(), + 'weight' => 0, + 'variant' => 'block_display', + ]); + } + + /** + * Test serialization of a page. + * + * @covers \Drupal\page_manager\Entity\Page::__sleep + */ + public function testPage() { + $page = $this->createPage(); + + // Test that a very simple page successfully serializes. + /* @var \Drupal\page_manager\Entity\Page $unserialized */ + $unserialized = $this->assertSerialization($page); + $this->assertEquals($page->id(), $unserialized->id()); + $this->assertEquals($page->label(), $unserialized->label()); + $this->assertEquals($page->getDescription(), $unserialized->getDescription()); + $this->assertEquals($page->getPath(), $unserialized->getPath()); + $this->assertEquals($page->usesAdminTheme(), $unserialized->usesAdminTheme()); + + // Test adding parameters. + $page->set('path', 'admin/foo/{id}'); + $page->setParameter('id', 'integer', 'ID'); + $unserialized = $this->assertSerialization($page); + $this->assertEquals($page->getPath(), $unserialized->getPath()); + $this->assertEquals($page->getParameters(), $unserialized->getParameters()); + + // Test adding access conditions. + $condition = [ + 'id' => 'request_path', + 'pages' => '/admin/foo/*', + 'negate' => FALSE, + 'context_mapping' => [], + ]; + $page->addAccessCondition($condition); + $unserialized = $this->assertSerialization($page); + $this->assertNull($unserialized->get('accessConditionCollection')); + $this->assertEquals($page->getAccessConditions()->getConfiguration(), $unserialized->getAccessConditions()->getConfiguration()); + + // Test adding context. + $context = new Context(new ContextDefinition('integer', 'ID'), 1); + $page->addContext('id', $context); + $unserialized = $this->assertSerialization($page); + $this->assertEquals([], $unserialized->get('contexts')); + + // Test adding a very basic variant. + $page_variant = $this->createPageVariant(); + $page->addVariant($page_variant); + $unserialized = $this->assertSerialization($page); + $this->assertInstanceOf(PageVariant::class, $unserialized->getVariant($page_variant->id())); + $this->assertEquals($page_variant->id(), $unserialized->getVariant($page_variant->id())->id()); + } + + /** + * Test serialization of a variant. + * + * @covers \Drupal\page_manager\Entity\PageVariant::__sleep + */ + public function testPageVariant() { + $page_variant = $this->createPageVariant(); + + // Test that a very simple page variant successfully serializes. + /* @var \Drupal\page_manager\Entity\PageVariant $unserialized */ + $unserialized = $this->assertSerialization($page_variant); + $this->assertEquals($page_variant->id(), $unserialized->id()); + $this->assertEquals($page_variant->label(), $unserialized->label()); + $this->assertEquals($page_variant->getWeight(), $unserialized->getWeight()); + $this->assertEquals($page_variant->getVariantPluginId(), $unserialized->getVariantPluginId()); + + // Test setting the page. + $page = $this->createPage(); + $page_variant->setPageEntity($page); + $unserialized = $this->assertSerialization($page_variant); + $this->assertInstanceOf(Page::class, $unserialized->getPage()); + $this->assertEquals($page->id(), $unserialized->getPage()->id()); + + // Test adding static context. + $page_variant->setStaticContext('test', [ + 'label' => 'Test', + 'type' => 'integer', + 'value' => 1, + ]); + $unserialized = $this->assertSerialization($page_variant); + $this->assertEquals($page_variant->getStaticContexts(), $unserialized->getStaticContexts()); + + // Add context to the page directly to avoid the + // \Drupal\page_manager\Event\PageManagerEvents::PAGE_CONTEXT event which + // relies on the router. + $context = new Context(new ContextDefinition('integer', 'ID'), 1); + $page->addContext('id', $context); + + // Test initializing context. + $page_variant->getContexts(); + $unserialized = $this->assertSerialization($page_variant); + $this->assertNull($unserialized->get('contexts')); + + // Test adding selection criteria. + $condition = [ + 'id' => 'request_path', + 'pages' => '/admin/foo/*', + 'negate' => FALSE, + 'context_mapping' => [], + ]; + $page_variant->addSelectionCondition($condition); + $unserialized = $this->assertSerialization($page_variant); + $this->assertNull($unserialized->get('selectionConditionCollection')); + $this->assertEquals($page_variant->getSelectionConditions()->getConfiguration(), $unserialized->getSelectionConditions()->getConfiguration()); + + // Initialize the variant plugin. + $page_variant->getVariantPlugin(); + $unserialized = $this->assertSerialization($page_variant); + $this->assertNull($unserialized->get('variantPluginCollection')); + + // Test adding variant settings. + $page_variant->getVariantPlugin()->setConfiguration([ + 'page_title' => $this->randomString(), + 'blocks' => [], + ]); + $unserialized = $this->assertSerialization($page_variant); + $this->assertEquals($page_variant->getVariantPlugin()->getConfiguration(), $unserialized->getVariantPlugin()->getConfiguration()); + } + + /** + * Test serialization of a block_display variant plugin. + */ + public function testPageBlockVariantPlugin() { + $configuration = [ + 'page_title' => 'Test variant', + ]; + /* @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */ + $variant_plugin = $this->container + ->get('plugin.manager.display_variant') + ->createInstance('block_display', $configuration); + $this->assertInstanceOf(PageBlockDisplayVariant::class, $variant_plugin); + + // Test that a very simple variant successfully serializes. + /* @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $unserialized */ + $unserialized = $this->assertSerialization($variant_plugin); + $this->assertEquals($variant_plugin->getConfiguration(), $unserialized->getConfiguration()); + + // Set some context. + $context = new Context(new ContextDefinition('integer', 'ID'), 1); + $variant_plugin->setContexts(['id' => $context]); + $unserialized = $this->assertSerialization($variant_plugin); + $this->assertEquals([], $unserialized->getContexts()); + } + + /** + * Test serialization of a block_display variant plugin. + */ + public function testHttpStatusCodeVariantPlugin() { + $configuration = [ + 'status_code' => '404', + ]; + /* @var \Drupal\page_manager\Plugin\DisplayVariant\HttpStatusCodeDisplayVariant $variant_plugin */ + $variant_plugin = $this->container + ->get('plugin.manager.display_variant') + ->createInstance('http_status_code', $configuration); + $this->assertInstanceOf(HttpStatusCodeDisplayVariant::class, $variant_plugin); + + // Test that a very simple variant successfully serializes. + /* @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $unserialized */ + $unserialized = $this->assertSerialization($variant_plugin); + $this->assertEquals($variant_plugin->getConfiguration(), $unserialized->getConfiguration()); + } + +} diff --git a/tests/src/Unit/PageTest.php b/tests/src/Unit/PageTest.php index 55713fc..53b4022 100644 --- a/tests/src/Unit/PageTest.php +++ b/tests/src/Unit/PageTest.php @@ -74,6 +74,103 @@ class PageTest extends UnitTestCase { } /** + * @covers ::addVariant + */ + public function testAddVariant() { + $variant1 = $this->prophesize(PageVariantInterface::class); + $variant1->id()->willReturn('variant1'); + $variant1->getWeight()->willReturn(0); + + $variant2 = $this->prophesize(PageVariantInterface::class); + $variant2->id()->willReturn('variant2'); + $variant2->getWeight()->willReturn(-10); + + $variant3 = $this->prophesize(PageVariantInterface::class); + $variant3->id()->willReturn('variant3'); + $variant3->getWeight()->willReturn(-5); + + $entity_storage = $this->prophesize(EntityStorageInterface::class); + $entity_storage + ->loadByProperties(['page' => 'the_page']) + ->willReturn([ + 'variant1' => $variant1->reveal(), + 'variant2' => $variant2->reveal(), + ]) + ->shouldBeCalledTimes(1); + + $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class); + $entity_type_manager->getStorage('page_variant')->willReturn($entity_storage); + + $container = new ContainerBuilder(); + $container->set('entity_type.manager', $entity_type_manager->reveal()); + \Drupal::setContainer($container); + + // Check that Variant 1 and 2 are in the right order. + $variants = $this->page->getVariants(); + $this->assertSame([ + 'variant2' => $variant2->reveal(), + 'variant1' => $variant1->reveal(), + ], $variants); + + // Add Variant 3. + $this->page->addVariant($variant3->reveal()); + + // Check that Variant 1, 2 and 3 are in the right order. + $variants = $this->page->getVariants(); + $this->assertSame([ + 'variant2' => $variant2->reveal(), + 'variant3' => $variant3->reveal(), + 'variant1' => $variant1->reveal(), + ], $variants); + } + + /** + * @covers ::removeVariant + */ + public function testRemoveVariant() { + $variant1 = $this->prophesize(PageVariantInterface::class); + $variant1->id()->willReturn('variant1'); + $variant1->getWeight()->willReturn(0); + $variant1->delete()->shouldBeCalledTimes(1); + + $variant2 = $this->prophesize(PageVariantInterface::class); + $variant2->id()->willReturn('variant2'); + $variant2->getWeight()->willReturn(-10); + + $entity_storage = $this->prophesize(EntityStorageInterface::class); + $entity_storage + ->loadByProperties(['page' => 'the_page']) + ->willReturn([ + 'variant1' => $variant1->reveal(), + 'variant2' => $variant2->reveal(), + ]) + ->shouldBeCalledTimes(1); + + $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class); + $entity_type_manager->getStorage('page_variant')->willReturn($entity_storage); + + $container = new ContainerBuilder(); + $container->set('entity_type.manager', $entity_type_manager->reveal()); + \Drupal::setContainer($container); + + // Check that Variant 1 and 2 are returned. + $variants = $this->page->getVariants(); + $this->assertSame([ + 'variant2' => $variant2->reveal(), + 'variant1' => $variant1->reveal(), + ], $variants); + + // Remove Variant 1. + $this->page->removeVariant($variant1->reveal()->id()); + + // Check that Variant 1 has been removed. + $variants = $this->page->getVariants(); + $this->assertSame([ + 'variant2' => $variant2->reveal(), + ], $variants); + } + + /** * @covers ::addContext */ public function testAddContext() { @@ -107,6 +204,30 @@ class PageTest extends UnitTestCase { * @covers ::filterParameters */ public function testFilterParameters() { + // Changing filters clears cached contexts on variants so we have to setup + // some variants for our page. + $variant1 = $this->prophesize(PageVariantInterface::class); + $variant1->id()->willReturn('variant1'); + $variant1->getWeight()->willReturn(0); + $variant1->resetCollectedContexts()->willReturn($variant1->reveal()); + $variant2 = $this->prophesize(PageVariantInterface::class); + $variant2->id()->willReturn('variant2'); + $variant2->getWeight()->willReturn(-10); + $variant2->resetCollectedContexts()->willReturn($variant2->reveal()); + + $entity_storage = $this->prophesize(EntityStorageInterface::class); + $entity_storage + ->loadByProperties(['page' => 'the_page']) + ->willReturn(['variant1' => $variant1->reveal(), 'variant2' => $variant2->reveal()]) + ->shouldBeCalledTimes(1); + + $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class); + $entity_type_manager->getStorage('page_variant')->willReturn($entity_storage); + + $container = new ContainerBuilder(); + $container->set('entity_type.manager', $entity_type_manager->reveal()); + \Drupal::setContainer($container); + $parameters = [ 'foo' => [ 'machine_name' => 'foo', @@ -118,10 +239,16 @@ class PageTest extends UnitTestCase { 'type' => '', 'label' => '', ], + 'baz' => [ + 'machine_name' => 'baz', + 'type' => 'integer', + 'label' => 'Baz', + ] ]; - $page = new Page(['id' => 'the_page', 'parameters' => $parameters], 'page'); + $page = new Page(['id' => 'the_page', 'parameters' => $parameters, 'path' => 'test/{foo}/{bar}'], 'page'); $expected = $parameters; + unset($expected['baz']); $this->assertEquals($expected, $page->getParameters()); $method = new \ReflectionMethod($page, 'filterParameters'); @@ -135,7 +262,7 @@ class PageTest extends UnitTestCase { 'label' => 'Foo', ], ]; - $this->assertEquals($expected, $page->getParameters()); + $this->assertEquals($expected, $page->get('parameters')); } } diff --git a/tests/src/Unit/PageVariantTest.php b/tests/src/Unit/PageVariantTest.php index cfe8ead..1fdbfd2 100644 --- a/tests/src/Unit/PageVariantTest.php +++ b/tests/src/Unit/PageVariantTest.php @@ -84,7 +84,7 @@ class PageVariantTest extends UnitTestCase { $data['additive'] = [ ['static' => 'static'], ['page' => 'page'], - ['static' => 'static', 'page' => 'page'], + ['page' => 'page', 'static' => 'static'], ]; $data['conflicting'] = [ ['foo' => 'static'],